/*
   Copyright (C) 1999-2006 Id Software, Inc. and contributors.
   For a list of contributors, see the accompanying CONTRIBUTORS file.

   This file is part of GtkRadiant.

   GtkRadiant is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   GtkRadiant is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with GtkRadiant; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

//-----------------------------------------------------------------------------
//
// DESCRIPTION:
// a class to handle control points in a 2D view
// TODO: this one can be placed under an interface, and provided to the editor as service
//

#include "StdAfx.h"

void CControlPointsManagerBFace::Init( int iPts, CtrlPts_t *Pts, C2DView *p2DView, int TexSize[2],
									   _QERFaceData* pFaceData, OpenGLBinding *pQglTable ){
	ManagerState = Idle;
	m_NumPoints = iPts;
	m_pPts = Pts;
	// store the initial config
	memcpy( &m_RefPts, Pts, sizeof( CtrlPts_t ) );
	// init TM
	memset( m_TM, 0, sizeof( float[2][3] ) );
	m_TM[0][0] = 1.0f; m_TM[1][1] = 1.0f;
	m_bGotAnchor = false;
	m_TransOffset[0] = 0.0f; m_TransOffset[1] = 0.0f;
	m_TexSize[0] = TexSize[0];
	m_TexSize[1] = TexSize[1];
	m_pFaceData = pFaceData;

	CControlPointsManager::Init( p2DView, pQglTable );
}

bool CControlPointsManagerBFace::OnLButtonDown( int xPos, int yPos ){
	if ( ManagerState == Idle ) {
		int i;

		// scan the point list to see if we selected something
		for ( i = 0; i < m_NumPoints; i++ )
			if ( m_p2DView->DoesSelect( xPos, yPos, m_pPts->data[i] ) ) {
				m_iDragPoint = i;
				ManagerState = Drag;
				if ( m_bGotAnchor && i == m_iAnchorPoint ) {
					// this means we selected the Anchor, so we'll translate
					m_bGotAnchor = false;
				}
				// perhaps we won't use translation, but we can compute it anyway
				ComputeTransOffset( i );
				if ( m_bGotAnchor ) {
					// we have an Anchor and selected another point
					m_Anchor[0] = m_pPts->data[m_iAnchorPoint][0];
					m_Anchor[1] = m_pPts->data[m_iAnchorPoint][1];
				}
			}
		// send a repaint message
		g_pToolWnd->Redraw();
		return true;
	}
	return false;
}

bool CControlPointsManagerBFace::OnMouseMove( int xPos, int yPos ){
	if ( ManagerState == Drag ) {
		if ( m_bGotAnchor ) {
			// there's an anchor, we are rotating the shape
			// we need to work in XY space for orthonormality
			float Pt[2];
			vec3_t V1,V2;
			vec3_t cross;
			float c,s;
			// used in XY space
			float XYTM[2][3];
			float XYRefAnchor[2];
			float XYAnchor[2];
			m_p2DView->GridForWindow( Pt, xPos, yPos );
			V2[0] = Pt[0] - m_Anchor[0];
			V2[1] = Pt[1] - m_Anchor[1];
			V2[2] = 0.0f;
			V1[0] = m_RefPts.data[m_iDragPoint][0] - m_RefPts.data[m_iAnchorPoint][0];
			V1[1] = m_RefPts.data[m_iDragPoint][1] - m_RefPts.data[m_iAnchorPoint][1];
			V1[2] = 0.0f;
			// compute transformation from V1 to V2
			// we need to work in XY orthonormal space
			XYSpaceForSTSpace( V1, V1 );
			XYSpaceForSTSpace( V2, V2 );
			VectorNormalize( V2, V2 );
			VectorNormalize( V1, V1 );
			c = DotProduct( V1, V2 );
			CrossProduct( V1, V2, cross );
			s = VectorLength( cross );
			// we compute the transformation matrix in XY space
			// reference position of the Anchor in XY space
			XYSpaceForSTSpace( XYRefAnchor, m_RefPts.data[m_iAnchorPoint] );
			// current position of the Anchor in XY space
			XYSpaceForSTSpace( XYAnchor, m_Anchor );
			// compute transformation matrix
			XYTM[0][0] = c; XYTM[1][1] = c;
			if ( cross[2] > 0 ) {
				s *= -1.0f;
			}
			XYTM[0][1] = s; XYTM[1][0] = -s;
			XYTM[0][2] = -c * XYRefAnchor[0] - s * XYRefAnchor[1] + XYAnchor[0];
			XYTM[1][2] = s * XYRefAnchor[0] - c * XYRefAnchor[1] + XYAnchor[1];
			// express this transformation matrix in ST space
			m_TM[0][0] = XYTM[0][0];
			m_TM[1][0] = XYTM[1][0] * (float)m_TexSize[0] / (float)m_TexSize[1];
			m_TM[0][1] = XYTM[0][1] * (float)m_TexSize[1] / (float)m_TexSize[0];
			m_TM[1][1] = XYTM[1][1];
			m_TM[0][2] = XYTM[0][2] / (float)m_TexSize[0];
			m_TM[1][2] = XYTM[1][2] / (float)m_TexSize[1];
			// update all points
			UpdateCtrlPts();
		}
		else
		{
			// no Anchor point is defined, we translate all points
			m_p2DView->GridForWindow( m_pPts->data[m_iDragPoint], xPos, yPos );
			m_TM[0][2] = m_pPts->data[m_iDragPoint][0] + m_TransOffset[0];
			m_TM[1][2] = m_pPts->data[m_iDragPoint][1] + m_TransOffset[1];
			// update all points
			UpdateCtrlPts();
		}
		// send a repaint message
		g_pToolWnd->Redraw();
		return true;
	}

	return false;
}

bool CControlPointsManagerBFace::OnLButtonUp( int x, int y ){
	if ( ManagerState == Drag ) {
		// this button is gonna become our Anchor
		m_bGotAnchor = true;
		m_iAnchorPoint = m_iDragPoint;
		// let's get out of Drag mode
		ManagerState = Idle;
		// send a repaint message
		g_pToolWnd->Redraw();
		return true;
	}
	return false;
}

void CControlPointsManagerBFace::render(){
	int i;

	m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
	m_pQglTable->m_pfn_qglPointSize( 6 );
	m_pQglTable->m_pfn_qglBegin( GL_POINTS );
	for ( i = 0; i < m_NumPoints; i++ )
	{
		if ( ManagerState == Drag && i == m_iDragPoint ) {
			m_pQglTable->m_pfn_qglColor3f( 1, 0, 0 );
		}
		else if ( m_bGotAnchor && i == m_iAnchorPoint ) {
			m_pQglTable->m_pfn_qglColor3f( 0, 0, 1 );
		}
		m_pQglTable->m_pfn_qglVertex2f( m_pPts->data[i][0], m_pPts->data[i][1] );
		m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
	}
	m_pQglTable->m_pfn_qglEnd();
}

void CControlPointsManagerBFace::UpdateCtrlPts(){
	int i;

	// update all points
	for ( i = 0; i < m_NumPoints; i++ )
	{
		m_pPts->data[i][0] = m_RefPts.data[i][0] * m_TM[0][0] + m_RefPts.data[i][1] * m_TM[0][1] + m_TM[0][2];
		m_pPts->data[i][1] = m_RefPts.data[i][0] * m_TM[1][0] + m_RefPts.data[i][1] * m_TM[1][1] + m_TM[1][2];
	}

	if ( g_bPrefsUpdateCameraView ) {
		Commit();
		// tell Radiant to update
		// NOTE: little speed optimisation, disable window updates, and only update camera view
		g_FuncTable.m_pfnSetScreenUpdate( false );
		g_SelectedFaceTable.m_pfnSetFaceInfo( 0, m_pFaceData );
		g_FuncTable.m_pfnSetScreenUpdate( true );
		g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
	}
}

//++timo FIXME: we are using a global for the reference data, use a m_pCancelFaceData instead
void CControlPointsManagerBFace::Commit(){
	brushprimit_texdef_t aux;
	aux.coords[0][0] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0];
	aux.coords[0][1] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1];
	aux.coords[0][2] = m_TM[0][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[0][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[0][2];
	aux.coords[1][0] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][0] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][0];
	aux.coords[1][1] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][1] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][1];
	aux.coords[1][2] = m_TM[1][0] * g_CancelFaceData.brushprimit_texdef.coords[0][2] + m_TM[1][1] * g_CancelFaceData.brushprimit_texdef.coords[1][2] + m_TM[1][2];
	memcpy( &m_pFaceData->brushprimit_texdef, &aux, sizeof( brushprimit_texdef_t ) );
}

void CControlPointsManagerBFace::ComputeTransOffset( int i ){
	// compute the translation offset used to counteract rotation
	m_TransOffset[0] = -m_TM[0][0] * m_RefPts.data[i][0] - m_TM[0][1] * m_RefPts.data[i][1];
	m_TransOffset[1] = -m_TM[1][0] * m_RefPts.data[i][0] - m_TM[1][1] * m_RefPts.data[i][1];
}

void CControlPointsManagerBFace::XYSpaceForSTSpace( float xy[2], const float st[2] ){
	xy[0] = st[0] * (float)m_TexSize[0];
	xy[1] = st[1] * (float)m_TexSize[1];
}

/*
   ======================================================================
   patch manager
   ======================================================================
 */

void CControlPointsManagerPatch::Init( patchMesh_t* pWorkPatch, C2DView *p2DView, OpenGLBinding *pQglTable, patchMesh_t* pPatch ){
	CControlPointsManager::Init( p2DView, pQglTable );
	m_pPatch = pPatch;
	m_pWorkPatch = pWorkPatch;
}

bool CControlPointsManagerPatch::OnLButtonDown( int xPos, int yPos ){
	if ( ManagerState == Idle ) {
		int i,j;

		// scan the point list to see if we selected something
		for ( i = 0; i < m_pPatch->width; i++ )
			for ( j = 0; j < m_pPatch->height; j++ )
				if ( m_p2DView->DoesSelect( xPos, yPos, m_pWorkPatch->ctrl[i][j].st ) ) {
					m_iDragPoint[0] = i;
					m_iDragPoint[1] = j;
					ManagerState = Drag;
				}
		// send a repaint message
		g_pToolWnd->Redraw();
		return true;
	}
	return false;
}

bool CControlPointsManagerPatch::OnMouseMove( int xPos, int yPos ){
	if ( ManagerState == Drag ) {
		m_p2DView->GridForWindow( m_pWorkPatch->ctrl[ m_iDragPoint[0] ][ m_iDragPoint[1] ].st, xPos, yPos );
		if ( g_bPrefsUpdateCameraView ) {
			Commit();
			// ask to rebuild the patch display data
			m_pPatch->bDirty = true;
			// send a repaint to the camera window as well
			g_FuncTable.m_pfnSysUpdateWindows( W_CAMERA );
		}
		// send a repaint message
		g_pToolWnd->Redraw();
		return true;
	}
	return false;
}

bool CControlPointsManagerPatch::OnLButtonUp( int x, int y ){
	if ( ManagerState == Drag ) {
		ManagerState = Idle;
		// send a repaint message
		g_pToolWnd->Redraw();
	}
	return false;
}

void CControlPointsManagerPatch::render(){
	int i,j;

	m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
	m_pQglTable->m_pfn_qglPointSize( 6 );
	m_pQglTable->m_pfn_qglBegin( GL_POINTS );
	for ( i = 0; i < m_pPatch->width; i++ )
		for ( j = 0; j < m_pPatch->height; j++ )
		{
			if ( ManagerState == Drag && i == m_iDragPoint[0] && j == m_iDragPoint[1] ) {
				m_pQglTable->m_pfn_qglColor3f( 1, 0, 0 );
			}
			m_pQglTable->m_pfn_qglVertex2f( m_pWorkPatch->ctrl[i][j].st[0], m_pWorkPatch->ctrl[i][j].st[1] );
			m_pQglTable->m_pfn_qglColor3f( 0, 1, 0 );
		}
	m_pQglTable->m_pfn_qglEnd();
}

void CControlPointsManagerPatch::Commit(){
	int i,j;
	for ( i = 0; i < m_pPatch->width; i++ )
		for ( j = 0; j < m_pPatch->height; j++ )
		{
			m_pPatch->ctrl[i][j].st[0] = m_pWorkPatch->ctrl[i][j].st[0];
			m_pPatch->ctrl[i][j].st[1] = m_pWorkPatch->ctrl[i][j].st[1];
		}
}
