#if defined(_MSC_VER) /* MSVC Compiler */
#pragma warning ( disable : 4305 )
#pragma warning ( disable : 4786 )
#endif

#include "qwt3d_plot.h"
#include "qwt3d_enrichment.h"


using namespace Qwt3D;
	
/*!
  This should be the first call in your derived classes constructors.  
*/
#if QT_VERSION < 0x040000
Plot3D::Plot3D( QWidget* parent, const char* name )
    : QGLWidget( parent, name )
#else
Plot3D::Plot3D( QWidget * parent, const QGLWidget * shareWidget)
    : QGLWidget( parent, shareWidget) 
#endif
{  
  initializedGL_ = false;
  renderpixmaprequest_ = false;
  xRot_ = yRot_ = zRot_ = 0.0;		// default object rotation
  
	xShift_ = yShift_ = zShift_ = xVPShift_ = yVPShift_ = 0.0;
	xScale_ = yScale_ = zScale_ = 1.0;
	zoom_ = 1;
	ortho_ = true;
	plotstyle_ = FILLEDMESH;
  userplotstyle_p = 0;
	shading_ = GOURAUD;
	floorstyle_ = NOFLOOR;
	isolines_ = 10;
	displaylegend_ = false;
	smoothdatamesh_p = false;
  actualData_p = 0;

	lastMouseMovePosition_ = QPoint(0,0);
	mpressed_ = false;
	mouse_input_enabled_ = true;

	setPolygonOffset(0.5);
	setMeshColor(RGBA(0.0,0.0,0.0));
	setMeshLineWidth(1);
	setBackgroundColor(RGBA(1.0,1.0,1.0,1.0));

	displaylists_p = std::vector<GLuint>(DisplayListSize);
	for (unsigned k=0; k!=displaylists_p.size(); ++k)
	{
		displaylists_p[k] = 0;
	}

	datacolor_p = new StandardColor(this, 100);
	title_.setFont("Courier", 16, QFont::Bold);
	title_.setString("");

	setTitlePosition(0.95);
	
  kbd_input_enabled_ = true;

#if QT_VERSION < 0x040000
  setFocusPolicy(QWidget::StrongFocus);
  assignMouse(Qt::LeftButton, 
							Qt::LeftButton | Qt::ShiftButton,
							Qt::LeftButton, 
							Qt::LeftButton | Qt::AltButton, 
							Qt::LeftButton | Qt::AltButton, 
							Qt::LeftButton | Qt::AltButton | Qt::ShiftButton,
							Qt::LeftButton | Qt::AltButton | Qt::ControlButton,
							Qt::LeftButton | Qt::ControlButton, 
							Qt::LeftButton | Qt::ControlButton);


  assignKeyboard(Qt::Key_Down, Qt::Key_Up,
    Qt::ShiftButton + Qt::Key_Right, Qt::ShiftButton + Qt::Key_Left,
    Qt::Key_Right, Qt::Key_Left,
    Qt::AltButton + Qt::Key_Right, Qt::AltButton + Qt::Key_Left,
    Qt::AltButton + Qt::Key_Down, Qt::AltButton + Qt::Key_Up,
    Qt::AltButton + Qt::ShiftButton + Qt::Key_Down, Qt::AltButton + Qt::ShiftButton + Qt::Key_Up,
    Qt::AltButton + Qt::ControlButton + Qt::Key_Down, Qt::AltButton + Qt::ControlButton + Qt::Key_Up,
    Qt::ControlButton + Qt::Key_Right, Qt::ControlButton + Qt::Key_Left,
    Qt::ControlButton + Qt::Key_Down, Qt::ControlButton + Qt::Key_Up
   );
#else
  setFocusPolicy(Qt::StrongFocus);
  assignMouse(Qt::LeftButton, 
							MouseState(Qt::LeftButton, Qt::ShiftModifier),
							Qt::LeftButton, 
							MouseState(Qt::LeftButton, Qt::AltModifier), 
							MouseState(Qt::LeftButton, Qt::AltModifier), 
							MouseState(Qt::LeftButton, Qt::AltModifier | Qt::ShiftModifier),
							MouseState(Qt::LeftButton, Qt::AltModifier | Qt::ControlModifier),
							MouseState(Qt::LeftButton, Qt::ControlModifier), 
							MouseState(Qt::LeftButton, Qt::ControlModifier)
              );


  assignKeyboard(Qt::Key_Down, Qt::Key_Up,
    KeyboardState(Qt::Key_Right, Qt::ShiftModifier), KeyboardState(Qt::Key_Left, Qt::ShiftModifier),
    Qt::Key_Right, Qt::Key_Left,
    KeyboardState(Qt::Key_Right, Qt::AltModifier), KeyboardState(Qt::Key_Left, Qt::AltModifier),
    KeyboardState(Qt::Key_Down, Qt::AltModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier),
    KeyboardState(Qt::Key_Down, Qt::AltModifier|Qt::ShiftModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier|Qt::ShiftModifier),
    KeyboardState(Qt::Key_Down, Qt::AltModifier|Qt::ControlModifier), KeyboardState(Qt::Key_Up, Qt::AltModifier|Qt::ControlModifier),
    KeyboardState(Qt::Key_Right, Qt::ControlModifier), KeyboardState(Qt::Key_Left, Qt::ControlModifier),
    KeyboardState(Qt::Key_Down, Qt::ControlModifier), KeyboardState(Qt::Key_Up, Qt::ControlModifier)
   );
#endif
  setKeySpeed(3,5,5);

	legend_.setLimits(0, 100);
	legend_.setMajors(10);
	legend_.setMinors(2);
	legend_.setOrientation(ColorLegend::BottomTop, ColorLegend::Left);

  lighting_enabled_ = false;
  disableLighting();
  lights_ = std::vector<Light>(8);
}

/*!
  Release allocated resources
*/

Plot3D::~Plot3D()
{
	makeCurrent();
	SaveGlDeleteLists( displaylists_p[0], displaylists_p.size() );
	datacolor_p->destroy();
  delete userplotstyle_p;
  for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it)
    delete (*it);

  elist_p.clear();
}


/*!
  Set up the OpenGL rendering state
*/
void Plot3D::initializeGL()
{
  glEnable( GL_BLEND );
  glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);
	
  // Set up the lights

  disableLighting();
	
  GLfloat whiteAmb[4] = {1.0, 1.0, 1.0, 1.0};
    
  setLightShift(0, 0, 3000);
  glEnable(GL_COLOR_MATERIAL);

	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, whiteAmb);

  setMaterialComponent(GL_DIFFUSE, 1.0);
  setMaterialComponent(GL_SPECULAR, 0.3);
  setMaterialComponent(GL_SHININESS, 5.0);
  setLightComponent(GL_DIFFUSE, 1.0);
  setLightComponent(GL_SPECULAR, 1.0);

  initializedGL_ = true;	
  if (renderpixmaprequest_)
  {
    updateData();
    renderpixmaprequest_ = false;
  }
}

/*!
  Paint the widgets content.
*/
void Plot3D::paintGL()
{
	glClearColor(bgcolor_.r, bgcolor_.g, bgcolor_.b, bgcolor_.a);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
  glMatrixMode( GL_MODELVIEW );
	glPushMatrix();
  applyLights();

  glRotatef( -90, 1.0, 0.0, 0.0 ); 
  glRotatef( 0.0, 0.0, 1.0, 0.0 ); 
  glRotatef( 0.0, 0.0, 0.0, 1.0 );

	if (displaylegend_)
	{		
		legend_.draw();
	}
	title_.setRelPosition(titlerel_, titleanchor_);
	title_.draw();
	
	Triple beg = coordinates_p.first();
	Triple end = coordinates_p.second();
	
	Triple center = beg + (end-beg) / 2;
	double radius = (center-beg).length();
	
	glLoadIdentity();

  glRotatef( xRot_-90, 1.0, 0.0, 0.0 ); 
  glRotatef( yRot_, 0.0, 1.0, 0.0 ); 
  glRotatef( zRot_, 0.0, 0.0, 1.0 );

	glScalef( zoom_ * xScale_, zoom_ * yScale_, zoom_ * zScale_ );
	
	glTranslatef(xShift_-center.x, yShift_-center.y, zShift_-center.z);
  
  glMatrixMode( GL_PROJECTION );
  glLoadIdentity();

	if (beg != end)
	{		
		if (ortho_)
    {	
      glOrtho( -radius, +radius, -radius, +radius, 0, 40 * radius);
    }
    else
    {	
      glFrustum( -radius, +radius, -radius, +radius, 5 * radius, 400 * radius );
    }
  }
	else
	{
		if (ortho_)
			glOrtho( -1.0, 1.0, -1.0, 1.0, 10.0, 100.0 );
		else
			glFrustum( -1.0, 1.0, -1.0, 1.0, 10.0, 100.0 );
	}

  glTranslatef( xVPShift_ * 2 * radius , yVPShift_ * 2 * radius , -7 * radius );
  
  if (lighting_enabled_)
    glEnable(GL_NORMALIZE);

  for (unsigned i=0; i!= displaylists_p.size(); ++i)
	{
		if (i!=LegendObject)
			glCallList( displaylists_p[i] );
	}
  coordinates_p.draw();
	
  if (lighting_enabled_)
    glDisable(GL_NORMALIZE);
  
  glMatrixMode( GL_MODELVIEW );
  glPopMatrix();
}


/*!
  Set up the OpenGL view port
*/
void Plot3D::resizeGL( int w, int h )
{
	glViewport( 0, 0, w, h );
	paintGL();
}

/*!
  Reimplemented from QGLWidget
*/
QPixmap Plot3D::renderPixmap(int w/* =0 */, int h/* =0 */, bool useContext/* =false */)
{
  renderpixmaprequest_ = true;
  return QGLWidget::renderPixmap(w,h,useContext);
}

/*!
	Create a coordinate system with generating corners beg and end 
*/
void Plot3D::createCoordinateSystem( Triple beg, Triple end )
{
	if (beg != coordinates_p.first() || end != coordinates_p.second())
		coordinates_p.init(beg, end);
}

/*!
	Create a coordinate system from data
*/
void Plot3D::createCoordinateSystem()
{
	calculateHull();
  Triple beg = hull().minVertex; // Irix 6.5 compiler bug
  Triple end = hull().maxVertex;
  createCoordinateSystem(beg, end);
}

/*!
  Show a color legend
*/
void Plot3D::showColorLegend( bool show )
{
 	displaylegend_ = show;
	if (show)
    datacolor_p->createVector(legend_.colors);
	updateGL();
}

void Plot3D::setMeshColor(RGBA rgba)
{
	meshcolor_ = rgba;
}

void Plot3D::setBackgroundColor(RGBA rgba)
{
	bgcolor_ = rgba;
}


/*!
	assign a new coloring object for the data.
*/
void Plot3D::setDataColor( Color* col )
{
	Q_ASSERT(datacolor_p);

	datacolor_p->destroy();
	datacolor_p = col;
}

/*!
  Set up ortogonal or perspective mode and updates widget
*/
void Plot3D::setOrtho( bool val )
{
	if (val == ortho_)
		return;
	ortho_ = val;
	updateGL();
	
	emit projectionChanged(val);
}

/*!
  Set style of coordinate system
*/
void Plot3D::setCoordinateStyle(COORDSTYLE st)
{
	coordinates_p.setStyle(st);
	updateGL();
}

/*!
  Set plotstyle for the standard plotting types. An argument of value Qwt3D::USER
  is ignored.
*/
void Plot3D::setPlotStyle( PLOTSTYLE val )
{
  if (val == Qwt3D::USER)
    return;
  delete userplotstyle_p;
  userplotstyle_p = 0;
  plotstyle_ = val;
}

/*!
  Set plotstyle to Qwt3D::USER and an associated enrichment object.
*/
Qwt3D::Enrichment* Plot3D::setPlotStyle( Qwt3D::Enrichment const& obj )
{
  if (&obj == userplotstyle_p)
    return userplotstyle_p;
  
  delete userplotstyle_p;
  userplotstyle_p = obj.clone();
  plotstyle_ = Qwt3D::USER;
  return userplotstyle_p;
}

/*!
  Set shading style
*/
void Plot3D::setShading( SHADINGSTYLE val )
{
	if (val == shading_)
		return;
	
	shading_ = val;
	
	switch (shading_)
	{
		case FLAT:
			glShadeModel(GL_FLAT);
			break;
		case GOURAUD:
			glShadeModel(GL_SMOOTH);
			break;
		default:
			break;
	}
	updateGL();
}

/*!
  Set number of isolines. The lines are equidistant between minimal and maximal Z value
*/
void Plot3D::setIsolines(int steps)
{
	if (steps < 0)
		return;

	isolines_ = steps;
}

/*!
  Set Polygon offset. The function affects the OpenGL rendering process. 
	Try different values for surfaces with polygons only and with mesh and polygons
*/
void Plot3D::setPolygonOffset( double val )
{
	polygonOffset_ = val;
}

void Plot3D::setMeshLineWidth( double val )
{
	Q_ASSERT(val>=0);

	if (val < 0)
		return;
	
	meshLineWidth_ = val;
}


/*!
Set relative caption position (0.5,0.5) means, the anchor point lies in the center of the screen
*/
void Plot3D::setTitlePosition(double rely, double relx, Qwt3D::ANCHOR anchor)
{
	titlerel_.y = (rely<0 || rely>1) ? 0.5 : rely;
	titlerel_.x = (relx<0 || relx>1) ? 0.5 : relx;

	titleanchor_ = anchor;
}

/*!
Set caption font
*/
void Plot3D::setTitleFont(const QString& family, int pointSize, int weight, bool italic)
{ 
	title_.setFont(family, pointSize, weight, italic);
}

Enrichment* Plot3D::addEnrichment(Enrichment const& e)
{
  if ( elist_p.end() == std::find( elist_p.begin(), elist_p.end(), &e ) )
    elist_p.push_back(e.clone());
  return elist_p.back();
}

bool Plot3D::degrade(Enrichment* e)
{
	ELIT it = std::find(elist_p.begin(), elist_p.end(), e);
	
	if ( it != elist_p.end() )
	{
		delete (*it);
    elist_p.erase(it);
    return true;
	}
  return false;
}

void Plot3D::createEnrichments()
{
  for (ELIT it = elist_p.begin(); it!=elist_p.end(); ++it)
  {
    this->createEnrichment(**it);
  } 
}

/*!
  Update OpenGL data representation
*/
void Plot3D::updateData()
{
	makeCurrent();
  GLStateBewarer dt(GL_DEPTH_TEST, true);
	GLStateBewarer ls(GL_LINE_SMOOTH, true);

	calculateHull();	

	SaveGlDeleteLists(displaylists_p[DataObject], 1); // nur Daten
	
	displaylists_p[DataObject] = glGenLists(1);
	glNewList(displaylists_p[DataObject], GL_COMPILE);
	
  this->createEnrichments();
	this->createData();
		
	glEndList();
}
