#include "wx/wx.h"
#include "wx/glcanvas.h"
#include <GL/glu.h>

class HelloApp : public wxApp
{
public:
	bool OnInit();
};

DECLARE_APP(HelloApp)


class HelloGLCanvas;
class HelloFrame : public wxFrame
{
public:
	HelloFrame();

	void OnExit(wxCommandEvent& event);
	void OnSlider(wxScrollEvent& event);
	void OnButton(wxCommandEvent& event);
	void OnTextCtrl(wxCommandEvent& event);
	void Update();

	void OnMenuAbout(wxCommandEvent& event);

private:
	HelloGLCanvas *canvas;
	wxSlider *slider;
	wxButton *button;
	wxTextCtrl *text;

	DECLARE_EVENT_TABLE()
};

class HelloGLCanvas : public wxGLCanvas
{
friend class HelloFrame;
public:
	HelloGLCanvas(wxWindow *parent, const wxWindowID id, const wxPoint& pos, const wxSize& size);
	void Init();
	void Render();
	void OnPaint(wxPaintEvent& event);
	void OnSize(wxSizeEvent& event);

private:
	float cube_x;

	DECLARE_EVENT_TABLE()
};





enum {
	HELLO_GL_CANVAS = 100,
	HELLO_BUTTON,
	HELLO_SLIDER,
	HELLO_TEXT,

	HELLO_MENU_EXIT,
	HELLO_MENU_ABOUT,
};

// 各Widgetsに来たイベントとメンバ関数を接続
BEGIN_EVENT_TABLE( HelloFrame, wxFrame )
	EVT_COMMAND_SCROLL( HELLO_SLIDER, HelloFrame::OnSlider )
	EVT_BUTTON( HELLO_BUTTON, HelloFrame::OnButton )
	EVT_TEXT_ENTER( HELLO_TEXT, HelloFrame::OnTextCtrl )

    EVT_MENU( HELLO_MENU_EXIT, HelloFrame::OnExit )
    EVT_MENU( HELLO_MENU_ABOUT, HelloFrame::OnMenuAbout )
END_EVENT_TABLE()


HelloFrame::HelloFrame() :
	wxFrame(0, -1, wxT("test app"))
{
	canvas = new HelloGLCanvas(this, HELLO_GL_CANVAS, wxDefaultPosition, wxSize(400, 300));

	// 各Widgetsを乗っけるパネル
	wxPanel *widgets_panel = new wxPanel( this, -1, wxPoint(0, 0), wxSize(400, 30) );

	// スライダ、ボタン、テキストコントロールを作成
	slider = new wxSlider( widgets_panel, HELLO_SLIDER, 0, -200, 200 );
	button = new wxButton( widgets_panel, HELLO_BUTTON, wxT("reset") );
	text = new wxTextCtrl( widgets_panel, HELLO_TEXT, wxT("0"), wxDefaultPosition, wxSize(50, -1) );

	// Sizerは各Widgetsを適切に並べてくれるclass
	// ここでは前述の3つのWidgetsを横に並べている
	wxBoxSizer *widgets_sizer = new wxBoxSizer(wxHORIZONTAL);
	widgets_sizer->Add( slider, 1, wxEXPAND );
	widgets_sizer->Add( text, 0, wxALIGN_RIGHT );
	widgets_sizer->Add( button, 0, wxALIGN_RIGHT );

	// パネルにSizerをセット
	widgets_panel->SetAutoLayout( TRUE );
	widgets_panel->SetSizer( widgets_sizer );

	// GLCanvasを上に、Widgetsのパネルを下に配置
	wxBoxSizer *top_sizer = new wxBoxSizer(wxVERTICAL);
	top_sizer->Add( canvas, 1, wxEXPAND );
	top_sizer->Add( widgets_panel, 0, wxEXPAND );

	SetAutoLayout( TRUE );
	SetSizer( top_sizer );


	// 適当にメニューをくっつける
	wxMenu *menu = new wxMenu();
	menu->Append(HELLO_MENU_ABOUT, wxT("&About") );
	menu->Append(HELLO_MENU_EXIT, wxT("&Close"));
	wxMenuBar *menu_bar = new wxMenuBar();
	menu_bar->Append(menu, wxT("&Menu"));
	SetMenuBar(menu_bar);
}

void HelloFrame::OnExit(wxCommandEvent&)
{
	Close(true);
}

void HelloFrame::OnSlider(wxScrollEvent&)
{
	canvas->cube_x = (float)slider->GetValue()/100;

	Update();
}

void HelloFrame::OnButton(wxCommandEvent&)
{
	canvas->cube_x = 0;
	slider->SetValue( 0 );

	Update();
}

void HelloFrame::OnTextCtrl(wxCommandEvent&)
{
	double value;
	if( text->GetValue().ToDouble(&value) ) {
		canvas->cube_x = value;
	}

	Update();
}

void HelloFrame::Update()
{
	wxString value;
	value.sprintf("%.2f", canvas->cube_x);
	text->SetValue( value );

	slider->SetValue( canvas->cube_x*100 );

	canvas->Render();
}

void HelloFrame::OnMenuAbout(wxCommandEvent& event)
{
	wxMessageDialog *dialog = new wxMessageDialog(this,
		wxT("wxWidgets+OpenGLのテキトーなサンプルプログラム"),
		wxT("名無しのダイアログ"));
	dialog->ShowModal();
}





BEGIN_EVENT_TABLE( HelloGLCanvas, wxGLCanvas )
	EVT_SIZE( HelloGLCanvas::OnSize )
	EVT_PAINT( HelloGLCanvas::OnPaint )
END_EVENT_TABLE()

HelloGLCanvas::HelloGLCanvas(wxWindow *parent, const wxWindowID id, const wxPoint& pos, const wxSize& size) :
	wxGLCanvas(parent, id, pos, size),
	cube_x(0)
{
	Init();
}

void HelloGLCanvas::Init()
{
	SetCurrent();

	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glShadeModel( GL_SMOOTH );
	glCullFace( GL_BACK );
	glFrontFace( GL_CCW );
	glEnable( GL_CULL_FACE );
	glEnable(GL_DEPTH_TEST);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	float light_pos[] = { 2.0, 2.0, 3.0, 1.0 };
	glLightfv(GL_LIGHT0, GL_POSITION, light_pos);
}

void HelloGLCanvas::Render()
{
	wxPaintDC dc(this);
	SetCurrent();

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	int w, h;
	GetClientSize(&w, &h);
	gluPerspective( 60.0, (float)w/(float)h, 1.0, 10000.0 );

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(
		2.0, 2.0, 2.0,
		0.0, 0.0, 0.0,
		0.0, 1.0, 0.0);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glPushMatrix();
	glTranslatef(cube_x, 0, 0);

	glBegin(GL_QUADS);
	glNormal3f( 0.0, 0.0, 1.0);
	glVertex3f( 0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5, 0.5);
	glVertex3f(-0.5,-0.5, 0.5); glVertex3f( 0.5,-0.5, 0.5);

	glNormal3f( 0.0, 0.0,-1.0);
	glVertex3f(-0.5,-0.5,-0.5); glVertex3f(-0.5, 0.5,-0.5);
	glVertex3f( 0.5, 0.5,-0.5); glVertex3f( 0.5,-0.5,-0.5);

	glNormal3f( 0.0, 1.0, 0.0);
	glVertex3f( 0.5, 0.5, 0.5); glVertex3f( 0.5, 0.5,-0.5);
	glVertex3f(-0.5, 0.5,-0.5); glVertex3f(-0.5, 0.5, 0.5);

	glNormal3f( 0.0,-1.0, 0.0);
	glVertex3f(-0.5,-0.5,-0.5); glVertex3f( 0.5,-0.5,-0.5);
	glVertex3f( 0.5,-0.5, 0.5); glVertex3f(-0.5,-0.5, 0.5);

	glNormal3f( 1.0, 0.0, 0.0);
	glVertex3f( 0.5, 0.5, 0.5); glVertex3f( 0.5,-0.5, 0.5);
	glVertex3f( 0.5,-0.5,-0.5); glVertex3f( 0.5, 0.5,-0.5);

	glNormal3f(-1.0, 0.0, 0.0);
	glVertex3f(-0.5,-0.5,-0.5); glVertex3f(-0.5,-0.5, 0.5);
	glVertex3f(-0.5, 0.5, 0.5); glVertex3f(-0.5, 0.5,-0.5);
	glEnd();

	glPopMatrix();

	SwapBuffers();
}

void HelloGLCanvas::OnPaint(wxPaintEvent&)
{
	Render();
}

void HelloGLCanvas::OnSize(wxSizeEvent& event)
{
	wxGLCanvas::OnSize(event);

	SetCurrent();
	glViewport(0, 0, event.GetSize().GetWidth(), event.GetSize().GetHeight());
}




IMPLEMENT_APP(HelloApp)

bool HelloApp::OnInit()
{
	HelloFrame *frame = new HelloFrame();
	frame->Show(TRUE);
	SetTopWindow(frame);

	return true;
}




