package math.linear;

import math.utils.Tuple3d;

public class Vector3 {
    private double x, y, z;

    public Vector3() {
        this(0.0, 0.0, 0.0);
    }

    public Vector3(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Vector3(double[] array) throws VectorException {
        if (array.length == 3) {
            x = array[0];
            y = array[1];
            z = array[2];
        } else {
            throw new VectorException("Niewłaściwa długość tablicy");
        }
    }

    public Vector3(Vector3 vector) {
        x = vector.x;
        y = vector.y;
        z = vector.z;
    }

    public void add(Vector3 vector) {
        this.x += vector.x;
        this.y += vector.y;
        this.z += z;
    }

    public void sub(Vector3 vector) {
        this.x -= vector.x;
        this.y -= vector.y;
        this.z -= vector.z;
    }

    public void scale(double s) {
        this.x *= s;
        this.y *= s;
        this.z *= s;
    }

    public void cross(Vector3 vector) {
        x = y * vector.z - z * vector.y;
        y = z * vector.x - x * vector.z;
        z = x * vector.y - y * vector.x;
    }

    public Vector3 surface(Vector3 vector) {
        return LinearUtil.normalize(LinearUtil.cross(this, vector));
    }

    public void normalize() {
        x /= getLength();
        y /= getLength();
        z /= getLength();
    }

    public double dot(Vector3 vector) {
        return (x * vector.x + y * vector.y + z * vector.z);
    }

    public double lengthSquared() {
        return (x * x + y * y + z * z);
    }

    public double getLength() {
        return Math.sqrt(x * x + y * y + z * z);
    }

    public void setLength(double length) {
        double r = this.getLength();
        if (r != 0.0) {
            scale(length / r);
        } else {
            this.x = length;
        }
    }

    public double angleBetween(Vector3 vector) {
        double val = dot(vector) / (getLength() * vector.getLength());
        return ((Math.acos(val)));
    }

    public double getX() {
        return x;
    }

    public void setX(double x) {
        this.x = x;
    }

    public double getY() {
        return y;
    }

    public void setY(double y) {
        this.y = y;
    }

    public double getZ() {
        return z;
    }

    public void setZ(double z) {
        this.z = z;
    }

    public void set(double x, double y, double z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    @Override
    public String toString() {
        return "[" + x + ", " + y + ", " + z + "]";
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        if (!(obj instanceof Vector3)) {
            return false;
        }
        Vector3 t2 = (Vector3) obj;
        return (x == t2.getX()) && (y == t2.getY() && z == t2.getZ());
    }

    @Override
    public int hashCode() {
        return 17 * Double.valueOf(x).hashCode() + 19 * Double.valueOf(y).hashCode()
                * 23 * Double.valueOf(z).hashCode();
    }

    public Tuple3d toTuple3d() {
        return new Tuple3d(x, y, z);
    }
}
