//
// Tools_UVEdit_Internal.h
//
// Created by Richard Rice - 24/04/2008
// 
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//

#ifndef __TOOLS_UVEDIT_INTERNAL_H__
#define __TOOLS_UVEDIT_INTERNAL_H__

class Asura_Mesh;
class Tools_UVEdit_SelectionUndo_EnvVert;

//////////////////////////////////////////////////////////////////////
class Tools_UVEdit_Overlay : public Asura_Overlay
{
public :
	Tools_UVEdit_Overlay(const char* szName, bool bAlwaysOnTop, Asura_Vector_2* pxPosition, u_int* puMinimisedState)
	:Asura_Overlay(szName, bAlwaysOnTop, pxPosition, puMinimisedState)
	{}

	~Tools_UVEdit_Overlay();

	
};

//////////////////////////////////////////////////////////////////////
class Tools_UVEdit_Dialog : public Asura_Dialog
{
public :
	Tools_UVEdit_Dialog(Asura_Vector_2& xOffset, Tools_UVEdit* pxUVEdit);

		//Allows control to prepare for the overlay being resized
	virtual void StartResize(u_int uResizeFlags);
	//handles user resizing the overlay. xDelta is the cumulative delta from the start of the operation
	virtual void UpdateResize(u_int uResizeFlags, const Asura_Vector_2& xDelta);
	//Gets the direction(s) in which this dialog can be resized
	virtual u_int GetAllowedResizeFlags() const {return ASURA_OVERLAY_RESIZE_HORIZONTAL | ASURA_OVERLAY_RESIZE_VERTICAL;}


	virtual void PerFrameUpdate();

private :
	Tools_UVEdit* m_pxUVEdit;

};

//////////////////////////////////////////////////////////////////////
class Tools_UVEdit_Gadget : public Asura_Gadget
{
public :
	Tools_UVEdit_Gadget(Asura_Vector_2& xOffset, Tools_UVEdit* pxUVEdit);

	virtual void Render( const Asura_Vector_2& xOrigin);
	virtual void Update( const Asura_Vector_2& xOrigin);

	void CheckForResize();
private :

	void RenderTexture(float fX, float fY, int iTextureID);
	void RenderChequerBackground(float fX, float fY);

	void RenderEdges();
	void RenderNodes(float fX, float fY);

	void RenderSelectionRectangle();

	void RenderDragRectangle();

	//renders line in coordinates relative to the edit area , clipping it to the edit area
	void RenderClippedLine(const Asura_Vector_2& xPos1, const Asura_Vector_2& xPos2, u_int uColour);
	void RenderClippedRectangle(const Asura_Bounding_Rectangle& xRect, u_int uColour);

	void RenderGrid();

	void UpdatePosition(const Asura_Vector_2& xOrigin);
	//Get position of cursor in pixel coordinates relative to the edit area
	Asura_Vector_2 GetRelativeCursorPos() const;


	Tools_UVEdit* m_pxUVEdit;

	//copy of size of edit area from the uv editor 
	Asura_Vector_2 m_xSize;

	//pixel coordinates of top left of main edit area (with indent for border)
	Asura_Vector_2 m_xTopLeftPos;

	//ID of mouse button currently being used for dragging
	int m_iHeldMouseButton;
};



//////////////////////////////////////////////////////////////////////
class Tools_UVEdit_Node
{
	friend class Tools_UVEdit_NodeManager;

public :
	Tools_UVEdit_Node();
	virtual ~Tools_UVEdit_Node(){};

	bool IsSelected() const {return m_bSelected;}
	bool IsSelectable() const {return m_bSelectable;}
	bool IsEquivalentToASelectedNode() const {return m_bEquivalentToASelectedNode;}

	bool IsSelected(Tools_UVEditSelectMode eSelectMode) const;

	const Asura_Vector_2& GetUV() const {return m_xUV;}
	void SetUV(const Asura_Vector_2& xUV) {m_xUV = xUV;}

	const Asura_Vector_2& GetInitialUV() const {return m_xInitialUV;}
	void UpdateInitialUV() {m_xInitialUV = m_xUV;}


	const Tools_UVEdit_Node* GetNextNode() const {return m_pxNext;}
	Tools_UVEdit_Node* GetNextNode() {return m_pxNext;}
	void SetNextNode(Tools_UVEdit_Node* pxNext){m_pxNext = pxNext;}

	//returns true if the node is an equivalent vertex from another polygon
	virtual bool IsEquivalent(const Tools_UVEdit_Node* pxNode) const {return false;}

	//returns true if the node is an equivalent vertex from another polygon	, and the UVs are the same
	virtual bool IsWelded(const Tools_UVEdit_Node* pxNode) const {return false;}


	//for determining which colour to use
	virtual bool IsPrimaryUV() const {return true;}

	//gets index into the polygon that this belongs to
	virtual int GetIndexInPoly() const {return 0;}

	virtual const Asura_Vector_3& GetPosition() = 0;

protected :
	Asura_Vector_2 m_xUV;
	Asura_Vector_2 m_xInitialUV;
	Tools_UVEdit_Node* m_pxNext;

	bool m_bSelected;
	bool m_bSelectable;
	bool m_bEquivalentToASelectedNode;
};

//////////////////////////////////////////////////////////////////////

class Tools_UVEdit_NodeManager
{
public :
	Tools_UVEdit_NodeManager(Tools_UVEdit* pxUVEdit);
	virtual ~Tools_UVEdit_NodeManager();

	virtual void UpdateNodes(){};
	virtual void Update(){}

	void Select(Tools_UVEdit_Node* pxNode, bool bAlsoSelectedWeldedNodes);
	void Select(Tools_UVEdit_Node* pxNode, Tools_UVEditSelectMode eSelectMode, bool bAlsoSelectedWeldedNodes);
	void Deselect(Tools_UVEdit_Node* pxNode, bool bAlsoDeselectedWeldedNodes);
	void Deselect(Tools_UVEdit_Node* pxNode, Tools_UVEditSelectMode eSelectMode, bool bAlsoDeselectedWeldedNodes);
	void ClearSelection();

	const List<Tools_UVEdit_Node*>* GetNodeList() const {return &m_xNodeList;}
	const List<Tools_UVEdit_Node*>* GetSelectedNodeList() const {return &m_xSelectedList;}

	bool IsAnySelection() const {return m_xSelectedList.size()!=0;}

	//Returns true if at least one node is selected , and if all the selected
	//nodes are in fact equivalent
	bool IsOneDistinctNodeSelected() const;

	//updates initial UV positions before doing drag operations
	void UpdateInitialUVs();

	//Apply the UVs for the selected nodes to the environment (or whatever)
	virtual void ApplyUV(){};

	//update UVs of nodes from the source mesh
	virtual void UpdateNodesFromSource(){};

	//initialises an undo if needed before editing the selected nodes
	virtual void PrepareUndo(){};

	//Ensures that m_bEquivalentToASelectedNode is up to date for each node
	void UpdateEquivalentToSelectionFlags();

	virtual bool IsEnvironmentNodeManager() const {return false;}

	virtual bool UndoSelection(){return false;}
	virtual bool RedoSelection(){return false;}
	virtual void LogSelection(const Tools_UVEdit_Node* pxNode, bool bSelect){};


protected :
	List<Tools_UVEdit_Node*> m_xNodeList;
	List<Tools_UVEdit_Node*> m_xSelectedList;

	bool m_bNeedToUpdateEquivalentToSelectionFlags;

	Tools_UVEdit* m_pxUVEdit;
};

class Tools_UVEdit_Node_EnvVert : public Tools_UVEdit_Node
{
public :
	Tools_UVEdit_Node_EnvVert(const Asura_Mesh* pxMesh, int iModuleIndex, int iPolygonIndex, int iVertInPoly, int iChannel, bool bSelectable);

	//returns true if the module/polygon/vert in poly and channel matches
	bool Matches(const Tools_UVEdit_Node_EnvVert* pxNode) const;

	//returns true if the node is an equivalent vertex from another polygon
	virtual bool IsEquivalent(const Tools_UVEdit_Node* pxNode) const;

	//returns true if the node is an equivalent vertex from another polygon	, and the UVs are the same
	virtual bool IsWelded(const Tools_UVEdit_Node* pxNode) const;

	virtual bool IsPrimaryUV() const {return m_iChannel == 0;}

	virtual int GetIndexInPoly() const {return m_iVertInPoly;}

	virtual const Asura_Vector_3& GetPosition() {return m_xPos;}

	//apply the UV to the mesh
	void ApplyUV();

	//get the UVs from the  mesh
	void UpdateFromMesh();

	int GetModuleIndex() const {return m_iModuleIndex;}
	int GetPolygonIndex() const {return m_iPolygonIndex;}
	int GetChannel() const {return m_iChannel;}

protected :
	int m_iModuleIndex;
	int m_iPolygonIndex;
	int m_iVertInPoly;
	int m_iChannel;

	Asura_Vector_3 m_xPos;
};
							    
//////////////////////////////////////////////////////////////////////

class Tools_UVEdit_NodeManager_Environment : public Tools_UVEdit_NodeManager
{
public :
	Tools_UVEdit_NodeManager_Environment(Tools_UVEdit* pxUVEdit);
	~Tools_UVEdit_NodeManager_Environment();

	virtual void Update();
	virtual void UpdateNodes();

	//Apply the UVs for the selected nodes to the environment (or whatever)
	virtual void ApplyUV();

	//update UVs of nodes from the source mesh
	virtual void UpdateNodesFromSource();

	//initialises an undo if needed before editing the selected nodes
	virtual void PrepareUndo();

	virtual bool IsEnvironmentNodeManager() const {return true;}

	Tools_UVEdit_Node_EnvVert* FindNode(int iModuleIndex, int iPolygonIndex, int iVertInPoly, int iChannel);

	virtual bool UndoSelection();
	virtual bool RedoSelection();
	virtual void LogSelection(const Tools_UVEdit_Node* pxNode, bool bSelect);

private :
	Tools_UVEdit_SelectionUndo_EnvVert* m_pxUndo;
	bool m_bDisableUndoLogging;
};

//////////////////////////////////////////////////////////////////////
//working data for doing the UV relaxing
class Tools_UVEdit_RelaxNode
{
public :
	void CalculateDestUV(const Asura_Vector_3& xUVNormal);
	Asura_Vector_3 GetUVNormal();

	Tools_UVEdit_Node* m_pxNode;
	//next and previous node in face
	Tools_UVEdit_Node* m_pxPrevNode;
	Tools_UVEdit_Node* m_pxNextNode;

	//linked list of equivalent to this one (ie same vertex in a different polygon)
	Tools_UVEdit_RelaxNode* m_pxPrevEquivNode;
	Tools_UVEdit_RelaxNode* m_pxNextEquivNode;

	//current destination UV
	Asura_Vector_2 m_xDestUV;

	//true if part of the selection boundary
	//only calculated if locking boundary nodes
	bool m_bBoundary;

	float m_fWeight;
};

/////////////////////////////////////////////////////////////////////
//selection undo

struct Tools_UVEdit_SelectionUndoData_EnvVert
{
	enum UndoType
	{
		UndoType_Select,
		UndoType_Deselect,
		UndoType_ForceInt = 0xffffffff,
	};
	
	enum UndoType m_eUndoType;
	u_short m_usModuleIndex;
	u_char m_ucVertInPoly;
	u_char m_ucChannel;
	int m_iPolygonIndex;
};

class Tools_UVEdit_SelectionUndo_EnvVert : public FixedSizeUndo
{
public :
	Tools_UVEdit_SelectionUndo_EnvVert(Tools_UVEdit_NodeManager_Environment* pxNodeManager)
	:FixedSizeUndo(2000, sizeof(Tools_UVEdit_SelectionUndoData_EnvVert))
	,m_pxNodeManager(pxNodeManager)
	{}

	void ProcessUndoData(void*);

	void LogNodeSelected(const Tools_UVEdit_Node_EnvVert* pxNode, bool bSelect);
private :
	Tools_UVEdit_NodeManager_Environment* m_pxNodeManager;
};

#endif //__TOOLS_UVEDIT_INTERNAL_H__
