#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2015 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library 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
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
#ifndef NUCLEX_GEOMETRY_POINT2_H
#define NUCLEX_GEOMETRY_POINT2_H
#include "Nuclex/Geometry/Config.h"
#include "Nuclex/Geometry/Vector2.h"
namespace Nuclex { namespace Geometry {
  // ------------------------------------------------------------------------------------------- //
  /// Point in 2D space using cartesian coordinates
  /// 
  ///   
  ///     Most math libraries combine the concepts of a "point" and
  ///     a "vector" because they both store a pair of scalars and have many
  ///     equivalent operations. There is however a semantic meaning associated with a point:
  ///     it is a location, not a size and not a direction.
  ///   
  ///   
  ///     This library attempts to keep up this distinction to provide meaning without
  ///     additional documentation and to guard against errors from mistaken identity.
  ///   
  /// 
  template 
  struct Point2 {
    /// A point located at the center of the coordinate system
    public: static const Point2 Zero;
    /// Constructs a new uninitialized point
    public: Point2() {}
    /// Initializes a new point with the given coordinates
    /// X coordinate of the point
    /// Y coordinate of the point
    public: Point2(TScalar x, TScalar y) : X(x), Y(y) {}
    /// Calculates the squared distance to another point
    /// Other point to which the distance will be calculated
    /// The squared distance from this point to the other point
    public: TScalar SquaredDistanceTo(const Point2 &other) const {
      float offsetX = other.X - this->X;
      float offsetY = other.Y - this->Y;
      return (offsetX * offsetX) + (offsetY * offsetY);
    }
    /// Calculates the squared distance to another point
    /// X coordinate to which the distance will be calculated
    /// Y coordinate to which the distance will be calculated
    /// The squared distance from this point to the other point
    public: TScalar SquaredDistanceTo(TScalar x, TScalar y) const {
      float offsetX = x - this->X;
      float offsetY = y - this->Y;
      return (offsetX * offsetX) + (offsetY * offsetY);
    }
    /// Calculates the distance to another point
    /// Other point to which the distance will be calculated
    /// The distance from this point to the other point
    public: TScalar DistanceTo(const Point2 &other) const {
      return Math::SquareRoot(SquaredDistanceTo(other));
    }
    /// Calculates the distance to another point
    /// X coordinate to which the distance will be calculated
    /// Y coordinate to which the distance will be calculated
    /// The distance from this point to the other point
    public: TScalar DistanceTo(TScalar x, TScalar y) const {
      return Math::SquareRoot(SquaredDistanceTo(x, y));
    }
    /// Calculates the squared distance between two points
    /// First point for the distance calculation
    /// Second point for the distance calculation
    /// The squared distance between the two points
    public: static TScalar SquaredDistanceBetween(const Point2 &first, const Point2 &second) {
      float offsetX = first.X - second.X;
      float offsetY = first.Y - second.Y;
      return (offsetX * offsetX) + (offsetY * offsetY);
    }
    /// Calculates the distance between two points
    /// First point for the distance calculation
    /// Second point for the distance calculation
    /// The distance between the two points
    public: static TScalar DistanceBetween(const Point2 &first, const Point2 &second) {
      return Math::SquareRoot(Point2::SquaredDistanceBetween(first, second));
    }
    /// Linearly interpolates between two points
    /// Point from which the interpolation begins
    /// Point to which the interpolation leds
    /// Interpolation point between the two points
    /// The interpolated point at 
    public: static Point2 Lerp(Point2 left, Point2 right, TScalar t) {
      TScalar inverseT = 1 - t;
      return Point2(
        (left.X * inverseT) + (right.X * t),
        (left.Y * inverseT) + (right.Y * t)
      );
    }
    /// Moves the point by a specified offset
    /// Offset by which the point will be moved
    /// The point offset by the specified offset
    public: Point2 operator +(const Vector2 &offset) const {
      return Point2(this->X + offset.X, this->Y + offset.Y);
    }
    /// Moves the point against the specified offset
    /// Offset against which the point will be move
    /// The point moved against the specified offset
    public: Point2 operator -(const Vector2 &offset) const {
      return Point2(this->X - offset.X, this->Y - offset.Y);
    }
    /// Calculates the offset from this point to another
    /// Other point to which the offset is calculated
    /// The offset to the other point
    public: Vector2 operator -(const Point2 &other) const {
      return Vector2(this->X - other.X, this->Y - other.Y);
    }
    /// Moves the point by a specified offset
    /// Offset by which the point will be moved
    /// The point after being moved by the specified offset
    public: Point2 operator +=(const Vector2 &vector) {
      this->X += vector.X;
      this->Y += vector.Y;
      return *this;
    }
    /// Moves the point against the specified offset
    /// Offset against which the point will be move
    /// The point after being moved against the specified offset
    public: Point2 operator -=(const Vector2 &vector) {
      this->X -= vector.X;
      this->Y -= vector.Y;
      return *this;
    }
    /// Checks whether another point is equal to this one
    /// Other point that will be checked for equality
    /// True if the other point is equal to this one
    public: bool operator ==(const Point2 &other) const {
      return (this->X == other.X) && (this->Y == other.Y);
    }
    /// Checks whether another point is different from this one
    /// Other point that will be checked for inequality
    /// True if the other point is different to this one
    public: bool operator !=(const Point2 &other) const {
      return (this->X != other.X) || (this->Y != other.Y);
    }
    /// Location of the point on the X axis
    public: TScalar X;
    /// Location of the point on the Y axis
    public: TScalar Y;
  };
  // ------------------------------------------------------------------------------------------- //
  template
  const Point2 Point2::Zero(0, 0);
  // ------------------------------------------------------------------------------------------- //
}} // namespace Nuclex::Geometry
#endif // NUCLEX_GEOMETRY_POINT2_H