/*********************************************************************
 * This file is part of the PRAPI library.
 *
 * Copyright (C) 2001-2002 Topi Mäenpää
 * Copyright (C) 2001 Jaakko Viertola
 * All rights reserved.
 *
 * This program is free software. You can redistribute and/or modify
 * it under the terms of the free software licence found in the
 * accompanying file "COPYING". The licence terms must always be
 * redistributed with this source file. The above copyright notice
 * must be reproduced in all modified and unmodified copies of this
 * source file.
 *
 * $Revision: 1.2 $
 *********************************************************************/

#ifndef _POINT_H
#define _POINT_H

#include <SmartPtr.h>
#include <String.h>
#include <xml/XMLParser.h>
#include <io/IO.h>

namespace prapi { namespace graphics {
	/**
	 * A class for storing 2D coordinates. This is mainly a convenience
	 * class for bundling two integer values into an easily-accessible
	 * package.
	 **/
	template <class T> class Point : virtual public util::Object
	{
	public:
		/**
		 * Initialize a point with the given coordinates.
		 **/
		Point(T xOrigin=0,T yOrigin=0) : x(xOrigin), y(yOrigin) {}
		/**
		 * Copy a point.
		 **/
		Point(const Point& other) { copy(other); }

		/**
		 * Make a copy of a point.
		 **/
		Point& operator= (const Point& other) { copy(other); return *this; }

		/**
		 * The x coordinate.
		 **/
		T x;
		/**
		 * The y coordinate.
		 **/
		T y;

		/**
		 * Multiply two points coordinate-by-coordinate.
		 **/
		Point& operator*= (const Point& other) { x *= other.x; y*=other.y; return *this; }
		/**
		 * Divide two points coordinate-by-coordinate.
		 **/
		Point& operator/= (const Point& other) { x /= other.x; y/=other.y; return *this; }
		/**
		 * Add coordinates to this point.
		 **/
		Point& operator+= (const Point& other) { x += other.x; y+=other.y; return *this; }
		/**
		 * Subtract coordinates from this point.
		 **/
		Point& operator-= (const Point& other) { x -= other.x; y-=other.y; return *this; }

		/**
		 * Multiply coordinates by a value.
		 **/
		Point& operator*= (T value) { x *= value; y*=value; return *this; }
		/**
		 * Divide coordinates by a value.
		 **/
		Point& operator/= (T value) { x /= value; y/=value; return *this; }
		/**
		 * Add a value to both coordinates.
		 **/
		Point& operator+= (T value) { x += value; y+=value; return *this; }
		/**
		 * Subtract a value from both coordinates.			 
		 **/
		Point& operator-= (T value) { x -= value; y-=value; return *this; }

	private:
		void copy(const Point& other) { x=other.x; y=other.y; }
	};

	typedef Point<int> IntegerPoint;
	typedef Point<float> FloatPoint;
	typedef Point<double> DoublePoint;
	
	/**
	 * Compare two points.
	 **/
	template <class T> bool operator== (const Point<T>& p1, const Point<T>& p2)
	{
		return ((p1.x==p2.x)&&(p1.y==p2.y));
	}

	/**
	 * Multiply two points coordinate-by-coordinate.
	 **/
	template <class T> Point<T> operator* (const Point<T>& p1, const Point<T>& p2)
	{
		Point<T> result(p1);
		result.x *= p2.x;
		result.y *= p2.y;
		return result;
	}
	/**
	 * Divide two points coordinate-by-coordinate.
	 **/
	template <class T> Point<T> operator/ (const Point<T>& p1, const Point<T>& p2)
	{
		Point<T> result(p1);
		result.x /= p2.x;
		result.y /= p2.y;
		return result;
	}
	/**
	 * Sum up coordinates.
	 **/
	template <class T> Point<T> operator+ (const Point<T>& p1, const Point<T>& p2)
	{
		Point<T> result(p1);
		result.x += p2.x;
		result.y += p2.y;
		return result;
	}
	/**
	 * Subtract coordinates.
	 **/
	template <class T> Point<T> operator- (const Point<T>& p1, const Point<T>& p2)
	{
		Point<T> result(p1);
		result.x -= p2.x;
		result.y -= p2.y;
		return result;
	}
	/**
	 * Multiply both coordinates by value.
	 **/
	template <class T> Point<T> operator* (const Point<T>& p1, T value)
	{
		Point<T> result(p1);
		result.x *= value;
		result.y *= value;
		return result;
	}
	/**
	 * Divide both coordinates by value.
	 **/
	template <class T> Point<T> operator/ (const Point<T>& p1, T value)
	{
		Point<T> result(p1);
		result.x /= value;
		result.y /= value;
		return result;
	}
	/**
	 * Add a value to both coordinates.
	 **/
	template <class T> Point<T> operator+ (const Point<T>& p1, T value)
	{
		Point<T> result(p1);
		result.x += value;
		result.y += value;
		return result;
	}
	/**
	 * Subtract a value from both coordinates.
	 **/
	template <class T> Point<T> operator- (const Point<T>& p1, T value)
	{
		Point<T> result(p1);
		result.x -= value;
		result.y -= value;
		return result;
	}

	template <class T> std::ostream& operator<< (std::ostream& sout, const Point<T>& point)
	{
		sout << "<point x='" << point.x << "' y='" << point.y << "'/>";
		return sout;
	}

	template <class T> std::istream& operator>> (std::istream& sin, Point<T>& point)
	{
		using namespace util;
		using namespace util::xml;
		
		XMLParser parser;
		sin >> ws;
		
		SmartPtr<Node> node(parser.readFragment(sin));
		if (node.get() && node->getNodeType() == Node::ELEMENT_NODE)
			{
				Element* element = (Element*)node.get();
				const Node *child = element->getChildNode("point.x");
				if (child && child->getNodeType() == Node::ATTRIBUTE_NODE)
					point.x = String::parse<T>(((Attr*)child)->value);
				else
					throw io::IOException("operator>>(istream&,Point&): Input stream does not contain a properly formatted Point object.");
				child = element->getChildNode("point.y");
				if (child && child->getNodeType() == Node::ATTRIBUTE_NODE)
					point.y = String::parse<T>(((Attr*)child)->value);
				else
					throw io::IOException("operator>>(istream&,Point&): Input stream does not contain a properly formatted Point object.");
			}
		return sin;
	}
}}

#endif
