//////////////////////////////////////////////////////////////////////////////////////////
//
//  FILE        :  Tools_Mode_Carpet.cpp
//
//  Created by Dave Reed - 22/11/05
//
//  Copyright (C) 2005 Rebellion (Derby), All Rights Reserved.
//
//  'Carpet' grass editing/painting
//
//////////////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_USE_CARPET

#include "Core/Asura_UserInput.h"
#include "Core/Asura_LOS.h"
#include "Core/Asura_Timers.h"

#include "Carpet/Asura_Carpet_StyleManager.h"
#include "Carpet/Asura_Carpet_MeshManager.h"
#include "Carpet/Asura_Direct3D_Carpet_Game_Render.h"

#include "Tools_Mode_Carpet.h"
#include "Tools_Carpet_Mesh.h"
#include "Tools_Carpet_Styles.h"

#include "Misc/Tools_Render_Options.h"
#include "Misc/Tools_Polygon_Selection.h"
#include "Misc/Tools_Timed_Messages.h"
#include "Misc/Tools_LOS.h"

#include "Objects/Tools_Object_System.h"

#include "Debugging/Asura_Logfile.h"
#include "Interface/Asura_Interface.h"
#include "Interface/Asura_CommandConsole.h"
#include "Interface/Asura_Menubar_System.h"
#include "Interface/Asura_Menubar.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Dialog.h"

#include "Rendering/Asura_Module.h"
#include "Mesh/Asura_Mesh.h"
#include "Load&Save/Tools_SaveAsura.h"
#include "Tools_Overlays.h"
#include "Tools_System.h"

#include "Lighting/Tools_Environment_LOS.h"

#ifdef ASURA_USE_BOTANICALS
#include "Botanicals/Tools_Mode_Botanicals.h"
#endif

// Data for the overlays
Asura_Handle	Tools_Mode_Carpet::s_iMenuHandle=ASURA_INVALID_HANDLE;
bool			Tools_Mode_Carpet::s_bPaintOverlayOpen=false;
bool            Tools_Mode_Carpet::s_bPaintingActive=false;
float		    Tools_Mode_Carpet::s_fSprayRate=20.0f;
float		    Tools_Mode_Carpet::s_fSprayRadius=10.0f;
float		    Tools_Mode_Carpet::s_fMaxSlopeAngle=45.0f;
Asura_Vector_2  Tools_Mode_Carpet::s_xPaintOverlayPosition=Asura_Vector_2(1.0f, 15.0f);
bool			Tools_Mode_Carpet::s_bShowMesh=true;
char			Tools_Mode_Carpet::s_szCarpetMeshPolys[128]="";
int             Tools_Mode_Carpet::s_iSelectedStyle= -1;
bool			Tools_Mode_Carpet::s_bUseTextureFlag = false;
bool			Tools_Mode_Carpet::s_bUsePolyFlag = true;

bool			Tools_Mode_Carpet::s_bStaticObjectsVisible=false;
bool			Tools_Mode_Carpet::s_bStaticObjectsCreated=false;
bool			Tools_Mode_Carpet::s_bShowStaticObjects=false;
bool			Tools_Mode_Carpet::s_bHideCarpet = false;
bool			Tools_Mode_Carpet::s_bHideCarpetClicked = false;

static			Asura_Overlay*	g_pxPaintOverlay=NULL;

// static this pointer
Tools_Mode_Carpet* Tools_Mode_Carpet::s_pxThis = NULL;

extern void     Render_Sphere( const Asura_Vector_3& xCentre, float fRadius, int iColour );


void Tools_Mode_Carpet::Initialise(void)
{	
	// store this pointer
	// for static access
	s_pxThis = this;
	
	Tools_Timed_Message_System::AddMessage(0x00ff00, "Switched to Carpet Mode");

	// Create the overlays
	CreatePaintOverlay();	

	// Create the 'carpet' item on the main menu
	s_iMenuHandle = Asura_Menubar_System::CreateMenuSection();
	Asura_Menubar_Title* pxTitle;
	pxTitle = Asura_Menubar_System::AddTitle(s_iMenuHandle,"Carpet");
	pxTitle->AddItem("Painting", Tools_Mode_Carpet::TogglePaintOverlay,Tools_Mode_Carpet::PaintOverlayIsOn);

	// Init the carpet mesh
	Tools_Carpet_Mesh::Init();

	// Guarantee the LOS data is up-to-date
	Tools_Environment_LOS::Reset();

	// Load existing mesh
	Asura_Carpet_MeshManager::ConvertToToolData(false);

	// Init the style editor
	Tools_Carpet_Styles::Initialise();

	// Init static object setting
	s_bStaticObjectsVisible=false;
	s_bStaticObjectsCreated=false;
	s_bShowStaticObjects=false;

	s_bHideCarpet = Tools_System::IsHideCarpetChecked();

	//Commented out the line below , since I'm not sure it makes sense to mark the carpet as unmodified
	//when re-entering the mode. RAR 26/06/2007
	//SetModified( false );
}



void Tools_Mode_Carpet::TogglePaintOverlay(void)
{
	if (!s_bPaintOverlayOpen)
	{
		CreatePaintOverlay();	
	}
	else
	{
		Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_CARPET_PAINT);
		s_bPaintOverlayOpen=false;
	}
}




void Tools_Mode_Carpet::CreatePaintOverlay(void)
{
	// If it's already open, don't create another
	if(s_bPaintOverlayOpen) return;
	s_bPaintOverlayOpen=true;

	Asura_Overlay* pxNewOverlay;
	pxNewOverlay = new Asura_Overlay("Carpet Painting",false,&s_xPaintOverlayPosition);
	g_pxPaintOverlay=pxNewOverlay;

	Asura_Dialog*  pxNewDialog;
	Asura_Vector_2 xOffset = Asura_Vector_2(1.0f,3.0f);

	
	pxNewDialog = new Asura_Dialog(s_szCarpetMeshPolys,xOffset);		
	pxNewDialog->m_iControlID = DialogID_PolyCount;
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();

	
	pxNewDialog = new Asura_Dialog("Active style: <None>",xOffset);
	pxNewDialog->SetHelpText("Style to be placed");
	pxNewDialog->m_iControlID = DialogID_SelectedStyle;
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();

	UpdateSelectedPaintStyle(s_iSelectedStyle);	

	pxNewDialog = new Asura_Function_Dialog("Select Style [T]",xOffset, _ChangeActiveStyle );
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog("Edit Styles",xOffset, _EditStyles );
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();
	
#ifdef ASURA_TARGET_SM3
	// SM3-only for now - Per-level speed control
	pxNewDialog = new Asura_Numerical_Dialog("Movement/Wind Speed", xOffset, &Asura_Carpet_MeshManager::s_fMovementSpeed, 0.0f, 5.0f, 0.01f, "%.01f");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::LargeSpacing();
#endif

	pxNewDialog = new Asura_Tickbox_Dialog("Painting Active [P]",xOffset,&s_bPaintingActive);
	pxNewDialog->SetHelpText("If ticked then the mouse buttons will be used for spraying grass, instead of polygon selection.\nShortcut key : P");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Dialog( "(to delete, use ctrl & right mouse button)", xOffset );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += ( 1.5f * Asura_Dialog::DefaultSpacing() );

	pxNewDialog = new Asura_Numerical_Dialog("Spray radius", xOffset, &s_fSprayRadius, 0.0f, 100.0f, 0.1f, "%.1f");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Numerical_Dialog("Spray rate", xOffset, &s_fSprayRate, 1.0f, 100.0f, 1.0f, "%.1f");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Numerical_Dialog("Max Slope (degrees)", xOffset, &s_fMaxSlopeAngle, 0.0f, 90.0f, 1.0f, "%.1f");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();
	
	pxNewDialog = new Asura_Tickbox_Dialog("Use 'Accept Carpet' flag",xOffset,&s_bUseTextureFlag);
	pxNewDialog->SetHelpText("Use this texture flag to filter out polys which should be carpetable");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Tickbox_Dialog( "Use 'Dont Render' polygon flag", xOffset, &s_bUsePolyFlag );
	pxNewDialog->SetHelpText( "Use this polygon flag to filter out polys which do not render" );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += ( 1.5f * Asura_Dialog::DefaultSpacing() );

	pxNewDialog = new Asura_Tickbox_Dialog("Show carpet mesh",xOffset,&s_bShowMesh);
	pxNewDialog->SetHelpText("Show the mesh used to define the carpeted areas");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();

#ifdef ASURA_USE_BOTANICALS
	pxNewDialog = new Asura_Tickbox_Dialog("Show static objects",xOffset,&s_bShowStaticObjects);
	pxNewDialog->SetHelpText("Show static objects whilst editing carpet");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.0f*Asura_Dialog::DefaultSpacing();
#endif

	pxNewDialog = new Asura_Tickbox_Dialog("Hide Carpet [H]",xOffset,&s_bHideCarpet,&s_bHideCarpetClicked);
	pxNewDialog->SetHelpText("Disables the carpet rendering");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y+=1.5f*Asura_Dialog::DefaultSpacing();


	

	pxNewDialog = new Asura_Function_Dialog("Update Lighting",xOffset,_UpdateLighting);
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += ( 1.5f * Asura_Dialog::DefaultSpacing() );

	pxNewDialog = new Asura_Function_Dialog("Clean Mesh",xOffset, _CleanMesh);
	pxNewDialog->SetHelpText("Removes any carpet polys that don't match the current level mesh");
	pxNewOverlay->AddDialog(pxNewDialog);
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog( "Clear Mesh", xOffset, _ClearMesh );
	pxNewDialog->SetHelpText( "Clears the carpet mesh." );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewDialog = new Asura_Function_Dialog( "Clear any with 'Dont Render' flag set", xOffset, _ClearMeshIfDontRender );
	pxNewDialog->SetHelpText( "Clears the carpet mesh of any polygons that have the 'DontRender' flag set." );
	pxNewOverlay->AddDialog( pxNewDialog );
	xOffset.y += Asura_Dialog::DefaultSpacing();

	pxNewOverlay->m_uID = TOOLS_OVERLAY_CARPET_PAINT;
	Asura_Overlay_System::SubmitNewOverlay(pxNewOverlay,false);	
}

void Tools_Mode_Carpet::Update(void)
{

	// Mouse wheel resizes the spray volume
	if (s_bPaintingActive)
	{
		UpdatePainting();
	}

	if(s_bPaintOverlayOpen)
	{
		// Update poly count
		Asura_PrintfToBfr(s_szCarpetMeshPolys,"Carpet mesh polys: %d",128,true,Tools_Carpet_Mesh::GetNumTriangles());
		g_pxPaintOverlay->UpdateDialogLabel(DialogID_PolyCount,s_szCarpetMeshPolys);

		// 'P' Toggle painting
		if(Asura_UserInput::DebouncedKeyPressed('p'))
		{
			s_bPaintingActive = !s_bPaintingActive;		
		}

	}

	if(!Asura_Overlay_System::OverlaysHaveKeyboardFocus())
	{
		if(!Asura_UserInput::KeyPressed(ASURA_KEYID_SHIFT) && 
			!Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL) &&
			!Asura_UserInput::KeyPressed(ASURA_KEYID_ALT))
		{
			//'H' Hide/unhide carpet
			if(Asura_UserInput::DebouncedKeyPressed('h'))
			{
				s_bHideCarpetClicked = true;
			}
			if(Asura_UserInput::DebouncedKeyPressed('t'))
			{
				ChangeActiveStyle();
			}
		}
	}

	//Update carpet visibility
	if(s_bHideCarpetClicked)
	{
		Tools_System::ToggleHideCarpet();
		s_bHideCarpetClicked = false;
	}
	s_bHideCarpet = Tools_System::IsHideCarpetChecked();

	
	// If painting isn't active, allow normal selection/movement
	Tools_Object_System::EnableAllControls(!s_bPaintingActive);		

	// Update the style editor
	Tools_Carpet_Styles::Update();

#ifdef ASURA_USE_BOTANICALS
	// Update 'show static objs' state
	Tools_Mode_Botanicals::UpdateStaticObjects(&s_bStaticObjectsVisible,&s_bStaticObjectsCreated,&s_bShowStaticObjects);
#endif
}


void Tools_Mode_Carpet::RenderWorld_PreToneMap(void)
{
	// No carpet rendering in wireframe mode
	if (Tools_Render_Options::GetWireframeMode()!=TOOLS_WIREFRAME_ON && !s_bHideCarpet)
	{	
		Tools_Carpet_Mesh::Render(s_bShowMesh,s_iSelectedStyle);
	}
}


void Tools_Mode_Carpet::RenderWorld_PostToneMap(void)
{
	// If we're in paint mode, update that, as long as we're not 
	// trying to use the UI

	if (s_bPaintingActive && !Asura_Interface::UsingMenusOrOverlays())
	{
		RenderPainting();
	}
}

void Tools_Mode_Carpet::RenderUI(void)
{
	// Render the style editor
	Tools_Carpet_Styles::Render();
}


void Tools_Mode_Carpet::Shutdown(void)
{

	// Save the mesh
	Asura_Carpet_MeshManager::ConvertFromToolData();

	//delete all the objects
	Tools_Object_System::DeleteAllModeSpecific();
	Tools_Object_System::EnableAllControls(false);


	// Switch out of paint mode, as it makes it very easy to mess things up
	// by accidentally spraying everywhere...
	s_bPaintingActive=false;

	// Close our overlays
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_CARPET_PAINT);
	s_bPaintOverlayOpen=false;

	// Remove our menu
	if(s_iMenuHandle != ASURA_INVALID_HANDLE)
	{
		Asura_Menubar_System::DestroyMenuSection(s_iMenuHandle);
		s_iMenuHandle = ASURA_INVALID_HANDLE;
	}	

	// Clear the tools copy of the carpet mesh
	Tools_Carpet_Mesh::Reset();

	// Shutdown the style editor
	Tools_Carpet_Styles::Shutdown();
	
	// DLH - 19 January 2006 15:24:17 - Create Direct3D optimised data for rendering
	Asura_Direct3D_Carpet_Game_Render::MakeDataSet( Asura_Carpet_MeshManager::GetTriangleList());
}




void Tools_Mode_Carpet::UpdatePainting(void)
{	
	UpdateSelectedPaintStyle(s_iSelectedStyle);

	if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEWHEELUP))
	{
		if (s_fSprayRadius>0.5f)
			s_fSprayRadius-=0.5f;
	}

	if(Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEWHEELDOWN))
	{
		if (s_fSprayRadius<100.0f)
			s_fSprayRadius+=0.5f;
	}		
}



void Tools_Mode_Carpet::RenderPainting(void)
{
	//draw a sphere where the cursor is
	int iPoly,iModule,iCount=0;
	Asura_Vector_3 xPosition;

	Asura_LinkList<Asura_Vector_3> xVertList;

	float fSprayRadiusSquared=s_fSprayRadius*s_fSprayRadius;

	
	if(Tools_LOS::FindPolyAndPositionUnderCursor(iPoly,iModule,xPosition))
	{				
		Render_Sphere(xPosition,s_fSprayRadius,0x7f8040ff);

		// Find verts within sphere, and attempt to update them
		for(int i=0;i<Asura_Module_System::GetNumberOfModules();i++)
		{
			Asura_Vector_3 xCentre=Asura_Module_System::GetCentreOfModule(i);
			float		   fRadius=Asura_Module_System::GetRadiusOfModule(i);

			Asura_Vector_3 xOfs=xPosition-xCentre;
			
			// Test the sphere cursor agains the module's bounding sphere
			if (xOfs.MagnitudeSquared()<= ((fRadius+s_fSprayRadius)*(fRadius+s_fSprayRadius)))
			{
				// We need to test all the verts
				Asura_Mesh* pxMesh = Asura_Module_System::GetMeshForModule(i);
				Asura_Vector_3 xModulePosition = Asura_Module_System::GetPositionOfModule(i);
				
				if (pxMesh->VertexDataValid)
				{				
					int iNumVerts=pxMesh->NumberOfVertices;
					
					for (int j=0;j<iNumVerts;j++)
					{									
						Asura_Vector_3 xVertexWorldPos= pxMesh->VertexDataPtr[j].Position+xModulePosition;

						Asura_Vector_3 xOfs=xVertexWorldPos-xPosition;

						if (xOfs.MagnitudeSquared()<=fSprayRadiusSquared)
						{
							// Need to add the vert to the list to update, only
							// if it doesn't already exist. Otherwise we'll get problems
							// at module joins, where the verts are duplicated
							bool bFound=false;							
							for (Asura_ListElt<Asura_Vector_3>* pxElt=xVertList.Front(); pxElt; pxElt=pxElt->Next())
							{		
								Asura_Vector_3& xPos=pxElt->Data();
								if ( xPos.ApproximatelyEquals( xVertexWorldPos, 0.001f ) )
								{
									bFound=true;
								}
							}

							if (!bFound)
							{
								xVertList.Push_Back(xVertexWorldPos);
								iCount++;
							}							
						}
					}
				}				
			}
		}
	}
	


	if (Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON2) && Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
	{
		Tools_Carpet_Mesh::Delete(xPosition,s_fSprayRadius);
		SetModified();
	}
	else
	{	
		bool bLeftMouse = Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON1);
		bool bRightMouse = Asura_UserInput::KeyPressed(ASURA_KEYID_MOUSEBUTTON2);
		
		if (bLeftMouse || bRightMouse)
		{
			Tools_Carpet_Mesh::BeginAdjustVertex(xPosition,s_fSprayRadius);
		}

		// Iterate through the list, updating the verts
		for (Asura_ListElt<Asura_Vector_3>* pxElt=xVertList.Front(); pxElt; pxElt=pxElt->Next())
		{		
			Render_Sphere(pxElt->Data(),0.075f,0xffffff80);

			if (bLeftMouse)
			{
				if (s_iSelectedStyle<0)
				{
					Tools_Timed_Message_System::AddMessage(0x00ff00, "No style selected!");
				}
				else
				{			
					Tools_Carpet_Mesh::AdjustVertex(pxElt->Data(),s_iSelectedStyle,s_fSprayRate*0.0005f,false,false,s_fMaxSlopeAngle, false, s_bUseTextureFlag, s_bUsePolyFlag );
					SetModified();
				}
			}

			if (bRightMouse)
			{
				bool bDeleteMode=false;
				if(Asura_UserInput::KeyPressed(ASURA_KEYID_CTRL))
				{
					bDeleteMode=true;
				}			

				Tools_Carpet_Mesh::AdjustVertex(pxElt->Data(),s_iSelectedStyle,-s_fSprayRate*0.0005f,false,false,s_fMaxSlopeAngle,bDeleteMode, s_bUseTextureFlag, s_bUsePolyFlag );
				SetModified();
			}		
		}

		if (bLeftMouse || bRightMouse)
		{
			Tools_Carpet_Mesh::EndAdjustVertex();
		}

		
		xVertList.Clear();
	}



}



void Tools_Mode_Carpet::UpdateSelectedPaintStyle(int iStyle)
{		
	s_iSelectedStyle=iStyle;

	if (s_iSelectedStyle>=Asura_Carpet_StyleManager::GetNumStyles())
	{
		s_iSelectedStyle=Asura_Carpet_StyleManager::GetNumStyles()-1;		
	}

	static char s_szText[64];

	if (s_bPaintOverlayOpen)
	{
		if (s_iSelectedStyle>=0)			
		{				
			sprintf(s_szText,"Style: %s",Asura_Carpet_StyleManager::GetStyleName(s_iSelectedStyle));
		}
		else
		{
			sprintf(s_szText,"Style: <none>","");
		}

		g_pxPaintOverlay->UpdateDialogLabel(DialogID_SelectedStyle,s_szText);
	}
}



void Tools_Mode_Carpet::ChangeActiveStyle()
{
	s_bPaintingActive=false;
	ListSelection_Overlay xOverlay("Select Carpet Style:");

	xOverlay.AddItem("<None>",0);
	for (int i=0;i<Asura_Carpet_StyleManager::GetNumStyles();i++)	
	{		
		xOverlay.AddItem(Asura_Carpet_StyleManager::GetStyleName(i), 1+i);	
	}

	if (!xOverlay.DoModal()) return;
	UpdateSelectedPaintStyle(xOverlay.GetSelectionData()-1);	
}


bool Tools_Mode_Carpet::IsModified()
{
	return Tools_SaveAsura::IsCarpetModified();
}

void Tools_Mode_Carpet::SetModified(bool bHasChanged /*=true*/)
{
	Tools_SaveAsura::SetCarpetModified(bHasChanged);
}

void Tools_Mode_Carpet::QuickSave(void)
{		
	// Save just this mode
	PreSave();
	Tools_SaveAsura::SaveCarpet();
	PostSave();
}

void Tools_Mode_Carpet::PreSave()
{		
	Asura_Carpet_MeshManager::ConvertFromToolData();
}


void Tools_Mode_Carpet::PostSave()
{		
	Asura_Carpet_MeshManager::ConvertToToolData();
}


void Tools_Mode_Carpet::EditStyles()
{		
	// Bring up the style list, for adding/removing/editing styles
	s_bPaintingActive=false;
	Tools_Carpet_Styles::OpenStyleList();
	SetModified();
}


void Tools_Mode_Carpet::UpdateLighting()
{
	Tools_Carpet_Mesh::CaptureLighting();
	SetModified();
}



void Tools_Mode_Carpet::CleanMesh()
{
	Tools_Carpet_Mesh::CleanMesh();
	SetModified();
}

void Tools_Mode_Carpet::ClearMesh()
{
	Tools_Carpet_Mesh::ClearMesh();
	SetModified();
}

void Tools_Mode_Carpet::ClearMeshIfDontRender()
{
	Tools_Carpet_Mesh::ClearMeshIfDontRender();
	SetModified();
}

#endif //ASURA_USE_CARPET
