#include "stdafx.h"
#include "MW4GameEd.h"
#include "MoveCommand.h"
#include "EditorWaypoint.h"
#include <Adept\Player.hpp>
#include <Adept\Entity.hpp>
#include <Adept\Interface.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\VideoRenderer.hpp>
#include <Adept\CameraComponent.hpp>
#include <Adept\Map.hpp>
#include "DisplayWindow.h"
#include "OverviewWindow.h"
#include "Selection.h"
extern CMW4GameEdApp theApp;

MoveCommand::MoveCommand(Entity *object)
	: Command()
{
	Check_Object(object);
	moveOffset = Point3D::Identity;
	cursorPosition = object->GetLocalToWorld();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MoveCommand::~MoveCommand()
{

}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void 
	MoveCommand::Execute()
{
	Move();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MoveCommand::Undo()
{
	theApp.MakeObjectsZoneBased();

	LinearMatrix4D new_local_to_world;
	Point3D target_in_world;
	CSelectionNode *selection_node;
	YawPitchRoll current_rotation;
	Entity* entity;

	if (!selectedEntities.IsEmpty())
	{
		ChainIteratorOf<CSelectionNode*> iterator(&selectedEntities);
		while((selection_node = iterator.ReadAndNext()) != NULL)
		{
			entity = selection_node->selectedEntity;
			target_in_world = entity->GetLocalToWorld();
			target_in_world -= moveOffset;
			current_rotation = entity->GetLocalToParent();
	
			new_local_to_world.BuildTranslation(target_in_world);
			new_local_to_world.BuildRotation(current_rotation);

			Check_Object(entity);
			entity->SetNewLocalToParent(new_local_to_world);
			entity->SyncMatrices(true);

			if (entity->IsDerivedFrom(EditorPathNode::DefaultData))
			{
				EditorPathNode* node;
				node = Cast_Object(EditorPathNode*,entity);
				node->pathSlot.GetCurrent()->pathSlot.GetCurrent()->pathPoints[node->index] = target_in_world;
				node->UpdateLinkPointData();
			}
			else if (entity->IsDerivedFrom(EditorBoundaryNode::DefaultData))
			{
				EditorBoundaryNode* node;
				node = Cast_Object(EditorBoundaryNode*,entity);
				(*node->myBoundary)[node->index].x = target_in_world.x;
				(*node->myBoundary)[node->index].y = target_in_world.z;
				node->UpdateLinkPointData();
			}
			else if (entity->IsDerivedFrom(EditorDropNode::DefaultData))
			{
				EditorDropNode* node;
				node = Cast_Object(EditorDropNode*,entity);
				node->dropZone->SetNewLocalToParent(new_local_to_world);
				node->dropZone->SyncMatrices(true);

				EditorDropZonePoint* point;
				Point3D newPoint;
				LinearMatrix4D matrix = LinearMatrix4D::Identity;
				ChainIteratorOf<EditorDropZonePoint*> iterator(&node->pointChain);
				while((point = iterator.ReadAndNext()) != NULL)
				{
					newPoint = new_local_to_world;
					newPoint -= point->offset;
					newPoint.y = theApp.displayWindow->GetMapPointVertical(newPoint.x,newPoint.z);
					matrix.BuildTranslation(newPoint);
					point->SetNewLocalToParent(matrix);
					point->SyncMatrices(true);
					point->UpdateLinkPointData();
				}
				node->UpdatePoints();
			}
			else if (entity->IsDerivedFrom(EditorDropZonePoint::DefaultData))
			{
				EditorDropZonePoint* node;
				node = Cast_Object(EditorDropZonePoint*,entity);
				node->SetNewLocalToParent(new_local_to_world);
				node->SyncMatrices(true);
				node->UpdateLinkPointData();
				node->dropNode->UpdatePoints();
			}
/*
			else if (entity->IsDerivedFrom(EditorObjectiveMarker::DefaultData))
			{
				EditorObjectiveMarker* node;
				node = Cast_Object(EditorObjectiveMarker*,entity);
				node->objective->SetNewLocalToParent(new_local_to_world);
				node->objective->SyncMatrices(true);
			}
*/
			else if (entity->IsDerivedFrom(EditorPointLight::DefaultData))
			{
				EditorPointLight* pointLight;
				pointLight = Cast_Object(EditorPointLight*,entity);

				pointLight->SetLightToWorldMatrix(pointLight->GetLocalToWorld());
			}
			else if (entity->IsDerivedFrom(EditorSpotLight::DefaultData))
			{
				EditorSpotLight* spotLight;
				spotLight = Cast_Object(EditorSpotLight*,entity);

				spotLight->SetLightToWorldMatrix(spotLight->GetLocalToWorld());
			}
			else if (entity->IsDerivedFrom(EditorCameraShip::DefaultData))
			{
				EditorCameraShip* node;
				node = Cast_Object(EditorCameraShip*,entity);
				node->cameraShip->SetNewLocalToParent(new_local_to_world);
				node->cameraShip->SyncMatrices(true);
			}

		}
	}
	theApp.MakeObjectsTileBased();
	theApp.overviewWindow->DrawWindow();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MoveCommand::Move()
{
	//This function needs to run through the list and move all according
	//to the offset of the cursor.  This is not going to be as easy as it
	//seems.  First just move one.

	Check_Object(Map::Instance);

	theApp.MakeObjectsZoneBased();
	
	Point3D  new_offset;
	LinearMatrix4D new_local_to_world;
	CSelectionNode *selection_node;
	Entity* entity;
	Point3D translation = Point3D::Identity;

	Check_Object(Player::Instance);
	Check_Object(Player::Instance->playerInterface);
	int x_down = GetAsyncKeyState(KEY_X) & 0x8000;
	int y_down = GetAsyncKeyState(KEY_Y) & 0x8000;
	int z_down = GetAsyncKeyState(KEY_Z) & 0x8000;
	Entity *reticule;
	reticule = Player::Instance->playerInterface->reticuleEntity.GetCurrent();
	if(reticule)
	{
		Point3D world_offset = Player::Instance->playerInterface->reticuleOffset; 
		LinearMatrix4D new_local_to_parent = LinearMatrix4D::Identity;
		new_local_to_parent.Multiply(reticule->GetLocalToParent(), world_offset);
		translation = new_local_to_parent;
	}
	else if (!y_down)	// make sure we're not trying to move vertically.
		return;		// must be pointing at the sky

	bool x_blocked = false, y_blocked = true, z_blocked = false;

	if (x_down || y_down || z_down)
	{
		x_blocked = !x_down;
		y_blocked = !y_down;
		z_blocked = !z_down;
	}

	new_offset = translation;
	new_offset -= cursorPosition;
	
	if (x_blocked)
		new_offset.x = 0;
	if (z_blocked)
		new_offset.z = 0;

	if (!(selectedEntities.IsEmpty()))
	{
		ChainIteratorOf<CSelectionNode*> iterator(&selectedEntities);
		iterator.First();
		while((selection_node = iterator.ReadAndNext()) != NULL)
		{
			entity = selection_node->selectedEntity;
			Point3D target_in_world,old_entity_pos;
			target_in_world = entity->GetLocalToWorld();
			old_entity_pos = target_in_world;
			YawPitchRoll current_rotation;
			current_rotation = entity->GetLocalToParent();

			if (y_blocked)
			{
				target_in_world += new_offset;
				moveOffset += new_offset;

				target_in_world.y = theApp.displayWindow->GetMapPointVertical(target_in_world.x,target_in_world.z);
				CEditorEntityData *exdata=theApp.GetEntityData(entity);
				if(exdata) 
				target_in_world.y+=exdata->VOffset;

			}
			else
			{
				Vector2DOf<Scalar> cursor;
				gos_GetMouseInfo(&cursor.x, &cursor.y, NULL, NULL, NULL, NULL);

				// Clean out the interface data

				Check_Object(VideoRenderer::Instance);
				EntityComponentWeb *web =
					Cast_Object(EntityComponentWeb*,VideoRenderer::Instance->GetSceneCamera()->GetComponentWeb());
				Interface *intface = Cast_Object(Interface*, web->GetEntity());
				intface->reticuleEntity.Remove();

				// Figure out the eyeline

				Line3D world_line;
				VideoRenderer::Instance->GetSceneCamera()->ComputeEyeLine(&world_line, cursor);

				Point3D point,y_offset;
				point = entity->GetLocalToWorld();
				Vector3D vector(world_line.m_direction.x,0,world_line.m_direction.z);
				Normal3D plane_normal;
				plane_normal.Normalize(vector);

				Stuff::Plane plane(plane_normal,plane_normal * point);

				Scalar axis;
				Scalar length = world_line.GetDistanceTo(plane,&axis);
				world_line.Project(length,&y_offset);
			
				
				target_in_world += new_offset;
				moveOffset += new_offset;
			}

			if (theApp.gridOn && !(x_down || y_down || z_down))
				theApp.GetGridCoords(target_in_world);


			new_local_to_world.BuildTranslation(target_in_world);
			new_local_to_world.BuildRotation(current_rotation);

			Check_Object(entity);
			entity->SetNewLocalToParent(new_local_to_world);
			entity->SyncMatrices(true);
//			EntityManager::Instance->RequestPostCollisionExecution(entity);
			Map::Instance->UpdateZone(entity);

			Check_Object(EntityManager::Instance);

			if (entity->GetSolidVolume() || entity->GetCollisionMask() == Entity::EditorIsMovingFlag)
			{
				if (entity->IsMultiZone())
					entity->GetElement()->SetCallbackIndex(ElementRenderer::Element::DrawRedBounds);
				else if (entity->IsMultiTile())
					entity->GetElement()->SetCallbackIndex(ElementRenderer::Element::DrawYellowBounds);
				else
					entity->GetElement()->SetCallbackIndex(ElementRenderer::Element::DrawGreenBounds);
			}
			else
				entity->GetElement()->SetCallbackIndex(ElementRenderer::Element::DrawGreenBounds);

			if (entity->IsDerivedFrom(EditorLatticeNode::DefaultData))
			{
				EditorLatticeNode* node;
				node = Cast_Object(EditorLatticeNode*,entity);
				node->railNode->Location(target_in_world);
				node->railNode->UpdateRadiusPointData();
			}
			else if (entity->IsDerivedFrom(EditorPathNode::DefaultData))
			{
				EditorPathNode* node;
				node = Cast_Object(EditorPathNode*,entity);
				node->pathSlot.GetCurrent()->pathSlot.GetCurrent()->pathPoints[node->index] = target_in_world;
				node->UpdateLinkPointData();
			}
			else if (entity->IsDerivedFrom(EditorBoundaryNode::DefaultData))
			{
				EditorBoundaryNode* node;
				node = Cast_Object(EditorBoundaryNode*,entity);

				(*node->myBoundary)[node->index].x = target_in_world.x;
				(*node->myBoundary)[node->index].y = target_in_world.z;

				node->CheckValidPos();
				node->UpdateLinkPointData();
			}
			else if (entity->IsDerivedFrom(EditorDropNode::DefaultData))
			{
				EditorDropNode* node;
				node = Cast_Object(EditorDropNode*,entity);
				node->dropZone->SetNewLocalToParent(new_local_to_world);
				node->dropZone->SyncMatrices(true);

				EditorDropZonePoint* point;
				LinearMatrix4D matrix = LinearMatrix4D::Identity;
				ChainIteratorOf<EditorDropZonePoint*> iterator(&node->pointChain);
				Point3D newPoint;
				while((point = iterator.ReadAndNext()) != NULL)
				{
					newPoint = new_local_to_world;
					newPoint -= point->offset;
					newPoint.y = theApp.displayWindow->GetMapPointVertical(newPoint.x,newPoint.z);
					matrix.BuildTranslation(newPoint);
					point->SetNewLocalToParent(matrix);
					point->SyncMatrices(true);
					point->UpdateLinkPointData();
				}
				node->UpdatePoints();
			}
			else if (entity->IsDerivedFrom(EditorDropZonePoint::DefaultData))
			{
				EditorDropZonePoint* node;
				node = Cast_Object(EditorDropZonePoint*,entity);
				node->SetNewLocalToParent(new_local_to_world);
				node->SyncMatrices(true);
				node->UpdateLinkPointData();

				Point3D newPoint;
				newPoint = new_local_to_world;
				node->offset = node->dropNode->GetLocalToWorld();
				node->offset -= newPoint;
				node->dropNode->UpdatePoints();
			}
/*
			else if (entity->IsDerivedFrom(EditorObjectiveMarker::DefaultData))
			{
				EditorObjectiveMarker* node;
				node = Cast_Object(EditorObjectiveMarker*,entity);
				node->objective->SetNewLocalToParent(new_local_to_world);
				node->objective->SyncMatrices(true);
			}
*/
			else if (entity->IsDerivedFrom(EditorPointLight::DefaultData) ||
					 entity->IsDerivedFrom(EditorSpotLight::DefaultData))
			{
				EditorLight* light;
				light = Cast_Object(EditorLight*,entity);

				light->SetLightToWorldMatrix(light->GetLocalToWorld());
			}
			
			else if (entity->IsDerivedFrom(EditorCameraShip::DefaultData))
			{
				EditorCameraShip* node;
				node = Cast_Object(EditorCameraShip*,entity);
				node->cameraShip->SetNewLocalToParent(new_local_to_world);
				node->cameraShip->SyncMatrices(true);
			}

		}
		cursorPosition = translation;
	}
	theApp.MakeObjectsTileBased();
}
