// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010  Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.


#include "std_afx.h"
#include "object_viewer.h"
#include "direction_edit.h"
#include "direction_attr.h"
#include "popup_notify.h"

#include "nel/misc/vector.h"

/////////////////////////////////////////////////////////////////////////////
// CDirectionEdit dialog


CDirectionEdit::CDirectionEdit(IPSWrapper<NLMISC::CVector> *wrapper) 
: _Wrapper(wrapper), _MouseState(CDirectionEdit::Wait)

{
	nlassert(wrapper);
	//{{AFX_DATA_INIT(CDirectionEdit)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
}


void CDirectionEdit::init(IPopupNotify *pn, CWnd *pParent)
{
	Create(IDD_DIRECTION_EDIT, pParent);
	_Parent = pn;
	ShowWindow(SW_SHOW);
}


void CDirectionEdit::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CDirectionEdit)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CDirectionEdit, CDialog)
	//{{AFX_MSG_MAP(CDirectionEdit)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_LBUTTONUP()
	ON_WM_CLOSE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


// private : draw a basis with the given letters
static void DrawBasis(CDC &dc, sint x, sint y, sint size, const char *xStr, const char *yStr)
{
	dc.FillSolidRect(x, y,  size, size, 0xffffff);

	CPen p;
	p.CreatePen(PS_SOLID, 1, (COLORREF) 0);

	CPen *oldPen = dc.SelectObject(&p);

	dc.MoveTo(x + 5, y + size / 2);
	dc.LineTo(x + size - 5, y + size / 2);
	dc.MoveTo(x + size / 2, y + 5);
	dc.LineTo(x + size / 2, y + size - 5);

	dc.TextOut(x + size - 20, y + size / 2 - 18, CString(xStr));
	dc.TextOut(x + 5 + size / 2,  y + 5, CString(yStr));

	dc.SelectObject(oldPen);
}


/// private : draw a vector in a basis  
static void DrawVector(CDC &dc, float vx, float vy, sint x, sint y, sint size)
{
	CPen p;
	p.CreatePen(PS_SOLID, 1, (COLORREF) 0xff);
	CPen *oldPen = dc.SelectObject(&p);

	dc.MoveTo(x + size / 2, y + size / 2);
	dc.LineTo(int(x + size / 2 + vx * 0.9f * size / 2), int(y + size / 2 - vy * 0.9f * size / 2));	

	dc.SelectObject(oldPen);
	
}

// size in pixel of the basis that is drawn in the dialog
const uint32 BasisSize = 120;

// the gap between 2 basis on the screen
const uint32 BasisGap = 20;

// the distance to the upper left corner for the basis drawing
const uint CornerDist = 10;




/// private get back a screen coord (pox, py) to a part of a vector. The basis of click is (x, y) - (x + size, y + size)
static void ScreenToVect(float &vx, float &vy, sint px, sint py, sint x, sint y, sint size)
{
	vx = (px - (x + size / 2)) / 0.9f;
	vy = ((y + size / 2) - py) / 0.9f;
}

void CDirectionEdit::OnPaint() 
{
	CPaintDC dc(this); 

	// get the current vector;
	NLMISC::CVector v = _Wrapper->get();


	// draw a white square, and draw the vector in it
	
	DrawBasis(dc, CornerDist, CornerDist, BasisSize, "X", "Z");
	DrawVector(dc, v.x, v.z, CornerDist, CornerDist, BasisSize);

	DrawBasis(dc, CornerDist, CornerDist + BasisGap + BasisSize, BasisSize, "Y", "Z");	
	DrawVector(dc, v.y, v.z, CornerDist, CornerDist + BasisGap + BasisSize, BasisSize);
	
}

void CDirectionEdit::selectNewVect(const CPoint &point)
{
	const float epsilon = 10E-3f;
	NLMISC::CVector v = _Wrapper->get();
	if (point.x > CornerDist && point.y > CornerDist && point.x < (CornerDist + BasisSize) && point.y < (CornerDist + BasisSize))
	{		
		ScreenToVect(v.x, v.z, point.x, point.y, CornerDist, CornerDist, BasisSize);	
		float d = v.x * v.x + v.z * v.z;
		float f; 
		if (fabsf(d > epsilon))
		{
			f = sqrtf((1.f - v.y * v.y) / d);
		}
		else
		{
			f = 1;
		}

		v.x *= f;
		v.z *= f;
	}

	if (point.x > CornerDist && point.y > (BasisGap + BasisSize + CornerDist) && point.x < (CornerDist + BasisSize) && point.y < (CornerDist + BasisGap + 2 * BasisSize))
	{		
		ScreenToVect(v.y, v.z, point.x, point.y, CornerDist, CornerDist + BasisGap + BasisSize, BasisSize);	
		float d = v.y * v.y + v.z * v.z;
		float f; 
		if (fabsf(d > epsilon))
		{
			f = sqrtf((1.f - v.x * v.x) / d);
		}
		else
		{
			f = 1;
		}

		v.y *= f;
		v.z *= f;
	}

	v.normalize();
	_Wrapper->setAndUpdateModifiedFlag(v);

	Invalidate();

}

void CDirectionEdit::OnLButtonDown(UINT nFlags, CPoint point) 
{
	selectNewVect(point);
	_MouseState = Drag;
	CDialog::OnLButtonDown(nFlags, point);
}

void CDirectionEdit::OnMouseMove(UINT nFlags, CPoint point) 
{
	if (_MouseState == Drag)
	{
			selectNewVect(point);

	}
	CDialog::OnMouseMove(nFlags, point);
}

void CDirectionEdit::OnLButtonUp(UINT nFlags, CPoint point) 
{
	_MouseState = Wait;	
	CDialog::OnLButtonUp(nFlags, point);
}


void CDirectionEdit::OnClose() 
{	
	nlassert(_Parent);	
	CDialog::OnClose();
	_Parent->childPopupClosed(this);
}