#include "ShapeEditor.h"
#include <string>
#include <fstream>
#include <iostream>
#include <sstream>
#include "Box2D/Collision/Shapes/b2PolygonShape.h"

using namespace rapidxml;


ShapeEditor::ShapeEditor( const char* path , sf::RenderWindow * window) : currentSet(""), editMode(false)
{
	FileInterface = new ShellFileInterface(path);
	Renderer.SetWindow(window);

	MyWindow = window;
	currentShape = 0;

	Rocket::Core::SetFileInterface(FileInterface);
	Rocket::Core::SetRenderInterface(&Renderer);
	Rocket::Core::SetSystemInterface(&SystemInterface);

	Rocket::Core::Initialise();
	Rocket::Controls::Initialise();

	Rocket::Core::FontDatabase::LoadFontFace("Delicious-Bold.otf");
	Rocket::Core::FontDatabase::LoadFontFace("Delicious-BoldItalic.otf");
	Rocket::Core::FontDatabase::LoadFontFace("Delicious-Italic.otf");
	Rocket::Core::FontDatabase::LoadFontFace("Delicious-Roman.otf");

	Context = Rocket::Core::CreateContext("default",
		Rocket::Core::Vector2i(MyWindow->GetWidth(), MyWindow->GetHeight()));

	Rocket::Debugger::Initialise(Context);

	Rocket::Core::ElementDocument *Document = Context->LoadDocument("menu.rml");

	if(Document)
	{
		Document->Show();
		Rocket::Core::Element* title = Document->GetElementById("title");
		if (title != NULL)
			title->SetInnerRML(Document->GetTitle());
		Document->RemoveReference();
	};
	Document = Context->LoadDocument("add_set.rml");
	if(Document)
	{
		Document->Show();
		Rocket::Core::Element* title = Document->GetElementById("title");
		if (title != NULL)
			title->SetInnerRML(Document->GetTitle());
		Document->RemoveReference();
	};
	Document = Context->LoadDocument("add_shape.rml");
	if(Document)
	{
		Document->Show();
		Rocket::Core::Element* title = Document->GetElementById("title");
		if (title != NULL)
			title->SetInnerRML(Document->GetTitle());
		Document->RemoveReference();
	};

	RegisterListeners();
	LoadSetFile("shapes.xml");
	SaveToFile("new.xml");
 
}
void ShapeEditor::ProcessEvent( Rocket::Core::Event& event )
{
	if (event.GetType() == "click") {
		ProcessClickEvent(event);
	}
}

void ShapeEditor::RegisterClickListener( Rocket::Core::Element* element )
{
	element->AddEventListener("click", this);
}

void ShapeEditor::SaveToFile(std::string fileName)
{
	xml_document<> doc;

	xml_node<>* decl = doc.allocate_node(node_declaration);
	decl->append_attribute(doc.allocate_attribute("version", "1.0"));
	decl->append_attribute(doc.allocate_attribute("encoding", "utf-8"));
	doc.append_node(decl);

	// root node
	xml_node<>* root = doc.allocate_node(node_element, "Shapes");
	root->append_attribute(doc.allocate_attribute("set", currentSet.c_str()));
	doc.append_node(root);

	for (unsigned int t = 0; t < storedShapes.size(); t++)
	{
		Shapes* shape = storedShapes[t];
		xml_node<>* child = doc.allocate_node(node_element, "Shape");

		char *name = doc.allocate_string(shape->name.c_str());			// Allocate string and copy name into it
		char *imgPath = doc.allocate_string(shape->imgPath.c_str());			// Allocate string and copy name into it
		child->append_attribute(doc.allocate_attribute("name", name));
		child->append_attribute(doc.allocate_attribute("img", imgPath));
		for (unsigned it = 0; it < shape->subShapes.size(); it++)
		{
			if (shape->subShapes[it]->GetType() == b2Shape::e_circle)
			{
				char *type = doc.allocate_string("Circle");			// Allocate string and copy name into it
				std::ostringstream radStr;
				radStr << shape->subShapes[it]->m_radius;
				char *radius = doc.allocate_string(radStr.str().c_str());			// Allocate string and copy name into it
				xml_node<>* child2 = doc.allocate_node(node_element, "Sub");
				child2->append_attribute(doc.allocate_attribute("type", type));
				child2->append_attribute(doc.allocate_attribute("radius", radius));
				child->append_node(child2);
			}
			else if (shape->subShapes[it]->GetType() == b2Shape::e_polygon)
			{
				b2PolygonShape *shap = (b2PolygonShape*)shape->subShapes[it];
				char *type = doc.allocate_string("Polygon");			// Allocate string and copy name into it
				xml_node<>* child2 = doc.allocate_node(node_element, "Sub");
				child2->append_attribute(doc.allocate_attribute("type", type));
				for (int at = 0; at < shap->GetVertexCount(); at++)
				{
					xml_node<>* child3 = doc.allocate_node(node_element, "Vertex");
					std::ostringstream posStrX;
					posStrX << shap->GetVertex(at).x;
					char *x = doc.allocate_string(posStrX.str().c_str());			// Allocate string and copy name into it
					std::ostringstream posStrY;
					posStrY << shap->GetVertex(at).y;
					char *y = doc.allocate_string(posStrY.str().c_str());			// Allocate string and copy name into it
					child3->append_attribute(doc.allocate_attribute("x", x));
					child3->append_attribute(doc.allocate_attribute("y", y));
					child2->append_node(child3);
				}
				child->append_node(child2);
			}
		}
		doc.first_node("Shapes")->append_node(child);
	}

	std::string xml_as_string;
	// watch for name collisions here, print() is a very common function name!
	print(std::back_inserter(xml_as_string), doc);

	std::ofstream xmlFile(fileName.c_str(), std::ios_base::out);

	xmlFile << xml_as_string;
	// xml_as_string now contains the XML in string form, indented
	// (in all its angle bracket glory)

	//std::string xml_no_indent;
	// print_no_indenting is the only flag that print() knows about
	//print(std::back_inserter(xml_as_string), doc, print_no_indenting);
	// xml_no_indent now contains non-indented XML
}

void ShapeEditor::LoadSetFile( string fileName)
{
	ClearStoredShapes();
	Shapes::ParseShapesToVector(currentSet, fileName, storedShapes);

	if (!storedShapes.empty())
	{
	currentShape = storedShapes[0];
	currentShapeSprite.SetImage(currentShape->img);
	currentShapeSprite.SetScale(1,1);
	currentShapeSprite.SetPosition(sf::Vector2f((float)MyWindow->GetWidth() / 2, (float)MyWindow->GetHeight() / 2));
	currentShapeSprite.SetOrigin( (float)currentShape->img.GetWidth() / 2, (float)currentShape->img.GetHeight() / 2);
	}
	else
		currentShape = 0;

	string infoText = "Current set is: " + currentSet;
	Context->GetRootElement()->GetElementById("current_set_info")->SetInnerRML(infoText.c_str());
}

void ShapeEditor::NewSet( string opt )
{
	currentSet = opt;

	string infoText = "Current set is: " + currentSet;
	Context->GetRootElement()->GetElementById("current_set_info")->SetInnerRML(infoText.c_str());

	ClearStoredShapes();
}

ShapeEditor::~ShapeEditor()
{

}

void ShapeEditor::Update()
{
	Context->Update();
}

void ShapeEditor::Render()
{
	Context->Render();
}
void ShapeEditor::OnEvent( const sf::Event &event )
{
	const sf::Input &input = MyWindow->GetInput();
	if (editMode == true && !editedLines.empty()) {
	editedLines.back()->end.x = (float32)input.GetMouseX();
	editedLines.back()->end.y = (float32)input.GetMouseY();
	}

	switch(event.Type)
	{ 
	case sf::Event::Resized:
		Renderer.Resize();
		break;
	case sf::Event::MouseMoved:
		Context->ProcessMouseMove(event.MouseMove.X, event.MouseMove.Y,
			SystemInterface.GetKeyModifiers(MyWindow));
		break;
	case sf::Event::MouseButtonPressed:
		Context->ProcessMouseButtonDown(event.MouseButton.Button,
			SystemInterface.GetKeyModifiers(MyWindow));
		if (editMode == true && event.MouseButton.Button == 0) {
			Line* newLine = new Line();
			newLine->begin.x = (float32)input.GetMouseX();
			newLine->begin.y = (float32)input.GetMouseY();
			editedLines.push_back(newLine);
		}
		if (editMode == true && event.MouseButton.Button == 1) {
			delete editedLines.back();
			editedLines.pop_back();
			b2Vec2 begin = editedLines.back()->begin;
			delete editedLines.back();
			editedLines.pop_back();
			Line* newLine = new Line();
			newLine->begin = begin;
			editedLines.push_back(newLine);
		}
		break;
	case sf::Event::MouseButtonReleased:
		Context->ProcessMouseButtonUp(event.MouseButton.Button,
			SystemInterface.GetKeyModifiers(MyWindow));
		break;
	case sf::Event::MouseWheelMoved:
		Context->ProcessMouseWheel(-event.MouseWheel.Delta,
			SystemInterface.GetKeyModifiers(MyWindow));
		break;
	case sf::Event::TextEntered:
		if (event.Text.Unicode >= 32)
			Context->ProcessTextInput(event.Text.Unicode);
	case sf::Event::KeyPressed:
		Context->ProcessKeyDown(SystemInterface.TranslateKey(event.Key.Code),
			SystemInterface.GetKeyModifiers(MyWindow));
		break;
	case sf::Event::KeyReleased:
		if(event.Key.Code == sf::Key::F8)
		{
			Rocket::Debugger::SetVisible(!Rocket::Debugger::IsVisible());
		};
		if(event.Key.Code == sf::Key::Space)
		{
			if (editMode == true) {
				editMode = false;
				int verticeCount = editedLines.size();

				b2Vec2 *vertices = new b2Vec2[editedLines.size() + 1];
				for (unsigned int t = 0; 0 > editedLines.size(); t++)
				{
					vertices[t] = editedLines[0]->begin;
					delete editedLines[0];
					editedLines.erase(editedLines.begin());
				}
				b2PolygonShape* newShape = new b2PolygonShape();
				newShape->Set(vertices, verticeCount);
				currentShape->subShapes.push_back(newShape);
			}
		}
		if(event.Key.Code == sf::Key::LControl)
		{
			editMode = true;
		}
		if(event.Key.Code == sf::Key::Back)
		{
			if (editMode == true) {
				editMode = false;
				ClearEditedLines();
			}
		}

		Context->ProcessKeyUp(SystemInterface.TranslateKey(event.Key.Code),
			SystemInterface.GetKeyModifiers(MyWindow));
		break;
	};
}

void ShapeEditor::ClearStoredShapes()
{
	for (;storedShapes.size() > 0;)
	{
		delete storedShapes[0];
		storedShapes.erase(storedShapes.begin());
	}
	storedShapes.clear();
}

void ShapeEditor::RenderEditedShape()
{
	if (currentShape != 0)
		MyWindow->Draw(currentShapeSprite);

	for (unsigned int t = 0; t < editedLines.size(); t++)
	{
		sf::Shape line = sf::Shape::Line(sf::Vector2f(editedLines[t]->begin.x, editedLines[t]->begin.y), sf::Vector2f(editedLines[t]->end.x, editedLines[t]->end.y), 2, sf::Color::Red);
		MyWindow->Draw(line);
	}
}

void ShapeEditor::ClearEditedShapes()
{
	for (;editedb2Shapes.size() > 0;)
	{
		delete editedb2Shapes[0];
		editedb2Shapes.erase(editedb2Shapes.begin());
	}
	editedb2Shapes.clear();
}

void ShapeEditor::ProcessClickEvent( Rocket::Core::Event& event )
{
	// WINDOW DIALOG OPENING
	//////////////////////////////////////////////////////////////////////////
	if (event.GetCurrentElement()->GetId() == "open_new_set")
	{
		Context->GetRootElement()->GetElementById("add_set")->SetProperty("display", "block");
		Context->GetRootElement()->GetElementById("add_set")->Focus();
	}
	if (event.GetCurrentElement()->GetId() == "open_new_shape")
	{
		Context->GetRootElement()->GetElementById("add_shape")->SetProperty("display", "block");
		Context->GetRootElement()->GetElementById("add_shape")->Focus();
	}
	//////////////////////////////////////////////////////////////////////////
	// ACCEPT - CANCEL BUTTONS
	//////////////////////////////////////////////////////////////////////////

	if (event.GetCurrentElement()->GetId() == "accept_add_set") {
		Rocket::Core::ElementDocument* options_body = event.GetTargetElement()->GetOwnerDocument();
		Rocket::Controls::ElementFormControlInput* opt = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("set_name"));
		NewSet(opt->GetValue().CString());
		opt->SetValue("");
		Context->GetRootElement()->GetElementById("add_set")->SetProperty("display", "none");
	}
	else if(event.GetCurrentElement()->GetId() == "cancel_add_set") {
		Rocket::Core::ElementDocument* options_body = event.GetTargetElement()->GetOwnerDocument();
		Rocket::Controls::ElementFormControlInput* opt = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("set_name"));
		Context->GetRootElement()->GetElementById("add_set")->SetProperty("display", "none");
		opt->SetValue("");
	}

	if (event.GetCurrentElement()->GetId() == "accept_add_shape") {
		Rocket::Core::ElementDocument* options_body = event.GetTargetElement()->GetOwnerDocument();
		Rocket::Controls::ElementFormControlInput* opt = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("shape_name"));
		Rocket::Controls::ElementFormControlInput* opt2 = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("img_name"));
		Context->GetRootElement()->GetElementById("add_shape")->SetProperty("display", "none");
		Shapes* newShape = new Shapes(currentSet, opt2->GetValue().CString(), opt->GetValue().CString());
		storedShapes.push_back(newShape);
		opt->SetValue("");
		opt2->SetValue("");
	}
	else if(event.GetCurrentElement()->GetId() == "cancel_add_shape") {
		Rocket::Core::ElementDocument* options_body = event.GetTargetElement()->GetOwnerDocument();
		Rocket::Controls::ElementFormControlInput* opt = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("shape_name"));
		Rocket::Controls::ElementFormControlInput* opt2 = dynamic_cast< Rocket::Controls::ElementFormControlInput* >(options_body->GetElementById("img_name"));
		Context->GetRootElement()->GetElementById("add_shape")->SetProperty("display", "none");
		opt->SetValue("");
		opt2->SetValue("");
	}
}

void ShapeEditor::RegisterListeners()
{
	RegisterClickListener(Context->GetRootElement()->GetElementById("open_new_set"));
	RegisterClickListener(Context->GetRootElement()->GetElementById("open_new_shape"));
	RegisterClickListener(Context->GetRootElement()->GetElementById("accept_add_shape"));
	RegisterClickListener(Context->GetRootElement()->GetElementById("cancel_add_shape"));

	RegisterClickListener(Context->GetRootElement()->GetElementById("accept_add_set"));
	RegisterClickListener(Context->GetRootElement()->GetElementById("cancel_add_set"));
}

void ShapeEditor::ClearEditedLines()
{
	for (;editedLines.size() > 0;)
	{
		delete editedLines[0];
		editedLines.erase(editedLines.begin());
	}
	editedLines.clear();
}