package com.srbenoit.geom;

import com.srbenoit.log.LoggedObject;

/**
 * A simple three-dimensional point.
 */
public class Point3 extends LoggedObject implements Point3Int {

    /** the X coordinate */
    private double posX;

    /** the Y coordinate */
    private double posY;

    /** the Z coordinate */
    private double posZ;

    /**
     * Constructs a point at the origin.
     */
    public Point3() {

        this.posX = 0;
        this.posY = 0;
        this.posZ = 0;
    }

    /**
     * Constructs a point.
     *
     * @param  xCoord  the X coordinate
     * @param  yCoord  the Y coordinate
     * @param  zCoord  the Z coordinate
     */
    public Point3(final double xCoord, final double yCoord, final double zCoord) {

        this.posX = xCoord;
        this.posY = yCoord;
        this.posZ = zCoord;
    }

    /**
     * Constructs a point.
     *
     * @param  source  the point whose coordinates are to be copied
     */
    public Point3(final Point3Int source) {

        this.posX = source.getPosX();
        this.posY = source.getPosY();
        this.posZ = source.getPosZ();
    }

    /**
     * Gets the X coordinate.
     *
     * @return  the X coordinate
     */
    public double getPosX() {

        return this.posX;
    }

    /**
     * Sets the X coordinate.
     *
     * @param  xCoord  the X coordinate
     */
    public void setPosX(final double xCoord) {

        this.posX = xCoord;
    }

    /**
     * Gets the Y coordinate.
     *
     * @return  the Y coordinate
     */
    public double getPosY() {

        return this.posY;
    }

    /**
     * Sets the Y coordinate.
     *
     * @param  yCoord  the Y coordinate
     */
    public void setPosY(final double yCoord) {

        this.posY = yCoord;
    }

    /**
     * Gets the Z coordinate.
     *
     * @return  the Z coordinate
     */
    public double getPosZ() {

        return this.posZ;
    }

    /**
     * Sets the Z coordinate.
     *
     * @param  zCoord  the Z coordinate
     */
    public void setPosZ(final double zCoord) {

        this.posZ = zCoord;
    }

    /**
     * Sets the coordinates of the point.
     *
     * @param  xCoord  the x coordinate
     * @param  yCoord  the y coordinate
     * @param  zCoord  the z coordinate
     */
    public void setPos(final double xCoord, final double yCoord, final double zCoord) {

        this.posX = xCoord;
        this.posY = yCoord;
        this.posZ = zCoord;
    }

    /**
     * Sets the coordinates of the point from another point.
     *
     * @param  source  the point whose position is to be copied
     */
    public void setPos(final Point3Int source) {

        this.posX = source.getPosX();
        this.posY = source.getPosY();
        this.posZ = source.getPosZ();
    }

    /**
     * Moves the coordinates of the point.
     *
     * @param  xDelta  the change to the x coordinate
     * @param  yDelta  the change to the y coordinate
     * @param  zDelta  the change to the z coordinate
     */
    public void move(final double xDelta, final double yDelta, final double zDelta) {

        this.posX += xDelta;
        this.posY += yDelta;
        this.posZ += zDelta;
    }

    /**
     * Moves the coordinates of the point by a vector.
     *
     * @param  vector  the vector by which to move the point
     */
    public void move(final Vector3Int vector) {

        this.posX += vector.getVecX();
        this.posY += vector.getVecY();
        this.posZ += vector.getVecZ();
    }

    /**
     * Adds a scaled version of a vector to this point's position (this = this + scale tuple).
     *
     * @param  scale   the scalar value
     * @param  vector  the vector to be scaled then added
     */
    public void moveScaled(final double scale, final Vector3Int vector) {

        this.posX += vector.getVecX() * scale;
        this.posY += vector.getVecY() * scale;
        this.posZ += vector.getVecZ() * scale;
    }

    /**
     * Computes the square of the Euclidean distance between this point and another point.
     *
     * @param   otherPoint  the other point
     * @return  the square of the distance
     */
    public double distSquared(final Point3Int otherPoint) {

        double distX;
        double distY;
        double distZ;

        distX = this.posX - otherPoint.getPosX();
        distY = this.posY - otherPoint.getPosY();
        distZ = this.posZ - otherPoint.getPosZ();

        return (distX * distX) + (distY * distY) + (distZ * distZ);
    }

    /**
     * Computes the Euclidean distance between this point and another point.
     *
     * @param   otherPoint  the other point
     * @return  the distance
     */
    public double dist(final Point3Int otherPoint) {

        double distX;
        double distY;
        double distZ;

        distX = this.posX - otherPoint.getPosX();
        distY = this.posY - otherPoint.getPosY();
        distZ = this.posZ - otherPoint.getPosZ();

        return Math.sqrt((distX * distX) + (distY * distY) + (distZ * distZ));
    }

    /**
     * Generates the string representation of the point.
     *
     * @return  the <code>String</code> representation
     */
    @Override public String toString() {

        StringBuilder str;

        str = new StringBuilder(50);

        str.append('(');
        str.append(this.posX);
        str.append(", ");
        str.append(this.posY);
        str.append(", ");
        str.append(this.posZ);
        str.append(')');

        return str.toString();
    }
}
