#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <gl\gl.h>
#include <gl\glu.h>
#include <stdio.h>

#include "PajaTypes.h"
#include "BBox2C.h"
#include "ColorC.h"
#include "Vector2C.h"
#include "DeviceInterfaceI.h"
#include "GUIDrawInterfaceI.h"
#include "OpenGLViewportC.h"
#include "OpenGLGUIDrawInterfaceC.h"

using namespace PajaTypes;
using namespace PajaSystem;
using namespace PluginClass;
using namespace Edit;


OpenGLGUIDrawInterfaceC::OpenGLGUIDrawInterfaceC() :
	m_pDevice( 0 )
{
}

OpenGLGUIDrawInterfaceC::OpenGLGUIDrawInterfaceC( OpenGLDeviceC* pDevice ) :
	m_pDevice( pDevice )
{
}

OpenGLGUIDrawInterfaceC::~OpenGLGUIDrawInterfaceC()
{
}


DataBlockI*
OpenGLGUIDrawInterfaceC::create()
{
	return new OpenGLGUIDrawInterfaceC;
}

OpenGLGUIDrawInterfaceC*
OpenGLGUIDrawInterfaceC::create_new( OpenGLDeviceC* pDevice )
{
	return new OpenGLGUIDrawInterfaceC( pDevice );
}

ClassIdC
OpenGLGUIDrawInterfaceC::get_class_id() const
{
	return CLASS_OPENGL_GUIINTERFACE;
}

const char*
OpenGLGUIDrawInterfaceC::get_class_name()
{
	return "OpenGL GUIDI";
}

void
OpenGLGUIDrawInterfaceC::draw_text( const PajaTypes::Vector2C& rPos, const char* szStr )
{
	if( m_dwFontListStart ) {
		glRasterPos2f( rPos[0], rPos[1] );
		glListBase( m_dwFontListStart ); 
		glCallLists( strlen( szStr ), GL_UNSIGNED_BYTE, szStr ); 
	}
}

void
OpenGLGUIDrawInterfaceC::use_font( HFONT hFont )
{
	HDC	hDC = m_pDevice->get_hdc();

	HGDIOBJ	hOldObj = SelectObject( hDC, hFont );

	if( !m_dwFontListStart )
		m_dwFontListStart = glGenLists( 255 );

	wglUseFontBitmaps( hDC, 0, 255, m_dwFontListStart );

	SelectObject( hDC, hOldObj );
}

void
OpenGLGUIDrawInterfaceC::draw_marker( const Vector2C& rPos, float32 f32Size )
{
	Vector2C	rVec[16];

	float32	f32Angle = 0;
	float32	f32DeltaAngle = (2.0f * (float32)M_PI) / 16.0f;

	for( uint32 i = 0; i < 16; i++ ) {
		rVec[i][0] = rPos[0] + (float32)sin( f32Angle ) * f32Size;
		rVec[i][1] = rPos[1] + (float32)cos( f32Angle ) * f32Size;
		f32Angle += f32DeltaAngle;
	}

	// draw crossed lines
	glBegin( GL_LINES );

	glVertex2f( rVec[2][0], rVec[2][1] );
	glVertex2f( rVec[10][0], rVec[10][1] );

	glVertex2f( rVec[6][0], rVec[6][1] );
	glVertex2f( rVec[14][0], rVec[14][1] );

	glEnd();

	// draw circle
	glBegin( GL_LINE_LOOP );

	for( i = 0; i < 16; i++ ) {
		glVertex2f( rVec[i][0], rVec[i][1] );
	}

	glEnd();
}

void
OpenGLGUIDrawInterfaceC::draw_box( const Vector2C& rMin, const Vector2C& rMax )
{
	glBegin( GL_LINE_LOOP );
	glVertex2f( rMin[0], rMin[1] );
	glVertex2f( rMax[0], rMin[1] );
	glVertex2f( rMax[0], rMax[1] );
	glVertex2f( rMin[0], rMax[1] );
	glEnd();
}

void
OpenGLGUIDrawInterfaceC::begin_layout()
{
	OpenGLViewportC*	pIface = (OpenGLViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	BBox2C	rViewport = pIface->get_viewport();

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity(); 

	glMatrixMode( GL_PROJECTION );
	glLoadIdentity(); 
	gluOrtho2D( rViewport[0][0], rViewport[1][0], rViewport[0][1], rViewport[1][1] );

	glDisable( GL_DEPTH_TEST );
	glDisable( GL_BLEND );
	glDisable( GL_TEXTURE_2D );
	glDisable( GL_SCISSOR_TEST );

	glDepthMask( GL_TRUE );	// disable depth buffering while drawing the GUI stuff
}

void
OpenGLGUIDrawInterfaceC::end_layout()
{
	// nothing
}

void
OpenGLGUIDrawInterfaceC::draw_layout( const ColorC& rLayoutCol )
{
	OpenGLViewportC*	pIface = (OpenGLViewportC*)m_pDevice->query_interface( GRAPHICSDEVICE_VIEWPORT_INTERFACE );
	BBox2C	rLayout = pIface->get_layout();

	glDepthMask( GL_FALSE );

	glBegin( GL_QUADS );
	glColor3f( rLayoutCol[0], rLayoutCol[1], rLayoutCol[2] );
	glVertex2f( rLayout[0][0], rLayout[0][1] );
	glVertex2f( rLayout[1][0], rLayout[0][1] );
	glVertex2f( rLayout[1][0], rLayout[1][1] );
	glVertex2f( rLayout[0][0], rLayout[1][1] );
	glEnd();

	glDepthMask( GL_TRUE );
}

void
OpenGLGUIDrawInterfaceC::set_color( const ColorC& rColor )
{
	glColor4f( rColor[0], rColor[1], rColor[2], rColor[3] );
}

void
OpenGLGUIDrawInterfaceC::set_point_size( float32 f32Size )
{
	glPointSize( f32Size );
}

void
OpenGLGUIDrawInterfaceC::draw_line( const Vector2C& rFrom, const Vector2C& rTo )
{
	glBegin( GL_LINES );

	glVertex2f( rFrom[0], rFrom[1] );
	glVertex2f( rTo[0], rTo[1] );

	glEnd();
}

void
OpenGLGUIDrawInterfaceC::draw_point( const Vector2C& rPos )
{
	glBegin( GL_POINTS );
	glVertex2f( rPos[0], rPos[1] );
	glEnd();
}

void
OpenGLGUIDrawInterfaceC::draw_grid( float32 f32Width, float32 f32Height, float32 f32GridSize )
{
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	glPointSize( 1.0f );

	int32	i32GSizeX = (int32)(f32Width / f32GridSize);
	int32	i32GSizeY = (int32)(f32Height / f32GridSize);
	float32	f32Y = f32GridSize;
	int32	i, j;

	glBegin( GL_POINTS );

	for( i = 1; i < i32GSizeY; i++ ) {
		float32	f32X = f32GridSize;
		for( j = 1; j < i32GSizeX; j++ ) {
			glVertex2f( f32X, f32Y );
			f32X += f32GridSize;
		}
		f32Y += f32GridSize;
	}

	glEnd();

	glDisable( GL_BLEND );
}

void
OpenGLGUIDrawInterfaceC::draw_selection_box( const Vector2C& rStartPos, const Vector2C& rEndPos )
{
	glLineStipple( 1, 0x5555 );
	glEnable( GL_LINE_STIPPLE );
	glEnable( GL_BLEND);
	glBlendFunc( GL_ONE_MINUS_DST_COLOR, GL_ZERO );

	glBegin( GL_LINE_LOOP );

	glColor3ub( 255, 255, 255 );

	glVertex2f( rStartPos[0], rStartPos[1] );
	glVertex2f( rEndPos[0], rStartPos[1] );
	glVertex2f( rEndPos[0], rEndPos[1] );
	glVertex2f( rStartPos[0], rEndPos[1] );

	glEnd();

	glDisable( GL_LINE_STIPPLE );
	glDisable( GL_BLEND );
}
