// ViewWindowInfo.cpp: implementation of the ViewWindowInfo class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "MW4GameEd2.h"
#include "ViewWindowInfo.h"


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

ViewWindowInfo::ViewWindowInfo()
{
ClientArea=CSize(100,100);
BitmapArea=CSize(256,256);

ZoomFact.x=1.0f;
ZoomFact.y=0.0f;
ZoomFact.z=1.0f;

Pan.x=50.0f;
Pan.y=0.0f;
Pan.z=50.0f;

MapOrg.x=-50.0f;
MapOrg.y=0.0f;
MapOrg.z=-50.0f;

MapSize.x=100.0f;
MapSize.y=0.0f;
MapSize.z=100.0f;

CalcConstants();
}

ViewWindowInfo::~ViewWindowInfo()
{

}

void ViewWindowInfo::CalcConstants()
{
MapOrg.x=-MapSize.x*0.5f;
MapOrg.z=-MapSize.z*0.5f;
MapRatio=MapSize.z/MapSize.x;
ViewSizeX=MapSize.x/ZoomFact.x;
ViewSizeY=MapSize.z/ZoomFact.z;

ClientRatio=((float)ClientArea.cx/(float)ClientArea.cy);

if(ClientRatio>1)
	ViewSizeY/=ClientRatio;
else
	ViewSizeX*=ClientRatio;

HalfViewSizeX=ViewSizeX*0.5f;
HalfViewSizeY=ViewSizeY*0.5f;
SourcePixs_Per_MeterX=BitmapArea.cx/MapSize.x;
SourcePixs_Per_MeterY=BitmapArea.cy/MapSize.z;
DestPixs_Per_MeterX=ClientArea.cx/ViewSizeX;
DestPixs_Per_MeterY=ClientArea.cy/ViewSizeY;
MeterX_Per_DestPixs=1/DestPixs_Per_MeterX;
MeterY_Per_DestPixs=1/DestPixs_Per_MeterY;
DestPixs_Per_SourcePixsX=DestPixs_Per_MeterX/SourcePixs_Per_MeterX;
DestPixs_Per_SourcePixsY=DestPixs_Per_MeterY/SourcePixs_Per_MeterY;
}

void ViewWindowInfo::SetClientArea(CSize &rct)	
{
ClientArea=rct;
if(ClientArea.cx==0) ClientArea.cx=1;
if(ClientArea.cy==0) ClientArea.cy=1;
CorrectViewBounds();
}

void ViewWindowInfo::SetBitmapArea(CSize &rct)	
{
BitmapArea=rct;
CorrectViewBounds();
}

void ViewWindowInfo::SetZoom(float zf)	
{
ZoomFact.z=ZoomFact.x=zf;
CorrectViewBounds();
}

void ViewWindowInfo::SetPan(Point3D &pnt)
{
Pan=pnt;
CorrectViewBounds();
}

void ViewWindowInfo::OffsetPan(Point3D &pnt)
{
Pan+=pnt;
CorrectViewBounds();
}

void ViewWindowInfo::MultiplyZoom(float zf)
{
ZoomFact.x*=zf;
ZoomFact.z*=zf;
CorrectViewBounds();
}

void ViewWindowInfo::SetViewLimits(Point3D min, Point3D max)
{
	if(min.x>max.x)
	{
	Scalar tmp;
	tmp=min.x; min.x=max.x; max.x=tmp;
	}

	if(min.z>max.z)
	{
	Scalar tmp;
	tmp=min.z; min.z=max.z; max.z=tmp;
	}

	Verify((min.x<max.x && min.z<max.z));
	
	Scalar xsize,zsize;
	xsize=max.x-min.x;
	zsize=max.z-min.z;
	
	Pan.x=min.x+xsize*0.5f;
	Pan.z=min.z+zsize*0.5f;
	
	ZoomFact.x=MapSize.x/xsize;
	ZoomFact.z=MapSize.z/zsize;
	
	// Do this to force a square to avoid draw problems
	ZoomFact.z=ZoomFact.x=ZoomFact.x<ZoomFact.z?ZoomFact.x:ZoomFact.z;

	CorrectViewBounds();
}

void ViewWindowInfo::SetMapSize(Point3D msze)
{
MapSize=msze;
CorrectViewBounds();
}

CPoint ViewWindowInfo::DataToScreen(const Point3D &pnt)
{
CPoint retcpnt;
	retcpnt.x=(ClientArea.cx-(int)((pnt.x-Pan.x+HalfViewSizeX)*DestPixs_Per_MeterX));
	retcpnt.y=(ClientArea.cy-(int)((pnt.z-Pan.z+HalfViewSizeY)*DestPixs_Per_MeterY));
	return retcpnt;
}

Point3D ViewWindowInfo::ScreenToData(CPoint &pnt)
{
Point3D retp3d;
	retp3d.x=ViewSizeX*(0.5f-((Scalar)pnt.x/ClientArea.cx))+Pan.x;
	retp3d.y=0.0f;
	retp3d.z=ViewSizeY*(0.5f-((Scalar)pnt.y/ClientArea.cy))+Pan.z;
return retp3d;
}

void ViewWindowInfo::GetViewLimits(Point3D *min, Point3D *max)
{
	
	min->y=max->y=0.0f;
	min->x=Pan.x-HalfViewSizeX;
	max->x=Pan.x+HalfViewSizeX;
	min->z=Pan.z-HalfViewSizeY;
	max->z=Pan.z+HalfViewSizeY;
	
}


bool ViewWindowInfo::GetBltRects(CRect *drct,CRect *srct)
{
		
		float src_top,src_right,src_left,src_bottom;
		
		drct->left=0;
		drct->top=0;
		drct->right=ClientArea.cx;
		drct->bottom=ClientArea.cy;

		Point3D flip_pan;
		flip_pan.y=0.0f;
		flip_pan.x=MapSize.x-(Pan.x-MapOrg.x);
		flip_pan.z=MapSize.z-(Pan.z-MapOrg.z);

		//Calulate percise Source window
		src_left=(flip_pan.x-HalfViewSizeX)*SourcePixs_Per_MeterX;
		src_right=(flip_pan.x+HalfViewSizeX)*SourcePixs_Per_MeterX;
		src_top=(flip_pan.z-HalfViewSizeY)*SourcePixs_Per_MeterY;
		src_bottom=(flip_pan.z+HalfViewSizeY)*SourcePixs_Per_MeterY;
		
		//Truncate Source Window with overlap
		srct->left=(int)floor(src_left);
		srct->right=(int)ceil(src_right);
		srct->top=(int)floor(src_top);
		srct->bottom=(int)ceil(src_bottom);
		

		//Adjust Dest Window for overlap
		if(srct->Width()>1)
		{
		drct->left-=(int)((src_left-floor(src_left))*DestPixs_Per_SourcePixsX);
		drct->right+=(int)((ceil(src_right)-src_right)*DestPixs_Per_SourcePixsX);
		}
		
		if(srct->Height()>1)
		{
		drct->top-=(int)((src_top-floor(src_top))*DestPixs_Per_SourcePixsY);
		drct->bottom+=(int)((ceil(src_bottom)-src_bottom)*DestPixs_Per_SourcePixsY);
		}

	return (abs(srct->right-srct->left)<=1 || abs(srct->bottom-srct->top)<=1 );
}

void ViewWindowInfo::SetPan(CPoint &pnt)
{
SetPan(ScreenToData(pnt));
}

void ViewWindowInfo::OffsetPan(CSize &pnt)
{
//Reverser for no 180 flip
//Pan.x+=(float)((pnt.cx)*MapSize.x)/(ClientArea.cx*ZoomFact.x);
//Pan.z+=(float)((pnt.cy)*MapSize.z)/(ClientArea.cy*ZoomFact.z);
Pan.x-=(float)((pnt.cx)*MapSize.x)/(ClientArea.cx*ZoomFact.x);
Pan.z-=(float)((pnt.cy)*MapSize.z)/(ClientArea.cy*ZoomFact.z);
CorrectViewBounds();
}


void ViewWindowInfo::CorrectViewBounds()
{
	if(ZoomFact.x<1.0f) ZoomFact.x=1.0f;
	if(ZoomFact.z<1.0f) ZoomFact.z=1.0f;

	CalcConstants();
	
	float src_top,src_right,src_left,src_bottom;
	bool change;

	//Calulate percise Source window
	src_left=((Pan.x-MapOrg.x)-HalfViewSizeX)*SourcePixs_Per_MeterX;
	src_right=((Pan.x-MapOrg.x)+HalfViewSizeX)*SourcePixs_Per_MeterX;
	src_top=((Pan.z-MapOrg.z)-HalfViewSizeY)*SourcePixs_Per_MeterY;
	src_bottom=((Pan.z-MapOrg.z)+HalfViewSizeY)*SourcePixs_Per_MeterY;
	
	if(src_left<0)
	{
		Pan.x=HalfViewSizeX+MapOrg.x;
		change=true;
	}
	
	if(src_top<0)
	{
		Pan.z=HalfViewSizeY+MapOrg.z;
		change=true;
	}

	if(src_right>BitmapArea.cx)
	{
		Pan.x=((BitmapArea.cx)/SourcePixs_Per_MeterX)-HalfViewSizeX+MapOrg.x;
		change=true;
	}
	
	if(src_bottom>BitmapArea.cy)
	{
		Pan.z=((BitmapArea.cy)/SourcePixs_Per_MeterY)-HalfViewSizeY+MapOrg.z;
		change=true;
	}

if(change) CalcConstants();
}
