/*****************/
/*** Layer RGB ***/
/*****************/


#include	"stdafx.h"
#include	<Vector3.h>
#include	<gl\gl.h>
#include	<gl\glu.h>
#include	"GLEnabledView.h"
#include	<io.h>
#include	<frame.hpp>

#include	"MapEdit.h"
#include	"MapEditDoc.h"
#include	"MapEditView.h"
#include	"MainFrm.h"

#include	"Core.h"
#include	"Layer.h"
#include	"LayerRGB.h"
#include	"Utils.h"
#include	"Select.h"
#include	"Export.h"
#include	"GUILayerRGB.h"
#include	"Elem.h"

/*****************************************************************************/
char	*CLayerRGB::RGBModeName[CLayerRGB::GUI_MODE_MAX]={"Paint","Lighten","Darken"};
float	RGBAlpha=0.5f;
int		RateInc=5;

#define	MAX_UNDO	16

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
// New Layer
CLayerRGB::CLayerRGB(sLayerDef &Def)
{
		InitLayer(Def);

		ShadeRGB.R=224;
		ShadeRGB.G=224;
		ShadeRGB.B=224;
		CurrentMode=0;
		CurrentBrush=0;
		CurrentRate=0;
		LastCursPos.x=-1;
		LastCursPos.y=-1;
		CurrentUndo=0;
}


/*****************************************************************************/
CLayerRGB::~CLayerRGB()
{
}

/*****************************************************************************/
void	CLayerRGB::InitLayer(sLayerDef &Def)
{
		CLayer::InitLayer(Def);
		SetSize(Def.Width,Def.Height,true);
		BuildBrushList();
}

/*****************************************************************************/
void	CLayerRGB::Load(CFile *File,int Version)
{
		InitLayer(LayerDef);

		File->Read(&ShadeRGB,sizeof(sRGBElem));
		File->Read(&CurrentBrush,sizeof(int));
		File->Read(&CurrentMode,sizeof(int));
		File->Read(&SpareFlag,sizeof(bool));

// Read Map
		File->Read(&MapWidth,sizeof(int));
		File->Read(&MapHeight,sizeof(int));
		SetSize(MapWidth,MapHeight,false);
		for (int Y=0; Y<MapHeight; Y++)
		{
			for (int X=0; X<MapWidth; X++)
			{
				sRGBElem	&ThisElem=Map[X][Y];

				File->Read(&ThisElem,sizeof(sRGBElem));
			}
		}
}

/*****************************************************************************/
void	CLayerRGB::Save(CFile *File)
{
// Always Save current version
		File->Write(&ShadeRGB,sizeof(sRGBElem));
		File->Write(&CurrentBrush,sizeof(int));
		File->Write(&CurrentMode,sizeof(int));
		File->Write(&SpareFlag,sizeof(bool));

// Read Map
		File->Write(&MapWidth,sizeof(int));
		File->Write(&MapHeight,sizeof(int));
		for (int Y=0; Y<MapHeight; Y++)
		{
			for (int X=0; X<MapWidth; X++)
			{
				sRGBElem	&ThisElem=Map[X][Y];

				File->Write(&ThisElem,sizeof(sRGBElem));
			}
		}
}

/*****************************************************************************/
void	CLayerRGB::BuildBrushList()
{
GString		Path;
GString		Filename;
_finddata_t Find;
long		FileHandle;
int			Error=0;

			BrushList.clear();

			GetExecPath(Path);
			Path+="\\Brushes\\";
			Filename=Path+"*.bmp";

			if( (FileHandle= _findfirst( Filename, &Find)) == -1L ) 
			{
				ASSERT(!"No Brushes Found :o(\n");
				return;
			}

			while (Error==0)
			{
				GString	ThisFile=Path+Find.name;
				LoadBrush(ThisFile);
				Error=_findnext( FileHandle, &Find);
			}
			_findclose( FileHandle);

}

/*****************************************************************************/
void	CLayerRGB::LoadBrush(const char *Name)
{
GFName	Filename=Name;
int		BrushCount=BrushList.size();
		BrushList.resize(BrushCount+1);			
sRGBBrush	&NewBrush=BrushList[BrushCount];
Frame		Frm;
int		W,H;
		NewBrush.Name=Filename.File();
		Frm.LoadBMP(Name);
		W=Frm.GetWidth();
		H=Frm.GetHeight();
		NewBrush.W=W;
		NewBrush.H=H;
		NewBrush.XOfs=W/2;
		NewBrush.YOfs=H/2;
		NewBrush.Gfx.resize(W*H);

int		Ofs=0;
		for (int Y=0;Y<H; Y++)
		{
			for (int X=0;X<W; X++)
			{
				u8	Col=Frm.GetPixel(X,Y);

				Col=Frm.GetPal()[Col].GetR();
				NewBrush.Gfx[Ofs++]=Col;
			}
		}

		

		TRACE1("%s\n",NewBrush.Name);

}

/*****************************************************************************/
bool	CLayerRGB::Command(int CmdMsg,CCore *Core,int Param0,int Param1)
{
bool	Ret=false;

		switch(CmdMsg)
		{
		case CmdMsg_Undo:
			Undo();
			break;
		case CmdMsg_Filter:
			CreateUndo();
			if (Param0==0) BiFilter(Core);
			if (Param0==1) TriFilter(Core);
			if (Param0==2) STriFilter(Core);
			break;
		default:
			break;
		}

		return(Ret);

}

/*****************************************************************************/
/*****************************************************************************/
/*****************************************************************************/
void	CLayerRGB::Render(CCore *Core,Vector3 &CamPos,bool Is3d)
{
Vector3		ThisCam=Core->OffsetCam(CamPos,GetScaleFactor());
float		ZoomW=Core->GetZoomW();
float		ZoomH=Core->GetZoomH();
float		ScrOfsX=(ZoomW/2);
float		ScrOfsY=(ZoomH/2);
Vector3		&Scale=Core->GetScaleVector();
int			StartX=(int)ThisCam.x;
int			StartY=(int)ThisCam.y;
float		ShiftX=ThisCam.x - (int)ThisCam.x;
float		ShiftY=ThisCam.y - (int)ThisCam.y;
CLayerTile	*ActionLayer=(CLayerTile*)Core->GetActionLayer();

		if (StartX<0) StartX=0;
		if (StartY<0) StartY=0;

int		DrawW=ZoomW+8;
int		DrawH=ZoomH+8;

		if (StartX+DrawW>MapWidth)	DrawW=MapWidth-StartX;
		if (StartY+DrawH>MapHeight)	DrawH=MapHeight-StartY;

		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glScalef(Scale.x,Scale.y,Scale.z);
		glTranslatef(-ShiftX,ShiftY,0);		// Set scroll offset
		glTranslatef(-ScrOfsX,ScrOfsY,0);	// Bring to top left corner

		for (int YLoop=0; YLoop<DrawH; YLoop++)
		{
			for (int XLoop=0; XLoop<DrawW; XLoop++)
			{
				int	XPos=StartX+XLoop;
				int	YPos=StartY+YLoop;
				
				sRGBElem	&ThisElem=Map[XPos][YPos];
				sMapElem	&MapElem=ActionLayer->GetMapElem(XPos,YPos);
				if (MapElem.Tile)
				{
					float	fR=(1.0f/255.0f)*ThisElem.R;
					float	fG=(1.0f/255.0f)*ThisElem.G;
					float	fB=(1.0f/255.0f)*ThisElem.B;
					glLoadName (0);
					glBegin (GL_QUADS); 
						glColor4f(fR,fG,fB,RGBAlpha);
						BuildGLQuad(0,1,0,1,0);
					glEnd();
				}
				glTranslatef(1.0f,0,0);	// Next X
			}
			glTranslatef(-DrawW,-1,0); // Next y, rewind to start X
		}
		glPopMatrix();
}


/*****************************************************************************/
void	CLayerRGB::RenderCursor(CCore *Core,Vector3 &CamPos,bool Is3d)
{
Vector3		ThisCam=Core->OffsetCam(CamPos,GetScaleFactor());
CPoint		CursPos=Core->GetCursorPos();

			if (CursPos.x<0 || CursPos.y<0) return;
sRGBBrush	&ThisBrush=BrushList[CurrentBrush];

			CursPos.x-=ThisBrush.XOfs;
			CursPos.y-=ThisBrush.YOfs;
			CursPos.x-=(int)ThisCam.x;
			CursPos.y-=(int)ThisCam.y;

float			ZoomW=Core->GetZoomW();
float			ZoomH=Core->GetZoomH();
float			ScrOfsX=(ZoomW/2);
float			ScrOfsY=(ZoomH/2);
Vector3			&Scale=Core->GetScaleVector();
float			ShiftX=ThisCam.x - (int)ThisCam.x;
float			ShiftY=ThisCam.y - (int)ThisCam.y;
std::vector<u8>	&Gfx=ThisBrush.Gfx;
int				Ofs=0;
int				Rate=100-(CurrentRate*RateInc);
float			fRate=(1.0/100.f)*Rate;
		
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glScalef(Scale.x,Scale.y,Scale.z);
		glTranslatef(-ShiftX,ShiftY,0);		// Set scroll offset
		glTranslatef(-ScrOfsX,ScrOfsY,0);	// Bring to top left corner
		glTranslatef(CursPos.x,-CursPos.y,0);	// Bring to top left corner

		for (int YLoop=0; YLoop<ThisBrush.H; YLoop++)
		{
			for (int XLoop=0; XLoop<ThisBrush.W; XLoop++)
			{
				u8	B=Gfx[Ofs++];
				
				if (B)
				{
					float	fRGB=(1.0f/255.0f)*fRate*B;

					glBegin (GL_QUADS); 
					glColor4f(fRGB,fRGB,fRGB,RGBAlpha);
						BuildGLQuad(0,1,0,1,0);
					glEnd();

				}
				glTranslatef(1.0f,0,0);	// Next X
			}
			glTranslatef(-ThisBrush.W,-1,0); // Next y, rewind to start X
		}
		glPopMatrix();

}

/*****************************************************************************/
void	CLayerRGB::CheckLayerSize(int Width,int Height)
{
		if (Resize(Width,Height))
		{
			CString mexstr;
			mexstr.Format("%s Layer Resized to Correct Size\nPlease re-save\n", GetName());
			AfxMessageBox(mexstr,MB_OK | MB_ICONEXCLAMATION);
		}
}

/*****************************************************************************/
void	CLayerRGB::SetSize(int Width,int Height,BOOL ClearFlag)
{
int	m,i;
		MapWidth=Width;
		MapHeight=Height;
		Map.resize(Width);
		for (i=0;i<Width;i++)
		{
			Map[i].resize(Height);
		}

		UndoList.resize(MAX_UNDO);
		TRACE1("%i\n",UndoList.size());
		for (m=0; m<MAX_UNDO; m++) 
		{
			sRGBUndo	&ThisUndo=UndoList[m];
			ThisUndo.Valid=false;
			ThisUndo.Map.resize(Width);
			for (i=0;i<Width;i++)
			{
				ThisUndo.Map[i].resize(Height);
			}
		}

	if (ClearFlag)	Clear();
}

/*****************************************************************************/
void	CLayerRGB::Clear()
{
		for (int Y=0;Y<MapHeight;Y++)
		{
			for (int X=0;X<MapWidth;X++)
			{
				Map[X][Y].R=128;
				Map[X][Y].G=128;
				Map[X][Y].B=128;
			}
		}
}


/*****************************************************************************/
bool	CLayerRGB::Resize(int Width,int Height)
{
		if (MapWidth!= Width || MapHeight!=Height)
		{
			SetSize(Width,Height,true);
			return(true);
		}
		return(false);
}

/*****************************************************************************/
/*** Gui *********************************************************************/
/*****************************************************************************/
void	CLayerRGB::GUIInit(CCore *Core)
{

int		ListSize,i;

		GUIRGB.DisableCallback();
		Core->GUIAdd(GUIRGB,IDD_LAYER_RGB);

// Init BrushList
		ListSize=BrushList.size();
		GUIRGB.m_BrushList.ResetContent();
		for (i=0; i<ListSize; i++)
		{
			GUIRGB.m_BrushList.AddString(BrushList[i].Name);
		}
// Init ModeList
		GUIRGB.m_ModeList.ResetContent();
		for (i=0; i<GUI_MODE_MAX; i++)
		{
			GUIRGB.m_ModeList.AddString(RGBModeName[i]);
		}

// Init RateList
		GUIRGB.m_RateList.ResetContent();
		for (i=0; i<100/RateInc; i++)
		{
			char	Str[8];
			sprintf(Str,"%i%",100-(i*RateInc));
			GUIRGB.m_RateList.AddString(Str);
		}

		GUIRGB.m_RSpin.SetRange(0,255);
		GUIRGB.m_GSpin.SetRange(0,255);
		GUIRGB.m_BSpin.SetRange(0,255);
		GUIRGB.EnableCallback();
		Core->RedrawView();
}

/*****************************************************************************/
void	CLayerRGB::GUIKill(CCore *Core)
{

		GUIChanged(Core);
		Core->GUIRemove(GUIRGB,IDD_LAYER_RGB);
}

/*****************************************************************************/
void	CLayerRGB::GUIUpdate(CCore *Core)
{
		GUIRGB.DisableCallback();

		GUIRGB.m_ModeList.SetCurSel(CurrentMode);
		GUIRGB.m_BrushList.SetCurSel(CurrentBrush);
		GUIRGB.m_RateList.SetCurSel(CurrentRate);
		GUIRGB.SetRGB(ShadeRGB.R,ShadeRGB.G,ShadeRGB.B);
		GUIRGB.EnableCallback();
}

/*****************************************************************************/
void	CLayerRGB::GUIChanged(CCore *Core)
{
		CurrentMode=GUIRGB.m_ModeList.GetCurSel();
		CurrentBrush=GUIRGB.m_BrushList.GetCurSel();
		CurrentRate=GUIRGB.m_RateList.GetCurSel();
		GUIRGB.GetRGB(ShadeRGB.R,ShadeRGB.G,ShadeRGB.B);

}

/*****************************************************************************/
/*** Functions ***************************************************************/
/*****************************************************************************/

bool	CLayerRGB::LButtonControl(CCore *Core,UINT nFlags, CPoint &CursorPos,bool DownFlag)
{
		if (DownFlag) 
		{
			if (CursorPos.x<0 || CursorPos.x>MapWidth) return(false);
			if (CursorPos.y<0 || CursorPos.y>MapHeight) return(false);
			CreateUndo();
			Paint(Core,CursorPos);
		}
		else
		{
			LastCursPos.x=-1;
			LastCursPos.y=-1;
		}
		return(true);
}

/*****************************************************************************/
bool	CLayerRGB::RButtonControl(CCore *Core,UINT nFlags, CPoint &CursorPos,bool DownFlag)
{
		if (DownFlag) Grab(Core,CursorPos);
		return(true);
}

/*****************************************************************************/
bool	CLayerRGB::MouseMove(CCore *Core,UINT nFlags, CPoint &CursorPos)
{
		if (nFlags & MK_LBUTTON)
		{
			Paint(Core,CursorPos);
		}
		return(true);
}

/*****************************************************************************/
void	CLayerRGB::CreateUndo()
{
sRGBUndo	&ThisUndo=UndoList[CurrentUndo];
		for (int y=0; y<MapHeight; y++)
		{
			for (int x=0; x<MapWidth; x++)
			{
				ThisUndo.Map[x][y]=Map[x][y];
			}
		}
		ThisUndo.Valid=true;
		CurrentUndo++;
		if (CurrentUndo>=MAX_UNDO) CurrentUndo=0;
}

/*****************************************************************************/
void	CLayerRGB::Undo()
{
int		Idx=CurrentUndo-1;

		if (Idx<0) Idx+=MAX_UNDO;
sRGBUndo	&ThisUndo=UndoList[Idx];
		if (ThisUndo.Valid)
		{
			CurrentUndo=Idx;
			for (int y=0; y<MapHeight; y++)
			{
				for (int x=0; x<MapWidth; x++)
				{
					Map[x][y]=ThisUndo.Map[x][y];
				}
			}
			ThisUndo.Valid=false;
		}

}
/*****************************************************************************/
void	CLayerRGB::Paint(CCore *Core,CPoint &CursorPos)
{

		if (CursorPos.x== LastCursPos.x && CursorPos.y==LastCursPos.y) return;
		LastCursPos=CursorPos;

sRGBBrush	&ThisBrush=BrushList[CurrentBrush];
std::vector<u8>		&Gfx=ThisBrush.Gfx;
CPoint		CursPos;
int			Ofs=0;

		CursPos.x=CursorPos.x-ThisBrush.XOfs;
		CursPos.y=CursorPos.y-ThisBrush.YOfs;

		for (int Y=0; Y<ThisBrush.H; Y++)
		{
			for (int X=0; X<ThisBrush.W; X++)
			{
				CPoint	Pos=CursPos;
				Pos.x+=X; 
				Pos.y+=Y;
				int	Blk=Gfx[Ofs++];

				if (Blk)
				if (Pos.x>=0 && Pos.x<MapWidth && Pos.y>=0 && Pos.y<MapHeight)
				{
					sRGBElem	MapElem=GetRGB(Pos.x,Pos.y);
					int			RGB;
					int			Rate=100-(CurrentRate*RateInc);
					float		fRate=(1.0/100.f)*Rate;
					int			RGBInc=(int)(fRate*(float)Blk);

					switch(CurrentMode)
					{
					case GUI_MODE_PAINT:
						RGB=RGBInc;
						break;
					case GUI_MODE_LIGHTEN:
						RGB=MapElem.R+RGBInc;
						break;
					case GUI_MODE_DARKEN:
						RGB=MapElem.R-RGBInc;
						break;
					}
					if (RGB<0) RGB=0; else if (RGB>255) RGB=255;
					Map[Pos.x][Pos.y].R=RGB;
					Map[Pos.x][Pos.y].G=RGB;
					Map[Pos.x][Pos.y].B=RGB;
				}
			}
		}

}

/*****************************************************************************/
void	CLayerRGB::Grab(CCore *Core,CPoint &CursorPos)
{
/*
		if (CursorPos.x<0 || CursorPos.x>MapWidth) return;
		if (CursorPos.y<0 || CursorPos.y>MapHeight) return;

		CurrentRGB=Map[CursorPos.x][CursorPos.y];
		GUIUpdate(Core);
*/
}

/*****************************************************************************/
void	CLayerRGB::BiFilter(CCore *Core)
{
		for(int Y=0;Y<MapHeight; Y++)
		{
			for(int X=0; X<MapWidth; X++)
			{	
				int	SCol=0,SCount=0;

//	            *p=(( (*p) + ( ( *(p+1)+*(p-1)+*(p-WIDTH)+*(p+WIDTH) ) >>2 ) )>>1);
				GetFilterCol(Core,X-1,Y+0,SCol,SCount);
				GetFilterCol(Core,X+1,Y+0,SCol,SCount);
				GetFilterCol(Core,X+0,Y-1,SCol,SCount);
				GetFilterCol(Core,X+0,Y+1,SCol,SCount);
				if (SCount)
				{
					SCol/=SCount;
					SetFilterCol(Core,X,Y,SCol,2);
				}
			}
		}
	
}

/*****************************************************************************/
void	CLayerRGB::TriFilter(CCore *Core)
{
		for(int Y=0;Y<MapHeight; Y++)
		{
			for(int X=0; X<MapWidth; X++)
			{	
				int	SCol=0,SCount=0;

//            *p=(( (*p) + ( (*(p+1)+*(p-1)+*(p-WIDTH)+*(p+WIDTH)+*(p-WIDTH-1)+*(p-WIDTH+1)+*(p+WIDTH-1)+*(p+WIDTH+1)) >>3 ) )>>1);
				GetFilterCol(Core,X-1,Y-1,SCol,SCount);
				GetFilterCol(Core,X+0,Y-1,SCol,SCount);
				GetFilterCol(Core,X+1,Y-1,SCol,SCount);
				GetFilterCol(Core,X-1,Y+0,SCol,SCount);
				GetFilterCol(Core,X+1,Y+0,SCol,SCount);
				GetFilterCol(Core,X-1,Y+1,SCol,SCount);
				GetFilterCol(Core,X+0,Y+1,SCol,SCount);
				GetFilterCol(Core,X+1,Y+1,SCol,SCount);
				if (SCount)
				{
					SCol/=SCount;
					SetFilterCol(Core,X,Y,SCol,2);
				}
			}
		}
	
}

/*****************************************************************************/
void	CLayerRGB::STriFilter(CCore *Core)
{
		for(int Y=0;Y<MapHeight; Y++)
		{
			for(int X=0; X<MapWidth; X++)
			{	
				int	SCol=0,SCount=0;
//            c=(( (*p) + ( (*(p+1)+*(p-1)+*(p-WIDTH)+*(p+WIDTH)+*(p-WIDTH-1)+*(p-WIDTH+1)+*(p+WIDTH-1)+*(p+WIDTH+1)) >>3 ) )>>1);
//            *(p-WIDTH-1)=c;
//            *(p-WIDTH+1)=c;
//            *(p+WIDTH-1)=c;
//            *(p+WIDTH+1)=c;

				GetFilterCol(Core,X-1,Y-1,SCol,SCount);
				GetFilterCol(Core,X+0,Y-1,SCol,SCount);
				GetFilterCol(Core,X+1,Y-1,SCol,SCount);
				GetFilterCol(Core,X-1,Y+0,SCol,SCount);
				GetFilterCol(Core,X+1,Y+0,SCol,SCount);
				GetFilterCol(Core,X-1,Y+1,SCol,SCount);
				GetFilterCol(Core,X+0,Y+1,SCol,SCount);
				GetFilterCol(Core,X+1,Y+1,SCol,SCount);
				if (SCount)
				{
					SCol/=SCount;
					SetFilterCol(Core,X-1,Y-1,SCol,1);
					SetFilterCol(Core,X+1,Y-1,SCol,1);
					SetFilterCol(Core,X-1,Y+1,SCol,1);
					SetFilterCol(Core,X+1,Y+1,SCol,1);
				}
			}
		}
	
}

/*****************************************************************************/
void	CLayerRGB::GetFilterCol(CCore *Core,int X,int Y,int &SumCol,int &Count)
{
CLayerTile	*ActionLayer=(CLayerTile*)Core->GetActionLayer();

		if (X>=0 && X<MapWidth && Y>=0 && Y<MapHeight)
		{
			sMapElem	&MapElem=ActionLayer->GetMapElem(X,Y);
			if (MapElem.Tile)
			{
				sRGBElem	&C=Map[X][Y];
				SumCol+=C.R;
				Count++;
			}
		}

}

/*****************************************************************************/
void	CLayerRGB::SetFilterCol(CCore *Core,int X,int Y,int Col,int Div)
{
CLayerTile	*ActionLayer=(CLayerTile*)Core->GetActionLayer();

		if (X>=0 && X<MapWidth && Y>=0 && Y<MapHeight)
		{
			sMapElem	&MapElem=ActionLayer->GetMapElem(X,Y);
			if (MapElem.Tile)
			{
				sRGBElem	&ThisElem=Map[X][Y];
				Col+=ThisElem.R;
				if (Div) Col/=Div;
				ThisElem.R=Col;
				ThisElem.G=Col;
				ThisElem.B=Col;
			}
		}
		
}
/*****************************************************************************/
void	CLayerRGB::Export(CCore *Core,CExport &Exp)
{
sRGBCol		RGB;

		Exp.ExportLayerHeader(LayerDef);//LAYER_TYPE_RGB,LayerDef.SubType,LayerDef.Width,LayerDef.Height);

		RGB.R=ShadeRGB.R;
		RGB.G=ShadeRGB.G;
		RGB.B=ShadeRGB.B;

		Exp.Write(&RGB,sizeof(sRGBCol));

		for (int Y=0; Y<MapHeight; Y++)
		{
			for (int X=0; X<MapWidth; X++)
			{
				sRGBElem	&ThisElem=Map[X][Y];

				RGB.R=ThisElem.R;
				RGB.G=ThisElem.G;
				RGB.B=ThisElem.B;

				Exp.Write(&RGB,sizeof(sRGBCol));
			}
		}
}