/*
 * Copyright (c) 2012 Mateusz Piękos
 * Licensed under the simplified BSD license.
 * See Documentation/Licenses/BSD-simplified.txt for more information.
 */

#include <Swiften/Serializer/PayloadSerializers/WhiteboardSerializer.h>

#include <iostream>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/lexical_cast.hpp>
#include <Swiften/Serializer/XML/XMLTextNode.h>
#include <Swiften/Elements/Whiteboard/WhiteboardInsertOperation.h>
#include <Swiften/Elements/Whiteboard/WhiteboardUpdateOperation.h>
#include <Swiften/Elements/Whiteboard/WhiteboardDeleteOperation.h>

namespace Swift {
	void WhiteboardElementSerializingVisitor::visit(WhiteboardLineElement& line) {
		element = boost::make_shared<XMLElement>("line");
		try {
			element->setAttribute("x1", boost::lexical_cast<std::string>(line.x1()));
			element->setAttribute("y1", boost::lexical_cast<std::string>(line.y1()));
			element->setAttribute("x2", boost::lexical_cast<std::string>(line.x2()));
			element->setAttribute("y2", boost::lexical_cast<std::string>(line.y2()));
			element->setAttribute("id", line.getID());
			element->setAttribute("stroke", line.getColor().toHex());
			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(line.getPenWidth()));
			element->setAttribute("opacity", alphaToOpacity(line.getColor().getAlpha()));
		} catch (boost::bad_lexical_cast&) {
		}
	}

	void WhiteboardElementSerializingVisitor::visit(WhiteboardFreehandPathElement& path) {
		element = boost::make_shared<XMLElement>("path");
		element->setAttribute("id", path.getID());
		element->setAttribute("stroke", path.getColor().toHex());
		try {
			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(path.getPenWidth()));
			element->setAttribute("opacity", alphaToOpacity(path.getColor().getAlpha()));
			std::string pathData;
			if (path.getPoints().size() != 0) {
				std::vector<std::pair<int, int> >::const_iterator it = path.getPoints().begin();
				pathData = "M"+boost::lexical_cast<std::string>(it->first)+" "+boost::lexical_cast<std::string>(it->second)+"L";
				for (; it != path.getPoints().end(); ++it) {
					pathData += boost::lexical_cast<std::string>(it->first)+" "+boost::lexical_cast<std::string>(it->second)+" ";
				}
			}
			element->setAttribute("d", pathData);
		} catch (boost::bad_lexical_cast&) {
		}
	}

	void WhiteboardElementSerializingVisitor::visit(WhiteboardRectElement& rect) {
		element = boost::make_shared<XMLElement>("rect");
		try {
			element->setAttribute("x", boost::lexical_cast<std::string>(rect.getX()));
			element->setAttribute("y", boost::lexical_cast<std::string>(rect.getY()));
			element->setAttribute("width", boost::lexical_cast<std::string>(rect.getWidth()));
			element->setAttribute("height", boost::lexical_cast<std::string>(rect.getHeight()));
			element->setAttribute("id", rect.getID());
			element->setAttribute("stroke", rect.getPenColor().toHex());
			element->setAttribute("fill", rect.getBrushColor().toHex());;
			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(rect.getPenWidth()));
			element->setAttribute("opacity", alphaToOpacity(rect.getPenColor().getAlpha()));
			element->setAttribute("fill-opacity", alphaToOpacity(rect.getBrushColor().getAlpha()));
		} catch (boost::bad_lexical_cast&) {
		}
	}

	void WhiteboardElementSerializingVisitor::visit(WhiteboardPolygonElement& polygon) {
		element = boost::make_shared<XMLElement>("polygon");
		try {
			element->setAttribute("id", polygon.getID());
			element->setAttribute("stroke", polygon.getPenColor().toHex());
			element->setAttribute("fill", polygon.getBrushColor().toHex());;
			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(polygon.getPenWidth()));
			element->setAttribute("opacity", alphaToOpacity(polygon.getPenColor().getAlpha()));
			element->setAttribute("fill-opacity", alphaToOpacity(polygon.getBrushColor().getAlpha()));
			std::string points;
			std::vector<std::pair<int, int> >::const_iterator it = polygon.getPoints().begin();
			for (; it != polygon.getPoints().end(); ++it) {
				points += boost::lexical_cast<std::string>(it->first)+","+boost::lexical_cast<std::string>(it->second)+" ";
			}
			element->setAttribute("points", points);
		} catch (boost::bad_lexical_cast&) {
		}
	}

	void WhiteboardElementSerializingVisitor::visit(WhiteboardTextElement& text) {
		element = boost::make_shared<XMLElement>("text");
		try {
			element->setAttribute("x", boost::lexical_cast<std::string>(text.getX()));
			element->setAttribute("y", boost::lexical_cast<std::string>(text.getY()));
			element->setAttribute("font-size", boost::lexical_cast<std::string>(text.getSize()));
			element->setAttribute("id", text.getID());
			element->setAttribute("fill", text.getColor().toHex());
			element->setAttribute("opacity", alphaToOpacity(text.getColor().getAlpha()));
			element->addNode(boost::make_shared<XMLTextNode>(text.getText()));
		} catch (boost::bad_lexical_cast&) {
		}
	}

 	void WhiteboardElementSerializingVisitor::visit(WhiteboardEllipseElement& ellipse) {
		element = boost::make_shared<XMLElement>("ellipse");
		try {
			element->setAttribute("cx", boost::lexical_cast<std::string>(ellipse.getCX()));
			element->setAttribute("cy", boost::lexical_cast<std::string>(ellipse.getCY()));
			element->setAttribute("rx", boost::lexical_cast<std::string>(ellipse.getRX()));
			element->setAttribute("ry", boost::lexical_cast<std::string>(ellipse.getRY()));
			element->setAttribute("id", ellipse.getID());
			element->setAttribute("stroke", ellipse.getPenColor().toHex());
			element->setAttribute("fill", ellipse.getBrushColor().toHex());;
			element->setAttribute("stroke-width", boost::lexical_cast<std::string>(ellipse.getPenWidth()));
			element->setAttribute("opacity", alphaToOpacity(ellipse.getPenColor().getAlpha()));
			element->setAttribute("fill-opacity", alphaToOpacity(ellipse.getBrushColor().getAlpha()));
		} catch (boost::bad_lexical_cast&) {
		}
	}

	XMLElement::ref WhiteboardElementSerializingVisitor::getResult() const {
		return element;
	}

	std::string WhiteboardElementSerializingVisitor::alphaToOpacity(int alpha) const {
		int opacity = 100*alpha/254;
		if (opacity == 100) {
			return "1";
		} else {
			return "."+boost::lexical_cast<std::string>(opacity);
		}
	}

	std::string WhiteboardSerializer::serializePayload(boost::shared_ptr<WhiteboardPayload> payload) const {
		XMLElement element("wb", "http://swift.im/whiteboard");
		if (payload->getType() == WhiteboardPayload::Data) {
			XMLElement::ref operationNode = boost::make_shared<XMLElement>("operation");
			WhiteboardElementSerializingVisitor visitor;
//			payload->getElement()->accept(visitor);
			WhiteboardInsertOperation::ref insertOp = boost::dynamic_pointer_cast<WhiteboardInsertOperation>(payload->getOperation());
			if (insertOp) {
				try {
					operationNode->setAttribute("type", "insert");
					operationNode->setAttribute("pos", boost::lexical_cast<std::string>(insertOp->getPos()));
					operationNode->setAttribute("id", insertOp->getID());
					operationNode->setAttribute("parentid", insertOp->getParentID());
				} catch (boost::bad_lexical_cast&) {
				}
				insertOp->getElement()->accept(visitor);
				operationNode->addNode(visitor.getResult());
			}
			WhiteboardUpdateOperation::ref updateOp = boost::dynamic_pointer_cast<WhiteboardUpdateOperation>(payload->getOperation());
			if (updateOp) {
				try {
					operationNode->setAttribute("type", "update");
					operationNode->setAttribute("pos", boost::lexical_cast<std::string>(updateOp->getPos()));
					operationNode->setAttribute("id", updateOp->getID());
					operationNode->setAttribute("parentid", updateOp->getParentID());
					operationNode->setAttribute("newpos", boost::lexical_cast<std::string>(updateOp->getNewPos()));
				} catch (boost::bad_lexical_cast&) {
				}
				updateOp->getElement()->accept(visitor);
				operationNode->addNode(visitor.getResult());

			}

			WhiteboardDeleteOperation::ref deleteOp = boost::dynamic_pointer_cast<WhiteboardDeleteOperation>(payload->getOperation());
			if (deleteOp) {
				try {
					operationNode->setAttribute("type", "delete");
					operationNode->setAttribute("pos", boost::lexical_cast<std::string>(deleteOp->getPos()));
					operationNode->setAttribute("id", deleteOp->getID());
					operationNode->setAttribute("parentid", deleteOp->getParentID());
					operationNode->setAttribute("elementid", deleteOp->getElementID());
				} catch (boost::bad_lexical_cast&) {
				}
			}
			element.addNode(operationNode);
		}
		element.setAttribute("type", typeToString(payload->getType()));
		return element.serialize();
	}

	std::string WhiteboardSerializer::typeToString(WhiteboardPayload::Type type) const {
		switch (type) {
			case WhiteboardPayload::Data:
				return "data";
			case WhiteboardPayload::SessionRequest:
				return "session-request";
			case WhiteboardPayload::SessionAccept:
				return "session-accept";
			case WhiteboardPayload::SessionTerminate:
				return "session-terminate";
			case WhiteboardPayload::UnknownType:
				std::cerr << "Warning: Serializing unknown action value." << std::endl;
				return "";
		}
		assert(false);
		return "";
	}
}