// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation; either version 2 of the License, or
//   (at your option) any later version.
//  
//   This program is distributed in the hope that it will be useful, but
//   WITHOUT ANY WARRANTY; without even the implied warranty of
//   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
//   General Public License for more details.
// 
//   You should have received a copy of the GNU General Public License
//   along with this program; if not, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#pragma once

#include <ostream>
#include <cmath>
#include <iostream>
#include <assert.h>
#include <string>

#include "../3rdparty/tinyxml/tinyxml.h"

#include "mathdefs.h"

/** @brief Vector in 2D space. (Or: Yet another vector class.)
 * Mostly inline, so all the implementation is in the header.
 * */
class Vector2D {
	public:
	
		/** @brief Constructor.
		 * @param x The x value of the vector.
		 * @param y The y value of the vector.
		 * */
		inline Vector2D(VEC_SCALAR x, VEC_SCALAR y) {
			this->x = x;
			this->y = y;
		}
		
		/** @brief Default constructor.
		 * */
		inline Vector2D(void) {
			this->x = 0;
			this->y = 0;
		}
		
		/** @brief Multiplication by scalar.
		 * @param s The scalar to multiply by.
		 * @return The vector multiplied by the scalar.
		 * */
		inline Vector2D operator*(VEC_SCALAR s) const {
			return Vector2D(x * s, y * s);
		}
		
		/** @brief Division by scalar.
		 * @param s The scalar to divide by.
		 * @return The vector divided by the scalar.
		 * */
		inline Vector2D operator/(VEC_SCALAR s) const {
			return Vector2D(x / s, y / s);
		}
		
		/** @brief Get the magnitude.
		 * @return The magnitude.
		 * */
		inline VEC_SCALAR magnitude(void) const {
			return std::sqrt(x * x + y * y);
		}
		
		/** @brief Get a normalized version of the vector.
		 * @return The normalized vector.
		 * */
		inline Vector2D normalize(void) const {
			assert(magnitude() != 0); // I'm going to leave this in here for now.
			return (*this)/magnitude();
		}
		
		/** @brief Subtraction operator.
		 * @param v The other vector to subtract.
		 * @return The result of the subtraction.
		 * */
		inline Vector2D operator-(const Vector2D &v) const {
			return Vector2D(x - v.x, y - v.y);
		}
		
		/** @brief Addition operator.
		 * @param v The other vector to add.
		 * @return The result of the addition.
		 * */
		inline Vector2D operator+(const Vector2D &v) const {
			return Vector2D(x + v.x, y + v.y);
		}
		
		/** @brief Dot product operation.
		 * @param v The other vector to dot with.
		 * @return The dot product of this vector and the other.
		 * */
		inline VEC_SCALAR dot(const Vector2D &v) const {
			return x * v.x + y * v.y;
		}
		
		/** @brief Addition and assignment operator.
		 * @param v The other vector to add.
		 * @return This vector after the operation.
		 * */
		inline Vector2D &operator+=(const Vector2D &v) {
			x += v.x;
			y += v.y;
			return *this;
		}
		
		/** @brief Subtraction and assignment operator.
		 * @param v The other vector to subtract.
		 * @return This vector after the operation.
		 * */
		inline Vector2D &operator-=(const Vector2D &v) {
			x -= v.x;
			y -= v.y;
			return *this;
		}
		
		/** @brief Multiplication by scalar and assignment operation.
		 * @param s The scalar to multiply by.
		 * @return This vector after the operation.
		 * */
		inline Vector2D &operator*=(VEC_SCALAR s) {
			x *= s;
			y *= s;
			return *this;
		}
		
		/** @brief Comparison operator.
		 * @param v The other vector to compare to.
		 * @return true if they're the same, false otherwise. 
		 * */
		inline bool operator==(const Vector2D &v) const {
			return (x == v.x && y == v.x);
		}
		
		/** @brief Checks to see if it's zero length.
		 * @return true if it's zero magnitude. false otherwise.
		 * */
		inline bool isZero(void) const {
			return x == 0 && y == 0;
		}
		
		/** @brief The x value of the vector. */
		VEC_SCALAR x;
		
		/** @brief The y value of the vector. */
		VEC_SCALAR y;
};

/** @brief Stream output operator.
 * Mainly for debugging purposes, as there is no corresponding input
 * operator. Use like: cout << vector << endl;
 * @param os The ostream to output to (ie: cout, or a file).
 * @param v The vector to output.
 * */
inline std::ostream &operator<<(std::ostream &os, const Vector2D &v) {
	os << "(" << v.x << " " << v.y << ")";
	return os;
}

/** @brief Get a vector that's perpendicular to the given one.
 * @param p The input vector.
 * @return The perpendicular vector.
 * */
inline Vector2D getPerpendicularAxis(const Vector2D &p) {
	return Vector2D(p.y, -p.x);
}

/** @brief Projects a point onto an axis.
 * Just does a dot product with some automatic normalization first.
 * @return The resulting point.
 * */
inline VEC_SCALAR projectPointOnAxis(const Vector2D &p1, const Vector2D &axis) {
	Vector2D realAxis = axis.normalize();
	return p1.x * realAxis.x + p1.y * realAxis.y;
}

/** @brief Set a Vector2D attribute in an XML element.
 * @param e The TiXmlElement to set in.
 * @param name The name of the attribute (will add _x and _y to this
 *   and split it in two).
 * @param v The vector value.
 * */
inline void setVectorInXml(TiXmlElement *e, const std::string &name, const Vector2D &v) {
	e->SetDoubleAttribute((name + "_x").c_str(), v.x);
	e->SetDoubleAttribute((name + "_y").c_str(), v.y);
}

/** @brief Get a Vector2D from an attribute in an XML element.
 * @param e The TiXmlElement.
 * @param name The name of the attribute.
 * @return The saved Vector2D.
 * */
inline Vector2D getVectorFromXml(TiXmlElement *e, const std::string &name) {
	double x = 0;
	double y = 0;
	e->QueryDoubleAttribute((name + "_x").c_str(), &x);
	e->QueryDoubleAttribute((name + "_y").c_str(), &y);
	
	return Vector2D(VEC_SCALAR(x), VEC_SCALAR(y));
}
