#include "cWindow.hpp"


#include "../FXEngine/cGFX.hpp"
#include "../UtilityEngine/cUtility.hpp"

#include "cGUI.hpp"
#include "cLabel.hpp"
#include "cLayout.hpp"

using GUIEngine::cWindow;

/*static*/ const UtilityEngine::cString cWindow::sNAME = "windows";
/*static*/ GUIHelpers::Position cWindow::sPOSITION = { 0, 0 };
/*static*/ GUIHelpers::Size cWindow::sSIZE = { 0, 0 };
/*static*/ GUIHelpers::Padding cWindow::sPADDING = { 5, 5, 5, 5 };
/*static*/ GUIHelpers::RGBA cWindow::sCOLOUR = { 100, 100, 100, 255 };

/*static*/ GUIHelpers::eAlign cWindow::sALIGN = GUIHelpers::eAlign::CENTER;
/*static*/ GUIHelpers::eLayout cWindow::sLAYOUT = GUIHelpers::eLayout::FILL_PARENT;
/*static*/ GUIHelpers::eOrientation cWindow::sORIENTATION = GUIHelpers::eOrientation::NONE;

cWindow::cWindow( cWindow* parent /*= nullptr*/, const signed int id /*= -1*/, const GUIHelpers::eOrientation& orientation /*= sORIENTATION*/, const GUIHelpers::eAlign& align /*= sALIGN*/,
	const GUIHelpers::eLayout& layout /*= sLAYOUT*/, const GUIHelpers::Position& pos /*= sPOSITION*/, const GUIHelpers::Size& size /*= sSIZE*/,
	const GUIHelpers::Padding& padding /*= sPADDING*/, const cString& name /*= sNAME*/ )
	: cObject(id)
{
	Create(parent, id, orientation, align, layout, pos, size, padding, name);
}

/*virtual*/ cWindow::~cWindow()
{
	for (unsigned int i = 0; i < m_children.size(); ++i) {
		delete m_children[i];
		m_children[i] = nullptr;
	}
	m_children.clear();
}

/*virtual*/ void cWindow::Create( cWindow* parent /*= nullptr*/, const signed int id /*= -1*/, const GUIHelpers::eOrientation& orientation /*= sORIENTATION*/, const GUIHelpers::eAlign& align /*= sALIGN*/,
	const GUIHelpers::eLayout& layout /*= sLAYOUT*/, const GUIHelpers::Position& pos /*= sPOSITION*/, const GUIHelpers::Size& size /*= sSIZE*/,
	const GUIHelpers::Padding& padding /*= sPADDING*/, const cString& name /*= sNAME*/ )
{
	mp_parent = parent;
	cObject::setID(id);
	setOrientation(orientation);
	setAlign(align);
	setLayout(layout);
	setPosition(pos);
	setSize(size);
	setPadding(padding);
	m_name = name;
}

/*virtual*/ void cWindow::Display() {

	std::vector<cWindow*>::iterator it;

	for (it = m_children.begin(); it < m_children.end(); it++) {
		(*it)->Display();
	}

	DebugDisplay();	
}

///Functions
const bool cWindow::Inside( const MathEngine::iVector2& location ) const
{
	bool rtn = false;

	if ((location.x >= m_pos.x) && (location.y >= m_pos.y)) {
		if ((location.x <= (m_pos.x + m_size.x)) && (location.y <= (m_pos.y + m_size.y)))
			rtn = true;
	}
	return rtn;
}

void cWindow::AddChild( cWindow* obj )
{
	m_children.push_back(obj);
}

const bool cWindow::RemoveChild( cWindow* obj )
{
	bool rtn = false;
	std::vector<cWindow*>::iterator it;

	for (it = m_children.begin(); it < m_children.end(); it++) {
		if (obj == (*it)) {
			m_children.erase(it);
			rtn = true;
			//break out of the for loop
			break;
		}
	}
	return rtn;
}

///Sets
/*static*/ void cWindow::PositionDefault( const GUIHelpers::Position& pos )
{
	if (pos.x >= 0)
		sPOSITION = pos;
}

/*static*/ void cWindow::SizeDefault( const GUIHelpers::Size& size )
{
	if (size.x >= 0)
		sSIZE = size;
}

/*static*/ void cWindow::PaddingDefault( const GUIHelpers::Padding& pad )
{
	if (pad.x >= 0)
		sPADDING = pad;
}

/*static*/ void cWindow::AlignDefault( const GUIHelpers::eAlign& align )
{
	if (align != GUIHelpers::eAlign::DEFAULT_ALIGN)
		sALIGN = align;
}

/*static*/ void cWindow::LayoutDefault( const GUIHelpers::eLayout& layout )
{
	if (layout != GUIHelpers::eLayout::DEFAULT_LAYOUT)
		sLAYOUT = layout;
}

/*static*/ void cWindow::OrientationDefault( const GUIHelpers::eOrientation& orientation )
{
	if (orientation != GUIHelpers::eOrientation::DEFAULT_ORIENTATION)
		sORIENTATION = orientation;
}

void cWindow::setOrientation( const GUIHelpers::eOrientation& orientation /*= sORIENTATION*/ )
{
	if (orientation == GUIHelpers::eOrientation::DEFAULT_ORIENTATION)
		m_orientation = cWindow::sORIENTATION;
	else
		m_orientation = orientation;
}

void cWindow::setAlign( const GUIHelpers::eAlign& align /*= sALIGN*/ )
{
	if (align == GUIHelpers::eAlign::DEFAULT_ALIGN)
		m_align = cWindow::sALIGN;
	else
		m_align = align;
	RebuildPos();
}

void cWindow::setLayout( const GUIHelpers::eLayout& layout /*= sLAYOUT*/ )
{
	if (layout == GUIHelpers::eLayout::DEFAULT_LAYOUT)
		m_layout = cWindow::sLAYOUT;
	else
		m_layout = layout;
}

void cWindow::setPosition( const GUIHelpers::Position& pos /*= POSITION*/ )
{
	if (pos.x < 0)
		m_pos = cWindow::sPOSITION;
	else
		m_pos = pos;
}

void cWindow::setSize( const GUIHelpers::Size& size /*= sSIZE*/ )
{
	if (size.x < 0)
		m_size = cWindow::sSIZE;
	else
		m_size = size;
}

void cWindow::setPadding( const GUIHelpers::Padding& padding /*= PADDING*/ )
{
	if (padding.x < 0)
		m_padding = cWindow::sPADDING;
	else
		m_padding = padding;
}

void cWindow::setContentSize( const GUIHelpers::Size& content )
{
	m_content = content;
}

void cWindow::setName( const cString& name /*= NAME*/ )
{
	m_name = name;
}

void cWindow::setParent( cWindow* parent )
{
	mp_parent = parent;
}

///Gets
const GUIHelpers::Position cWindow::getCenter( const bool pad /*= true*/ ) const
{
	GUIHelpers::Position rtn = { 0, 0 };
	rtn.x = (m_size.x / 2) + m_pos.x;
	rtn.y = (m_size.y / 2) + m_pos.y;
	if (pad == true) {
		rtn.x += (m_padding.x - m_padding.w);
		rtn.y += (m_padding.y - m_padding.z);
	}
	return rtn;
}

const GUIHelpers::eOrientation& cWindow::getOrientation() const
{
	return m_orientation;
}

const GUIHelpers::eAlign& cWindow::getAlign() const
{
	return m_align;
}

const GUIHelpers::eLayout& cWindow::getLayout() const
{
	if (m_layout == GUIHelpers::eLayout::MATCH_PARENT) {
		if (mp_parent != nullptr)
			return mp_parent->getLayout();
	}
	return m_layout;
}

const GUIHelpers::Position cWindow::getPosition( const bool pad /*= true*/ ) const
{
	GUIHelpers::Position rtn = m_pos;

	if (pad == false) {
		rtn.x -= m_padding.x;
		rtn.y -= m_padding.y;
	}
	
	return rtn;
}

const GUIHelpers::Size cWindow::getSize( const bool pad /*= true*/ ) const
{
	GUIHelpers::Size rtn = m_size;

	if (pad == true) {
		rtn.x += m_padding.x + m_padding.w;
		rtn.y += m_padding.y + m_padding.z;
	}

	return rtn;
}

const GUIHelpers::Padding& cWindow::getPadding() const
{
	return m_padding;
}

const GUIHelpers::Size& cWindow::getContentSize() const
{
	return m_content;
}

const cString& cWindow::getName() const
{
	return m_name;
}

const GUIHelpers::Size cWindow::getChildrenSize( const cWindow* ignore /*= nullptr*/ )
{
	GUIHelpers::Size rtn = { 0, 0 };

	std::vector<cWindow*>::iterator it;

	for (it = m_children.begin(); it < m_children.end(); it++) {
		if ((ignore == nullptr) || ((*it) != ignore))
			rtn += (*it)->getSize();
	}

	return rtn;
}

cWindow* cWindow::getParent()
{
	return mp_parent;
}

/*virtual*/ const GUIHelpers::eType cWindow::getType() const
{
	return GUIHelpers::eType::CWINDOW;
}

///Protected

/*virtual*/ void cWindow::Resize()
{
	RebuildLayout(this->getLayout());
	RePos();
}

/*virtual*/ void cWindow::RebuildLayout( const GUIHelpers::eLayout& layout )
{
	GUIHelpers::Size mySize = { 0, 0 };
	std::vector<cWindow*>::iterator it;

	switch (layout)
	{
	case GUIHelpers::eLayout::MATCH_PARENT:
	case GUIHelpers::eLayout::FILL_PARENT:
		if (mp_parent != nullptr) {
			GUIHelpers::Position myPos = mp_parent->getPosition(false);
			myPos.x += m_padding.x;
			myPos.y += m_padding.y;
			mySize = mp_parent->getSize(false);
			
			if (mp_parent->getType() == GUIHelpers::eType::CLAYOUT) {
				if (mp_parent->getChildren().size() > 1)
					mySize -= mp_parent->getChildrenSize(this);
				if (mp_parent->getOrientation() == GUIHelpers::eOrientation::VERTICAL)
					mySize.x = m_size.x;
				if (mp_parent->getOrientation() == GUIHelpers::eOrientation::HORIZONTAL)
					mySize.y = m_size.y;
			}

			mySize.x -= m_padding.x + m_padding.w;
			mySize.y -= m_padding.y + m_padding.z;
			m_pos = myPos;
			m_size = mySize;
		}
		for (it = m_children.begin(); it < m_children.end(); it++) {
			(*it)->Resize();
		}
		break;
	case GUIHelpers::eLayout::WRAP_CONTENT:
		GUIHelpers::Size size = { 0, 0 };
		if (m_children.size() > 0) {
			for (it = m_children.begin(); it < m_children.end(); it++) {
				(*it)->Resize();
				size = (*it)->getSize();
				AddSize(size.x, size.y, mySize);
			}
		}
		else {
			if (mp_parent != nullptr)
				m_pos = mp_parent->getCenter();
		}
		m_size = mySize;
		break;
	}
}

/*virtual*/ void cWindow::RePos()
{
	std::vector<cWindow*>::iterator it;

	for (it = m_children.begin(); it < m_children.end(); it++) {
		(*it)->RePos();
	}

	if (this->getType() == GUIHelpers::eType::CLAYOUT) {
		GUIHelpers::Position prtCenter = getCenter();
		GUIHelpers::Position ourSet = prtCenter;
		GUIHelpers::Size totalSize = { 0, 0 };
		GUIHelpers::Position totalCenter = { 0, 0 };
		
		for (it = m_children.begin(); it < m_children.end(); it++) {
			totalSize += (*it)->getSize();
		}

		totalCenter.x = totalSize.x / 2;
		totalCenter.y = totalSize.y / 2;

		ourSet.x -= totalCenter.x;
		ourSet.y -= totalCenter.y;		

		GUIHelpers::Position posSet = ourSet;
		GUIHelpers::Position posNext = posSet;
		for (it = m_children.begin(); it < m_children.end(); it++) {
			posSet = posNext;
			GUIHelpers::Padding pad = (*it)->getPadding();
			GUIHelpers::Size size = (*it)->getSize(false);
			if (m_orientation == GUIHelpers::eOrientation::HORIZONTAL) {
				posSet.x += pad.x;
				posSet.y = (pad.y - pad.z - (size.y / 2)) + prtCenter.y;

				posNext = posSet;

				posNext.x += size.x + pad.w;
				//posNext.y = ourSet.y;
			}
			else {
				posSet.x = (pad.x - pad.w - (size.x / 2)) + prtCenter.x;
				posSet.y += pad.y;

				posNext = posSet;

				//posNext.x = ourSet.x;
				posNext.y += size.y + pad.z;
			}

			(*it)->setPosition(posSet);
			(*it)->RebuildPos();
		}		
	}
}

/*virtual*/ void cWindow::RebuildPos()
{
	if ((mp_parent != nullptr) && (mp_parent->getOrientation() != GUIHelpers::eOrientation::NONE)) {
		GUIHelpers::Position prtPos = mp_parent->getPosition();
		GUIHelpers::Size prtSize = mp_parent->getSize();

		GUIHelpers::Position pos = this->getPosition();
		GUIHelpers::Size size = this->getSize();

		GUIHelpers::Padding pad = this->getPadding();

		if (mp_parent->getOrientation() == GUIHelpers::eOrientation::HORIZONTAL) {
			switch (this->getAlign()) {
			default:
			case GUIHelpers::eAlign::CENTER:
				pos.y = mp_parent->getCenter().y - (this->getSize(false).y / 2);
				break;
			case GUIHelpers::eAlign::BOTTOM:
				pos.y = prtSize.y - size.y;
				break;
			case GUIHelpers::eAlign::TOP:
				pos.y = prtPos.y + pad.y;
				break;
			}
		}
		if (mp_parent->getOrientation() == GUIHelpers::eOrientation::VERTICAL) {
			switch (this->getAlign()) {
			default:
			case GUIHelpers::eAlign::CENTER:
				pos.x = mp_parent->getCenter().x - (this->getSize(false).x / 2);
				break;
			case GUIHelpers::eAlign::RIGHT:
				pos.x = prtSize.x - size.x;
				break;
			case GUIHelpers::eAlign::LEFT:
				pos.x = prtPos.x + pad.x;
				break;
			}
		}
		this->setPosition(pos);
	}
}

/*virtual*/ const GUIHelpers::RGBA cWindow::getDebugColour() const
{
	return GUIHelpers::WHITE;
}

std::vector<cWindow*>& cWindow::getChildren()
{
	return m_children;
}

//private
void cWindow::AddSize( int x, int y, GUIHelpers::Size& mySize ) const
{
	int& addFrom = x;
	int& largestFrom = y;

	int& addTo = mySize.x;
	int& largestTo = mySize.y;

	if (m_orientation == GUIHelpers::eOrientation::HORIZONTAL) {
		addFrom = y;
		largestFrom = x;

		addTo = mySize.y;
		largestTo = mySize.x;
	}

	/*We do the real work here.*/
	if (largestTo < largestFrom)
		largestTo = largestFrom;

	addTo += addFrom;
}

void cWindow::DebugDisplay() const
{
	// Debug Level 0 display nothing
	// Debug Level 1 display a coloured rectangle and center pixel for each GUI element.
	// Debug Level 2 display the type of GUI element
	unsigned long int debugLvl = GUIEngine::cGUI::Inst().getDebugLevel();
	if (debugLvl >= 1) {
		SDL_Rect rect;
		rect.x = m_pos.x;
		rect.y = m_pos.y;
		rect.w = m_pos.x + m_size.x;
		rect.h = m_pos.y + m_size.y;

		GUIHelpers::Position strPos = getCenter();

		GUIHelpers::RGBA colour = getDebugColour();
		FXEngine::cGFX::Inst().Pixel(strPos.x, strPos.y, colour);
		
		cString str = "";
		str.format("X: %d Y: %d", strPos.x, strPos.y);
		FXEngine::cGFX::Inst().StringDefault(str, { strPos.x + 2, strPos.y + 2, 0, 0 }, colour);

		if (debugLvl >= 2) {
			SDL_Rect typePos = { 2, 2, 0, 0 };
			cString type = "";
			switch (getType()) {
			case GUIHelpers::eType::CPANEL:
				type = "Panel";
				break;
			case GUIHelpers::eType::CLAYOUT:
				type = "Layout";
				break;
			case GUIHelpers::eType::CLABEL:
				typePos = { -10, -10, 0, 0 };
				type = "Label";
				break;
			default:
				type = "";
			}
			
			typePos.x += m_pos.x;
			typePos.y += m_pos.y;
			
			FXEngine::cGFX::Inst().StringDefault(type, typePos, colour);

// 			if (debugLvl >= 3) {
// 				GUIHelpers::Position pos = getPosPPad();
// 				//GUIHelpers::Size size = getSizePPad();
// 				SDL_Rect box{ 0, 0, 0, 0 };
// 				box.x += pos.x;
// 				box.y += pos.y;
// 				box.h += pos.y + m_size.y + m_padding.y + m_padding.z;
// 				box.w += pos.x + m_padding.x;
// 
// 				FXEngine::cGFX::Inst().Box(box, colour);
// 			}
		}
		
		FXEngine::cGFX::Inst().Rectangle(rect, colour);
	}
}