#include "match3/match3field.h"

#include "main_code.h"
#include <algorithm>


void TChipTemplateParams::Serialize(boost::property_tree::ptree& propertyTree)
{
	
	std::string selectedShaderName = propertyTree.get<std::string>("SelectedShaderName");
	std::string selectedTexName = propertyTree.get<std::string>("SelectedTexName");
	std::string selectedNormTexName = propertyTree.get<std::string>("SelectedNormTexName");
	std::string finishingTexName = propertyTree.get<std::string>("FinishingTexName");

	SelectedRenderParams.ShaderName = selectedShaderName;
	SelectedRenderParams.TexName = selectedTexName;
	SelectedRenderParams.NormTexName = selectedNormTexName;
	SelectedRenderParams.Transparency = 1.f;

	FinishingRenderParams.ShaderName = "";
	FinishingRenderParams.TexName = finishingTexName;
	FinishingRenderParams.NormTexName = "";
	FinishingRenderParams.Transparency = 1.f;

	std::string selectedAnimFileName = propertyTree.get<std::string>("SelectedAnimFileName");
	std::string finishingAnimFileName = propertyTree.get<std::string>("FinishingAnimFileName");

	boost::shared_ptr<boost::property_tree::ptree> px = FileToPropertyTree(selectedAnimFileName);
	SelectedTemplateAnimObject.Serialize(*px);
	px = FileToPropertyTree(finishingAnimFileName);
	FinishingTemplateAnimObject.Serialize(*px);
	
}


void TMatch3FieldParams::Serialize(boost::property_tree::ptree& propertyTree)
{
	
	TChipTemplateParams chipTemplateParams;

	FieldWidth = propertyTree.get<cardinal>("Match3Params.FieldWidth");
	FieldHeight = propertyTree.get<cardinal>("Match3Params.FieldHeight");
	CellWidth = propertyTree.get<float>("Match3Params.CellWidth");
	CellHeight = propertyTree.get<float>("Match3Params.CellHeight");
	ChipLowestSpeed = propertyTree.get<float>("Match3Params.ChipLowestSpeed");
	ChipAcceleration = propertyTree.get<float>("Match3Params.ChipAcceleration");
	ChipPositionEpsilon = propertyTree.get<float>("Match3Params.ChipPositionEpsilon");
	ChipVelocityEpsilon = propertyTree.get<float>("Match3Params.ChipVelocityEpsilon");
	ChipK = propertyTree.get<float>("Match3Params.ChipK");
	ChipMatchTime = propertyTree.get<cardinal>("Match3Params.ChipMatchTime");
	ChipTypeCount = propertyTree.get<cardinal>("Match3Params.ChipTypeCount");

	BOOST_FOREACH(boost::property_tree::ptree::value_type &v, propertyTree.get_child("Match3Params.ChipList"))
	{
		boost::property_tree::ptree& p = v.second.get_child("");
		chipTemplateParams.Serialize(p);
		ChipTemplateParamsArr.push_back(chipTemplateParams);
	}

	
}

int TChip::StaticAnimationIndex = 0;

TChip::TChip(int chipType, TRenderPairList::iterator renderPair, cardinal vertexListShift, TChipState chipState)
	: ChipType(chipType)
	, RenderPair(renderPair)
	, VertexListShift(vertexListShift)
	, ChipState(chipState)
	, Velocity(0)
{
	AnimName = "test_anim"+tostr(StaticAnimationIndex++);
}

TChip::~TChip()
{
}

vec2 TChip::GetLeftBottomPos()
{
	std::vector<vec3>::iterator i = RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].begin() + VertexListShift;

	return vec2(i->v[0], i->v[1]);
}

void TChip::SetLeftBottomPos(vec2 newPos)
{
	vec2 shift = newPos - GetLeftBottomPos();

	MoveLeftBottomPos(shift);
}

void TChip::MoveLeftBottomPos(vec2 shift)
{
	for (int i=0; i<6; i++)
	{
		*(RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].begin() + VertexListShift + i) += vec3(shift, 0);
	}
}




TMatch3Logic::TMatch3Logic()
	//: TemplateAnimObject(TRenderPairList::iterator(), 0, 1)
{
	//TemplateAnimObject.Serialize(FileToPropertyTree("chip1_xml.xml"));
}


TMatch3Logic::~TMatch3Logic()
{

	//Match3FieldParams.ChipTemplateParamsArr.clear();

}

void TMatch3Logic::FillRandomChipMatrix(std::vector<TRenderPairList::iterator> renderPairIteratorVector, vec2 leftBottomPos)
{

	RenderPairIteratorVector = renderPairIteratorVector;
	
	LeftBottomPosField = leftBottomPos;

	ChipTypeCount = Match3FieldParams.ChipTypeCount;
	
	ChipMatrix.resize(Match3FieldParams.FieldWidth);

	for (cardinal i = 0; i < Match3FieldParams.FieldWidth; ++i)
	{
		for (cardinal j=0; j < Match3FieldParams.FieldHeight; ++j)
		{
			int chipType = rand() % ChipTypeCount;

			if (i == 0 && j == 6)
			{
				//AddChipToUp(i, chipType, GetExpectedLeftBottomPos(i, j), TChip::CS_LOCKED);
				AddChipToUp(i, chipType, GetExpectedLeftBottomPos(i, j), TChip::CS_STANDBY);
			}
			else
			{
				AddChipToUp(i, chipType, GetExpectedLeftBottomPos(i, j), TChip::CS_STANDBY);
			}
		}
	}

	

	std::vector<ivec2> chipList = GetAvailableMatchingChips();

	while (chipList.size() != 0)
	{
		UnmatchChips(chipList);
		chipList = GetAvailableMatchingChips();
	}

}


vec2 TMatch3Logic::GetExpectedLeftBottomPos(cardinal x, cardinal y)
{
	return LeftBottomPosField + vec2(x*Match3FieldParams.CellWidth, y*Match3FieldParams.CellHeight);
}



void TMatch3Logic::StartAnimateChip(cardinal x, cardinal y)
{
	ResourceManager->HalibutAnimationManager.StartAnimation(ChipMatrix[x][y].AnimName);
}

void TMatch3Logic::StopAnimateChip(cardinal x, cardinal y)
{
	
	ResourceManager->HalibutAnimationManager.StopAnimation(ChipMatrix[x][y].AnimName);
	
}


bool TMatch3Logic::ChipIsLocked(ivec2 chip)
{
	return ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_LOCKED;
}

bool TMatch3Logic::ChipIsFinishing(ivec2 chip)
{
	return ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_FINISHING;
}

bool TMatch3Logic::ChipIsStable(ivec2 chip)
{
	return ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_STANDBY ||
		ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_LOCKED;
}

bool TMatch3Logic::ChipCanBeSelected(ivec2 chip)
{

	if (ChipMatrix[chip.v[0]][chip.v[1]].ChipState != TChip::CS_STANDBY)
	{
		return false;
	}


	if (chip.v[1] == 0)
	{
		return true;
	}

	int i = chip.v[1] - 1;

	while (i >= 0 && ChipIsStable(ivec2(chip.v[0], i)) && !ChipIsLocked(ivec2(chip.v[0], i)))
	{
		i--;
	}

	if (i < 0 || ChipIsLocked(ivec2(chip.v[0], i)))
	{
		return true;
	}

	return false;


}

bool TMatch3Logic::ChipsCanBeSwapped(ivec2 p1, ivec2 p2)
{
	float distance_x = static_cast<float>(p1.v[0] - p2.v[0]);
	float distance_y = static_cast<float>(p1.v[1] - p2.v[1]);

	if ((((fabs(distance_x) == 1) && (fabs(distance_y) == 0))
		|| ((fabs(distance_x) == 0) && (fabs(distance_y) == 1))) &&
		ChipCanBeSelected(p1) &&
		ChipCanBeSelected(p2))
	{

		return true;
	}

	return false;
}

bool TMatch3Logic::ChipCanBeMatchedUp(ivec2 chip)
{
	if (chip.v[1] <= Match3FieldParams.FieldHeight - 3)
	{
		if ((ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0]][chip.v[1] + 1].ChipType)
			&& (ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0]][chip.v[1] + 2].ChipType))
		{

			if (ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0]][chip.v[1] + 1].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0]][chip.v[1] + 2].ChipState == TChip::CS_STANDBY)
			{
				return true;
			}

			
		}
	}

	return false;
}

bool TMatch3Logic::ChipCanBeMatchedDown(ivec2 chip)
{
	if (chip.v[1] >= 2)
	{
		if ((ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0]][chip.v[1] - 1].ChipType)
			&& (ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0]][chip.v[1] - 2].ChipType))
		{
			if (ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0]][chip.v[1] - 1].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0]][chip.v[1] - 2].ChipState == TChip::CS_STANDBY)
			{
				return true;
			}
		}
	}

	return false;
}

bool TMatch3Logic::ChipCanBeMatchedLeft(ivec2 chip)
{
	if (chip.v[0] >= 2)
	{
		if ((ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0] - 1][chip.v[1]].ChipType)
			&& (ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0] - 2][chip.v[1]].ChipType))
		{
			if (ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0] - 1][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0] - 2][chip.v[1]].ChipState == TChip::CS_STANDBY)
			{
				return true;
			}
		}
	}

	return false;
}

bool TMatch3Logic::ChipCanBeMatchedRight(ivec2 chip)
{
	if (chip.v[0] <= Match3FieldParams.FieldWidth - 3)
	{
		if ((ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0] + 1][chip.v[1]].ChipType)
			&& (ChipMatrix[chip.v[0]][chip.v[1]].ChipType == ChipMatrix[chip.v[0] + 2][chip.v[1]].ChipType))
		{
			if (ChipMatrix[chip.v[0]][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0] + 1][chip.v[1]].ChipState == TChip::CS_STANDBY &&
				ChipMatrix[chip.v[0] + 2][chip.v[1]].ChipState == TChip::CS_STANDBY)
			{
				return true;
			}
		}
	}

	return false;
}


void TMatch3Logic::UnmatchChips(std::vector<ivec2> chipList)
{
	std::vector<ivec2>::iterator i;

	for (i = chipList.begin(); i != chipList.end(); ++i)
	{

		ChangeChipType(*i);
	}
}

void TMatch3Logic::ReplaceAnimation(ivec2 p)
{
	

	TChip& chip = ChipMatrix[p.v[0]][p.v[1]];
	
	if (ResourceManager->HalibutAnimationManager.AnimationExists(chip.AnimName))
	{
		ResourceManager->HalibutAnimationManager.ReplaceAnimationObject(chip.AnimName, chip.RenderPair, chip.VertexListShift / 6);
	}
	else
	{
		
		THalibutExternalAnimObject testAnimObject(Match3FieldParams.ChipTemplateParamsArr[chip.ChipType].SelectedTemplateAnimObject);
		
		testAnimObject.ReplaceAnimObject(chip.RenderPair, chip.VertexListShift / 6);
		
		ResourceManager->HalibutAnimationManager.AddAnimationObject(chip.AnimName, testAnimObject);
		
	}

	ResourceManager->HalibutAnimationManager.StopAnimation(chip.AnimName);	
}


void TMatch3Logic::MoveVertexListShiftBack(TRenderPairList::iterator renderPairItr, cardinal moveFrom)
{
	for (cardinal i = 0; i < ChipMatrix.size(); i++)
	{
		for (cardinal j = 0; j < ChipMatrix[i].size(); j++)
		{
			if (ChipMatrix[i][j].RenderPair == renderPairItr)
			{
				if (ChipMatrix[i][j].VertexListShift > moveFrom)
				{
					ChipMatrix[i][j].VertexListShift -= 6;

					ReplaceAnimation(ivec2(i, j));
				}
			}
		}
	}
}

void TMatch3Logic::AddChipToUp(cardinal colNum, int chipType, vec2 spawnPos, TChip::TChipState chipState)
{
	cardinal yPos = ChipMatrix[colNum].size();

	vec2 posFrom = spawnPos;
	vec2 posTo = posFrom + vec2(Match3FieldParams.CellWidth, Match3FieldParams.CellHeight);

	cardinal renderPairIndex;

	if (chipState == TChip::CS_LOCKED)
	{
		renderPairIndex = chipType + Match3FieldParams.ChipTypeCount;
	}
	else
	{
		renderPairIndex = chipType;
	}

	TTriangleList& triangleList = RenderPairIteratorVector[renderPairIndex]->second;

	std::vector<vec3> vertexCoordArr = MakeVertexCoordVec(posFrom, posTo);

	std::vector<vec2> texCoordArr = MakeTexCoordVec();

	triangleList.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].insert(triangleList.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].end(), vertexCoordArr.begin(), vertexCoordArr.end());

	triangleList.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].insert(triangleList.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].end(), texCoordArr.begin(), texCoordArr.end());

	cardinal vertexListShift = RenderPairIteratorVector[renderPairIndex]->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].size()-6;

	ChipMatrix[colNum].push_back(TChip(chipType, RenderPairIteratorVector[renderPairIndex], vertexListShift, chipState));

	ReplaceAnimation(ivec2(colNum, yPos));

	triangleList.NeedRefreshBuffer = true;
}


void TMatch3Logic::InsertEmptyChip(cardinal colNum, cardinal rowNum, int chipType)
{

	vec2 posFrom = GetExpectedLeftBottomPos(colNum, rowNum);
	vec2 posTo = posFrom + vec2(Match3FieldParams.CellWidth, Match3FieldParams.CellHeight);
	
	cardinal renderPairIndex = Match3FieldParams.ChipTypeCount * 2 + chipType;

	TChip::TChipState chipState = TChip::CS_X;

	TTriangleList& triangleList = RenderPairIteratorVector[renderPairIndex]->second;

	std::vector<vec3> vertexCoordArr = MakeVertexCoordVec(posFrom, posTo);

	std::vector<vec2> texCoordArr = MakeTexCoordVec();

	triangleList.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].insert(triangleList.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].end(), vertexCoordArr.begin(), vertexCoordArr.end());

	triangleList.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].insert(triangleList.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].end(), texCoordArr.begin(), texCoordArr.end());

	cardinal vertexListShift = RenderPairIteratorVector[renderPairIndex]->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].size()-6;

	ChipMatrix[colNum].insert(ChipMatrix[colNum].begin() + rowNum, TChip(chipType, RenderPairIteratorVector[renderPairIndex], vertexListShift, chipState));

	ReplaceAnimation(ivec2(colNum, rowNum));


	triangleList.NeedRefreshBuffer = true;

	vec2 pos = GetExpectedLeftBottomPos(colNum, rowNum);

	ChipDeletingVector.push_back(TChipDeletingData(ivec2(colNum, rowNum), pos));
}



void TMatch3Logic::UpdateChipSwapping(cardinal dt)
{
	
	std::vector<TChipSwappingPair>::iterator i = ChipSwappingPairVector.begin();

	std::vector<std::pair<ivec2, ivec2> > chipsToSwapAgain;

	while (i != ChipSwappingPairVector.end())
	{
	
		i->T += dt / static_cast<float>(Match3FieldParams.ChipMatchTime);

		if (i->T >= 1.f)
		{

			ivec2 chip1pos = i->Chip1;
			ivec2 chip2pos = i->Chip2;

			bool isReturning = i->IsReturning;

			TChip& chip1 = ChipMatrix[chip1pos.v[0]][chip1pos.v[1]];
			TChip& chip2 = ChipMatrix[chip2pos.v[0]][chip2pos.v[1]];

		
			TChip c = chip1;
			chip1 = chip2;
			chip2 = c;


			ResetChipPos(chip1pos);
			ResetChipPos(chip2pos);

			chip1.ChipState = TChip::CS_STANDBY;
			chip2.ChipState = TChip::CS_STANDBY;


			i = ChipSwappingPairVector.erase(i);


			//Check if chips do 
			if (!isReturning)
			{
				std::vector<ivec2> matchingChipList = GetAvailableMatchingChips();

				if (matchingChipList.size() == 0)
				{
					//SwapChips(chip1pos, chip2pos);
					chipsToSwapAgain.push_back(std::pair<ivec2, ivec2>(chip1pos, chip2pos));
				}
			}
	
		}
		else
		{
			
			TChip& chip1 = ChipMatrix[i->Chip1.v[0]][i->Chip1.v[1]];
			TChip& chip2 = ChipMatrix[i->Chip2.v[0]][i->Chip2.v[1]];

			vec3 newPosChip1 = vec3(i->Chip1RealPosFrom + (i->Chip2RealPosFrom - i->Chip1RealPosFrom) * i->T, 0);
			vec3 newPosChip2 = vec3(i->Chip2RealPosFrom + (i->Chip1RealPosFrom - i->Chip2RealPosFrom) * i->T, 0);
			
			vec3 shiftChip1 = newPosChip1 - chip1.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][chip1.VertexListShift];
			vec3 shiftChip2 = newPosChip2 - chip2.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][chip2.VertexListShift];

			for (int k = 0; k < 6; k++)
			{

				chip1.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][chip1.VertexListShift + k] += shiftChip1;
				chip2.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][chip2.VertexListShift + k] += shiftChip2;
			}

			chip1.RenderPair->second.NeedRefreshBuffer = true;
			chip2.RenderPair->second.NeedRefreshBuffer = true;
		}


		if (i != ChipSwappingPairVector.end())
		{
			++i;
		}

	}

	std::vector<std::pair<ivec2, ivec2> >::iterator pairVecItr;

	for (pairVecItr = chipsToSwapAgain.begin(); pairVecItr != chipsToSwapAgain.end(); ++pairVecItr)	
	{
		SwapChips(pairVecItr->first, pairVecItr->second, true);
	}
}


void TMatch3Logic::UpdateChipPosition(cardinal dt)
{

	for (cardinal i=0; i<ChipMatrix.size(); i++)
	{
		for (cardinal j=1; j<ChipMatrix[i].size(); j++)
		{
			TChip& chip = ChipMatrix[i][j];

			TChip& chipBelow = ChipMatrix[i][j-1];
			if (chipBelow.ChipState == TChip::CS_FINISHING)
			{
				if (chip.ChipState != TChip::CS_LOCKED && chip.ChipState != TChip::CS_FINISHING && chip.ChipState != TChip::CS_X)
				{
					TChip c = chip;
					chip = chipBelow;
					chipBelow = c;
					chipBelow.ChipState = TChip::CS_FALLING;
				}
			}
		}
	}


	for (cardinal i=0; i<ChipMatrix.size(); i++)
	{
		for (cardinal j=0; j<ChipMatrix[i].size(); j++)
		{
			TChip& chip = ChipMatrix[i][j];
			if (chip.ChipState == TChip::CS_FALLING)
			{

				chip.Velocity += Match3FieldParams.ChipAcceleration * dt;

				if (chip.Velocity < Match3FieldParams.ChipLowestSpeed)
				{
					chip.Velocity = Match3FieldParams.ChipLowestSpeed;
				}

				float yShift = chip.Velocity * dt;

				chip.MoveLeftBottomPos(vec2(0, yShift));

				vec2 expectedPos = GetExpectedLeftBottomPos(i, j);
				vec2 realPos = chip.GetLeftBottomPos();

				if (realPos.v[1] < expectedPos.v[1] && chip.Velocity <= 0 && !(j > 0 && ChipIsFinishing(ivec2(i, j-1))))
				{
					
					chip.Velocity = -chip.Velocity * Match3FieldParams.ChipK;
					chip.SetLeftBottomPos(GetExpectedLeftBottomPos(i, j));
					
					
				}
				
				if (fabs(chip.Velocity) <= Match3FieldParams.ChipVelocityEpsilon)
				{
					
					if (fabs(expectedPos.v[0] - realPos.v[0]) <= Match3FieldParams.ChipPositionEpsilon &&
						fabs(expectedPos.v[1] - realPos.v[1]) <= Match3FieldParams.ChipPositionEpsilon
						)
					{
						chip.ChipState = TChip::CS_STANDBY;
						chip.SetLeftBottomPos(GetExpectedLeftBottomPos(i, j));
						chip.Velocity = 0.f;
					}
				}
				

				chip.RenderPair->second.NeedRefreshBuffer = true;

				for (cardinal k = j + 1; k < ChipMatrix[i].size(); k++)
				{

					TChip& chipAbove = ChipMatrix[i][k];

					vec2 posAbove = chipAbove.GetLeftBottomPos();

					if (chipAbove.GetLeftBottomPos().v[1] - chip.GetLeftBottomPos().v[1] < Match3FieldParams.CellHeight)
					{
						if (!ChipIsLocked(ivec2(i, k)) && !ChipIsFinishing(ivec2(i, k)))
						{
							chipAbove.ChipState = TChip::CS_FALLING;
							chipAbove.SetLeftBottomPos(chip.GetLeftBottomPos() + vec2(0, Match3FieldParams.CellHeight));
							chipAbove.Velocity = chip.Velocity;
							chipAbove.RenderPair->second.NeedRefreshBuffer = true;
						}
					}
				}
			}
		}
	}
}


void TMatch3Logic::RemoveBubbles()
{
	cardinal max_y = ChipMatrix.size()-1;

	for (cardinal i=0; i < ChipMatrix[max_y].size(); i++)
	{
		if (ChipMatrix[i][max_y].ChipState == TChip::CS_FINISHING)
		{
			DestroyChip(ivec2(i, max_y));
			AddChipToUp(i, rand() % ChipTypeCount, GetExpectedLeftBottomPos(i, ChipMatrix[i].size())+vec2(0, Match3FieldParams.CellHeight));
		}
	}
}

void TMatch3Logic::TryMatchAllChips()
{
	
	std::vector<ivec2> matchingChips = GetAvailableMatchingChips();
	
	std::vector<ivec2>::iterator i;

	for (i = matchingChips.begin(); i != matchingChips.end(); ++i)
	{
		ivec2 p = *i;

		int chipType = ChipMatrix[p.v[0]][p.v[1]].ChipType;
		DestroyChip(p);
		InsertEmptyChip(p.v[0], p.v[1], chipType);
	}

}

void TMatch3Logic::DestroyChip(ivec2 p)
{
	ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.NeedRefreshBuffer = true;

	ResourceManager->HalibutAnimationManager.DeleteAnimationObject(ChipMatrix[p.v[0]][p.v[1]].AnimName);

	ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].erase(
		ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].begin() + ChipMatrix[p.v[0]][p.v[1]].VertexListShift,
		ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].begin() + ChipMatrix[p.v[0]][p.v[1]].VertexListShift + 6);

	ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].erase(
		ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].begin() + ChipMatrix[p.v[0]][p.v[1]].VertexListShift,
		ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].begin() + ChipMatrix[p.v[0]][p.v[1]].VertexListShift + 6);

	MoveVertexListShiftBack(ChipMatrix[p.v[0]][p.v[1]].RenderPair, ChipMatrix[p.v[0]][p.v[1]].VertexListShift);

	ChipMatrix[p.v[0]].erase(ChipMatrix[p.v[0]].begin() + p.v[1]);
	/*
	for (cardinal i=p.v[1]; i < ChipMatrix[p.v[0]].size(); i++)
	{
		if (!ChipIsLocked(ivec2(p.v[0], i)) && !ChipIsFinishing(ivec2(p.v[0], i)))
		{
			ChipMatrix[p.v[0]][i].ChipState = TChip::CS_FALLING;
		}
	}*/

}

void TMatch3Logic::ChangeChipType(ivec2 p)
{
	cardinal newChipType = rand() % ChipTypeCount;

	TChip& chip = ChipMatrix[p.v[0]][p.v[1]];

	std::vector<vec3>::iterator vertexCoordItrBegin = chip.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].begin() + chip.VertexListShift;
	std::vector<vec3>::iterator vertexCoordItrEnd = vertexCoordItrBegin + 6;

	std::vector<vec2>::iterator texCoordItrBegin = chip.RenderPair->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].begin() + chip.VertexListShift;
	std::vector<vec2>::iterator texCoordItrEnd = texCoordItrBegin + 6;

	std::vector<vec3> vertexCoordSubVec(vertexCoordItrBegin, vertexCoordItrEnd);
	std::vector<vec2> texCoordSubVec(texCoordItrBegin, texCoordItrEnd);

	chip.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].erase(vertexCoordItrBegin, vertexCoordItrEnd);
	chip.RenderPair->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].erase(texCoordItrBegin, texCoordItrEnd);

	MoveVertexListShiftBack(chip.RenderPair, chip.VertexListShift);

	chip.RenderPair->second.NeedRefreshBuffer = true;

	chip.ChipType = newChipType;

	RenderPairIteratorVector[newChipType]->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].insert(RenderPairIteratorVector[newChipType]->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].end(), vertexCoordSubVec.begin(), vertexCoordSubVec.end());
	RenderPairIteratorVector[newChipType]->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].insert(RenderPairIteratorVector[newChipType]->second.Vec2CoordArr[CONST_STRING_TEXCOORD_ATTRIB].end(), texCoordSubVec.begin(), texCoordSubVec.end());

	RenderPairIteratorVector[newChipType]->second.NeedRefreshBuffer = true;

	chip.RenderPair = RenderPairIteratorVector[newChipType];
	chip.VertexListShift = RenderPairIteratorVector[newChipType]->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB].size() - 6;

	//Hack to make sure animation is replaced by new one
	ResourceManager->HalibutAnimationManager.DeleteAnimationObject(chip.AnimName);
	ReplaceAnimation(ivec2(p.v[0], p.v[1]));


}


void TMatch3Logic::SelectChip(ivec2 pos)
{
	if (!(selectedChip == ivec2(-1, -1)))
	{
		StopAnimateChip(selectedChip.v[0], selectedChip.v[1]);
	}

	StartAnimateChip(pos.v[0], pos.v[1]);

	selectedChip = pos;
}


void TMatch3Logic::UnselectChip()
{
	if (!(selectedChip == ivec2(-1, -1)))
	{
		StopAnimateChip(selectedChip.v[0], selectedChip.v[1]);
		selectedChip = ivec2(-1, -1);
	}
}

ivec2 TMatch3Logic::GetSelectedChip()
{
	return selectedChip;
}

std::vector<ivec2> TMatch3Logic::GetAvailableMatchingChips()
{
	std::vector<ivec2> result;

	for (cardinal x=0; x<Match3FieldParams.FieldWidth; x++)
	{
		for (cardinal y=0; y<Match3FieldParams.FieldHeight; y++)
		{
			ivec2 chip = ivec2(x, y);


			
			if (ChipCanBeMatchedUp(chip))
			{
				if (std::find(result.begin(), result.end(), chip) ==  result.end())
				{
					result.push_back(chip);
				}

				if (std::find(result.begin(), result.end(), chip + ivec2(0, 1)) ==  result.end())
				{
					result.push_back(chip + ivec2(0, 1));
				}

				if (std::find(result.begin(), result.end(), chip + ivec2(0, 2)) ==  result.end())
				{
					result.push_back(chip + ivec2(0, 2));
				}
			}


			if (ChipCanBeMatchedDown(chip))
			{
				if (std::find(result.begin(), result.end(), chip) ==  result.end())
				{
					result.push_back(chip);
				}

				if (std::find(result.begin(), result.end(), chip - ivec2(0, 1)) ==  result.end())
				{
					result.push_back(chip - ivec2(0, 1));
				}

				if (std::find(result.begin(), result.end(), chip - ivec2(0, 2)) ==  result.end())
				{
					result.push_back(chip - ivec2(0, 2));
				}
			}


			if (ChipCanBeMatchedLeft(chip))
			{
				if (std::find(result.begin(), result.end(), chip) ==  result.end())
				{
					result.push_back(chip);
				}

				if (std::find(result.begin(), result.end(), chip - ivec2(1, 0)) ==  result.end())
				{
					result.push_back(chip - ivec2(1, 0));
				}

				if (std::find(result.begin(), result.end(), chip - ivec2(2, 0)) ==  result.end())
				{
					result.push_back(chip - ivec2(2, 0));
				}
			}


			if (ChipCanBeMatchedRight(chip))
			{
				if (std::find(result.begin(), result.end(), chip) ==  result.end())
				{
					result.push_back(chip);
				}

				if (std::find(result.begin(), result.end(), chip + ivec2(1, 0)) ==  result.end())
				{
					result.push_back(chip + ivec2(1, 0));
				}

				if (std::find(result.begin(), result.end(), chip + ivec2(2, 0)) == result.end())
				{
					result.push_back(chip + ivec2(2, 0));
				}
			}


		}
	}


	return result;
}


void TMatch3Logic::SwapChips(ivec2 p1, ivec2 p2, bool isReturning)
{
	vec2 chip1PosFrom(LeftBottomPosField.v[0] + p1.v[0]*Match3FieldParams.CellWidth, LeftBottomPosField.v[1] + p1.v[1]*Match3FieldParams.CellHeight);
	vec2 chip2PosFrom(LeftBottomPosField.v[0] + p2.v[0]*Match3FieldParams.CellWidth, LeftBottomPosField.v[1] + p2.v[1]*Match3FieldParams.CellHeight);
	ChipSwappingPairVector.push_back(TChipSwappingPair(p1, p2, chip1PosFrom, chip2PosFrom));

	(ChipSwappingPairVector.end() - 1)->IsReturning = isReturning;

	ChipMatrix[p1.v[0]][p1.v[1]].ChipState = TChip::CS_SWAPPING;
	ChipMatrix[p2.v[0]][p2.v[1]].ChipState = TChip::CS_SWAPPING;


}

void TMatch3Logic::ResetChipPos(ivec2 p)
{
	vec2 posFrom(LeftBottomPosField.v[0] + p.v[0]*Match3FieldParams.CellWidth, LeftBottomPosField.v[1] + p.v[1]*Match3FieldParams.CellHeight);
	vec2 posTo(LeftBottomPosField.v[0] + (p.v[0] + 1)*Match3FieldParams.CellWidth, LeftBottomPosField.v[1] + (p.v[1] + 1)*Match3FieldParams.CellHeight);
	
	std::vector<vec3> vertexCoordVec = MakeVertexCoordVec(posFrom, posTo);

	TChip& chip = ChipMatrix[p.v[0]][p.v[1]];

	for (cardinal i = 0; i<vertexCoordVec.size(); i++)
	{
		chip.RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][chip.VertexListShift + i] = vertexCoordVec[i];
	}

	chip.RenderPair->second.NeedRefreshBuffer = true;
	
	
}

void TMatch3Logic::UpdateLogic(cardinal dt)
{

	if (dt > 1000)
	{
		dt = 1000; //To prevent float overflow during looong laaaag
	}

	UpdateChipSwapping(dt);
	UpdateChipPosition(dt);
	TryMatchAllChips();
	
	std::vector<TChipDeletingData>::iterator i;

	bool erased = false;

	for (i = ChipDeletingVector.begin(); i != ChipDeletingVector.end();)
	{
		erased = false;

		i->T += dt / 150.f;

		if (i->T > 1.f)
		{
			i->T = 1.f;
		}

		ivec2 p = i->Chip;
		vec2 leftBottomPos = i->Pos;

		vec2 halfPos = vec2(Match3FieldParams.CellWidth / 2.f, Match3FieldParams.CellHeight / 2.f);

		vec2 centerPos = leftBottomPos + halfPos;

		std::vector<vec3> vertexCoordArr = MakeVertexCoordVec(centerPos - (1.f - i->T)*halfPos, centerPos + (1.f - i->T)*halfPos);

		for (int t = 0; t < 6; t++)
		{
			ChipMatrix[p.v[0]][p.v[1]].RenderPair->second.Vec3CoordArr[CONST_STRING_POSITION_ATTRIB][ChipMatrix[p.v[0]][p.v[1]].VertexListShift + t] = vertexCoordArr[t];
		}


		if (i->T == 1.f)
		{
			
			ChipMatrix[p.v[0]][p.v[1]].ChipState = TChip::CS_FINISHING;

			i = ChipDeletingVector.erase(i);
			erased = true;
		}

		if (i  != ChipDeletingVector.end() && !erased)
		{
			++i;
		}
	}

	RemoveBubbles();
}



void TMatch3Logic::HitFieldWithPattern(ivec2 pos, std::vector<std::vector<char> > pattern, std::vector<std::vector<char> > jumpingPattern)
{

	const float CONST_VELOCITY_TO_ADD = 0.2f;

	for (cardinal i = 0; i < pattern.size(); i++)
	{
		for (int j = static_cast<int>(pattern[i].size()-1); j >=0; j--)
		{

			ivec2 fieldPos = pos + ivec2(i,j);

			if (fieldPos.v[0] >= 0 && fieldPos.v[0] < static_cast<int>(ChipMatrix.size()) &&
				fieldPos.v[1] >= 0 && fieldPos.v[1] < static_cast<int>(ChipMatrix[fieldPos.v[0]].size()))
			{

				if (ChipIsStable(fieldPos))
				{
					if (pattern[i][j] > 0)
					{
						DestroyChip(fieldPos);
						InsertEmptyChip(fieldPos.v[0], fieldPos.v[1], 0);
					}
				}

				if (ChipIsLocked(fieldPos))
				{
					ChipMatrix[fieldPos.v[0]][fieldPos.v[1]].ChipState = TChip::CS_STANDBY;
				}
			}
		}
	}

	for (cardinal i = 0; i < jumpingPattern.size(); i++)
	{
		
		ivec2 abovePos = pos + ivec2(i, jumpingPattern[i].size());
		
		if (abovePos.v[0] >= 0 && abovePos.v[0] < static_cast<int>(ChipMatrix.size()) &&
			abovePos.v[1] >= 0 && abovePos.v[1] < static_cast<int>(ChipMatrix[i].size()))
		{

			if (ChipMatrix[abovePos.v[0]][abovePos.v[1]].ChipState == TChip::CS_FALLING ||
				ChipMatrix[abovePos.v[0]][abovePos.v[1]].ChipState == TChip::CS_STANDBY)
			{
				ChipMatrix[abovePos.v[0]][abovePos.v[1]].ChipState = TChip::CS_FALLING;
				ChipMatrix[abovePos.v[0]][abovePos.v[1]].Velocity += CONST_VELOCITY_TO_ADD;
			}

		}

		
		for (cardinal j = 0; j < jumpingPattern[i].size(); j++)
		{

			ivec2 fieldPos = pos + ivec2(i,j);

			if (fieldPos.v[0] >= 0 && fieldPos.v[0] < static_cast<int>(ChipMatrix.size()) &&
				fieldPos.v[1] >= 0 && fieldPos.v[1] < static_cast<int>(ChipMatrix[fieldPos.v[0]].size()))
			{
				if (ChipMatrix[fieldPos.v[0]][fieldPos.v[1]].ChipState == TChip::CS_FALLING ||
					ChipMatrix[fieldPos.v[0]][fieldPos.v[1]].ChipState == TChip::CS_STANDBY)
				{
					if (jumpingPattern[i][j] > 0)
					{
						ChipMatrix[fieldPos.v[0]][fieldPos.v[1]].ChipState = TChip::CS_FALLING;
						ChipMatrix[fieldPos.v[0]][fieldPos.v[1]].Velocity += CONST_VELOCITY_TO_ADD;
					}
				}
			}
		}
	}


}



//================================================
//============ TMatch3Field ======================
//================================================


TMatch3Field::TMatch3Field()
{
	LastTappedPos = vec2(0,0);
}

TMatch3Field::TMatch3Field(const TMatch3Field& m)
{
	LastTappedPos = m.LastTappedPos;
	LastMovePos = m.LastMovePos;
}

TMatch3Field& TMatch3Field::operator=(const TMatch3Field& m)
{
	LastTappedPos = m.LastTappedPos;
	LastMovePos = m.LastMovePos;
	return *this;
}

TMatch3Field::TMatch3Field(TMatch3Controller& match3Controller)
	//: Match3Controller(match3Controller)
{
	//Match3Controller.Match3Field = this;

	FillBasicChipMatrixAndTriangleList();
}

TMatch3Field::~TMatch3Field()
{
}

void TMatch3Field::FillBasicChipMatrixAndTriangleList()
{
	TRenderParams renderParams;

	renderParams.Transparency = 1.f;

	
	boost::shared_ptr<boost::property_tree::ptree> px(FileToPropertyTree("match3params.xml"));
	Match3FieldParams.Serialize(*px);

	std::vector<TRenderPairList::iterator> triangleListVec;

	for (cardinal i = 0; i < Match3FieldParams.ChipTypeCount; i++)
	{
		TriangleListVector.push_back(TRenderPair(Match3FieldParams.ChipTemplateParamsArr[i].SelectedRenderParams, TTriangleList()));
	}

	
	renderParams.TexName = "chip1locked";
	TriangleListVector.push_back(TRenderPair(renderParams, TTriangleList()));
	
	renderParams.TexName = "chip2locked";
	TriangleListVector.push_back(TRenderPair(renderParams, TTriangleList()));

	renderParams.TexName = "chip3locked";
	TriangleListVector.push_back(TRenderPair(renderParams, TTriangleList()));

	renderParams.TexName = "chip4locked";
	TriangleListVector.push_back(TRenderPair(renderParams, TTriangleList()));
	
	
	
	
	for (cardinal i = 0; i < Match3FieldParams.ChipTypeCount; i++)
	{
		TriangleListVector.push_back(TRenderPair(Match3FieldParams.ChipTemplateParamsArr[i].FinishingRenderParams, TTriangleList()));
	}

	for (TRenderPairList::iterator i = TriangleListVector.begin(); i != TriangleListVector.end(); ++i)
	{
		

		triangleListVec.push_back(i);
	}

	LeftBottomPos = vec2(150, 0);

	FillRandomChipMatrix(triangleListVec, LeftBottomPos);
	
	//Init everything
	selectedChip = ivec2(-1, -1);

	for (TRenderPairList::iterator i = TriangleListVector.begin(); i != TriangleListVector.end(); ++i)
	{
		
		i->second.RefreshBuffer();
	}
	
}


ivec2 TMatch3Field::PosToChip(vec2 pos)
{
	
	int x, y;
	vec2 fieldPos = pos - LeftBottomPos;
	x = static_cast<int>(fieldPos.v[0] / Match3FieldParams.CellWidth);
	y = static_cast<int>(fieldPos.v[1] / Match3FieldParams.CellHeight);
	
	return ivec2(x, y);

}

void TMatch3Field::Update(cardinal dt)
{
	
	TMatch3Logic::UpdateLogic(dt);


		TRenderPairList::iterator i;
		for (i = TriangleListVector.begin(); i != TriangleListVector.end(); ++i)
		{
			if (i->second.NeedRefreshBuffer)
			{
				i->second.RefreshBuffer();
			}
		}

		int y = 6;
		y = 6 + y;
	

}

void TMatch3Field::OnTapDown(vec2 pos)
{
	//See also OnMove
	
	LastTappedPos = pos;
	LastMovePos = LastTappedPos;

	ivec2 chip = PosToChip(pos);
	


	/*
	To hit field by pattern
	std::vector<std::vector<char> > pattern;
	pattern.resize(3);
	pattern[0].resize(3);
	pattern[1].resize(3);
	pattern[2].resize(3);

	pattern[0][0] = 0; pattern[1][0] = 1; pattern[2][0] = 0;
	pattern[0][1] = 1; pattern[1][1] = 1; pattern[2][1] = 1;
	pattern[0][2] = 0; pattern[1][2] = 1; pattern[2][2] = 0;

	std::vector<std::vector<char> > jumpingPattern;
	jumpingPattern.resize(3);
	jumpingPattern[0].resize(3);
	jumpingPattern[1].resize(3);
	jumpingPattern[2].resize(3);

	jumpingPattern[0][0] = 0; jumpingPattern[1][0] = 0; jumpingPattern[2][0] = 0;
	jumpingPattern[0][1] = 0; jumpingPattern[1][1] = 0; jumpingPattern[2][1] = 0;
	jumpingPattern[0][2] = 1; jumpingPattern[1][2] = 0; jumpingPattern[2][2] = 1;


	HitFieldWithPattern(chip - ivec2(1, 1), pattern, jumpingPattern);

	return;*/


	
	if (selectedChip == chip)
	{
		UnselectChip();
	}
	else
	{

		if (ChipsCanBeSwapped(selectedChip, chip))
		{
			SwapChips(selectedChip, chip);
			UnselectChip();
		}
		else
		{
			if (ChipCanBeSelected(chip))
			{
				SelectChip(chip);
			}
		}

	}
}


void TMatch3Field::OnTapUp(vec2 pos)
{
	
}

void TMatch3Field::OnMove(vec2 shift)
{
	
	if (selectedChip == ivec2(-1, -1))
	{
		return;
	}

	LastMovePos += shift;

	if ((LastTappedPos - LastMovePos).v[0] >= Match3FieldParams.CellWidth * 0.5f)
	{
		ivec2 newPosChip = selectedChip + ivec2(1, 0);

		if (selectedChip.v[0] <= Match3FieldParams.FieldWidth - 2)
		{
			if (ChipsCanBeSwapped(selectedChip, newPosChip))
			{
				SwapChips(selectedChip, newPosChip);
				UnselectChip();
			}
		}
	}
	else if ((LastMovePos - LastTappedPos).v[0] >= Match3FieldParams.CellWidth * 0.5f)
	{
		ivec2 newPosChip = selectedChip - ivec2(1, 0);

		if (selectedChip.v[0] >= 1)
		{
			if (ChipsCanBeSwapped(selectedChip, newPosChip))
			{
				SwapChips(selectedChip, newPosChip);
				UnselectChip();
			}
		}
	}
	else if ((LastMovePos - LastTappedPos).v[1] >= Match3FieldParams.CellHeight * 0.5f)
	{
		ivec2 newPosChip = selectedChip + ivec2(0, 1);

		if (selectedChip.v[1] <= Match3FieldParams.FieldHeight - 2)
		{
			if (ChipsCanBeSwapped(selectedChip, newPosChip))
			{
				SwapChips(selectedChip, newPosChip);
				UnselectChip();
			}
		}
	}
	else if ((LastTappedPos - LastMovePos).v[1] >= Match3FieldParams.CellHeight * 0.5f)
	{
		ivec2 newPosChip = selectedChip - ivec2(0, 1);

		if (selectedChip.v[1] >= 1)
		{
			if (ChipsCanBeSwapped(selectedChip, newPosChip))
			{
				SwapChips(selectedChip, newPosChip);
				UnselectChip();
			}
		}
	}
	
}


bool TMatch3Field::CheckClick(vec2 mousePos)
{
	
	vec2 fieldPos = mousePos - LeftBottomPos;
	if (fieldPos.v[0] >= 0  && fieldPos.v[0] <= Match3FieldParams.FieldWidth*Match3FieldParams.CellWidth)
	{
		if (fieldPos.v[1] >= 0  && fieldPos.v[1] <= Match3FieldParams.FieldHeight*Match3FieldParams.CellHeight)
		{
			return true;
		}
	}
	
	return false;
}



void TMatch3Field::HighlightMatch3()
{
	
	std::vector<ivec2> chips = GetAvailableMatchingChips();

	std::vector<ivec2>::iterator i;

	for (i = chips.begin(); i != chips.end(); ++i)
	{
		ResourceManager->HalibutAnimationManager.StartAnimation(ChipMatrix[i->v[0]][i->v[1]].AnimName);
		
	}
}
