package org.sunflow.math;

public final class Point3 {
    public float x, y, z;

    public Point3() {
    }

    public Point3(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Point3(Point3 p) {
        x = p.x;
        y = p.y;
        z = p.z;
    }

    public float get(int i) {
        switch (i) {
            case 0:
                return x;
            case 1:
                return y;
            default:
                return z;
        }
    }

    public final float distanceTo(Point3 p) {
        float dx = x - p.x;
        float dy = y - p.y;
        float dz = z - p.z;
        return (float) Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
    }

    public final float distanceTo(float px, float py, float pz) {
        float dx = x - px;
        float dy = y - py;
        float dz = z - pz;
        return (float) Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
    }

    public final float distanceToSquared(Point3 p) {
        float dx = x - p.x;
        float dy = y - p.y;
        float dz = z - p.z;
        return (dx * dx) + (dy * dy) + (dz * dz);
    }

    public final float distanceToSquared(float px, float py, float pz) {
        float dx = x - px;
        float dy = y - py;
        float dz = z - pz;
        return (dx * dx) + (dy * dy) + (dz * dz);
    }

    public final Point3 set(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
        return this;
    }

    public final Point3 set(Point3 p) {
        x = p.x;
        y = p.y;
        z = p.z;
        return this;
    }

    public static final Point3 add(Point3 p, Vector3 v, Point3 dest) {
        dest.x = p.x + v.x;
        dest.y = p.y + v.y;
        dest.z = p.z + v.z;
        return dest;
    }

    public static final Vector3 sub(Point3 p1, Point3 p2, Vector3 dest) {
        dest.x = p1.x - p2.x;
        dest.y = p1.y - p2.y;
        dest.z = p1.z - p2.z;
        return dest;
    }

    public static final Point3 mid(Point3 p1, Point3 p2, Point3 dest) {
        dest.x = 0.5f * (p1.x + p2.x);
        dest.y = 0.5f * (p1.y + p2.y);
        dest.z = 0.5f * (p1.z + p2.z);
        return dest;
    }

    public static final Point3 blend(Point3 p0, Point3 p1, float blend, Point3 dest) {
        dest.x = (1 - blend) * p0.x + blend * p1.x;
        dest.y = (1 - blend) * p0.y + blend * p1.y;
        dest.z = (1 - blend) * p0.z + blend * p1.z;
        return dest;
    }

    public static final Vector3 normal(Point3 p0, Point3 p1, Point3 p2) {
        float edge1x = p1.x - p0.x;
        float edge1y = p1.y - p0.y;
        float edge1z = p1.z - p0.z;
        float edge2x = p2.x - p0.x;
        float edge2y = p2.y - p0.y;
        float edge2z = p2.z - p0.z;
        float nx = edge1y * edge2z - edge1z * edge2y;
        float ny = edge1z * edge2x - edge1x * edge2z;
        float nz = edge1x * edge2y - edge1y * edge2x;
        return new Vector3(nx, ny, nz);
    }

    public static final Vector3 normal(Point3 p0, Point3 p1, Point3 p2, Vector3 dest) {
        float edge1x = p1.x - p0.x;
        float edge1y = p1.y - p0.y;
        float edge1z = p1.z - p0.z;
        float edge2x = p2.x - p0.x;
        float edge2y = p2.y - p0.y;
        float edge2z = p2.z - p0.z;
        dest.x = edge1y * edge2z - edge1z * edge2y;
        dest.y = edge1z * edge2x - edge1x * edge2z;
        dest.z = edge1x * edge2y - edge1y * edge2x;
        return dest;
    }

    @Override
    public final String toString() {
        return String.format("(%.2f, %.2f, %.2f)", x, y, z);
    }
}