/*
 *  Alex Mathews, amathews@jhu.edu
 *  Pranav Lakshminarayanan, plakshm1@jhu.edu
 *  Computer Integrated Surgery I - Fall 2014
 */
package baseclasses;

import baseclasses.Point;
import java.util.Arrays;

/**
 *
 * @author Alex
 */
public class Triangle {
    
    //Global Variables========================================================//
    
    /**
     * Points that represent the vertices of the triangle. 
     * Always initialized to length of 3.
     * Values assigned in Constructor. 
     */
    private Point[] vertices;
    
    /**
     * Indices of points that represent the vertices of the triangle. 
     * Always initialized to length of 3.
     * Values assigned in Constructor. 
     */
    private final int[] vertInd;
    
    /**
     * Triangles that represent the neighbors of the triangle.
     * Always initialized to length of 3.
     * Values assigned in Constructor. 
     */
    private Triangle[] neighbors;
    
    /**
     * Indices of triangles that represent the neighbors of the triangle.
     * Always initialized to length of 3.
     * Values assigned in Constructor. 
     */
    private final int[] neighInd;
    
    /**
     * Index of the current triangle.
     */
    private final int index;
    
    /**
     * Radius of the circle. 
     */
    private double radius;
    
    /**
     * Centroid of the Triangle.
     */
    private Point centroid;
    
    //========================================================================//
    
    
    //Constructors============================================================//

    /**
     * Initialize a triangle with given vertices as vertices.
     * @param ind index of the triangle.
     * @param ptInd indices of the vertices.
     */
    public Triangle(int ind, int[] ptInd) {
        this.index = ind;
        this.vertInd = ptInd;
        this.neighInd = null;
    }
    
    /**
     * Initialize a triangle with given vertices as vertices.
     * @param ind index of the triangle. 
     * @param pts given vertices for vertices.
     * @param ptInd indices of the vertices.
     * @param neigh give triangles for neighbors.
     * @param triInd indices of neighbors.
     */
    public Triangle(int ind, int[] ptInd, Point[] pts, int[] triInd, 
            Triangle[] neigh) {
        this.index = ind;
        this.vertices = pts;
        this.centroid = Point.calculateAvgPt(pts);
        this.neighbors = neigh;
        this.vertInd = ptInd;
        this.neighInd = triInd;
        this.radius = this.calculateRadius();
    }
    
    /**
     * Initialize a triangle with given vertices as vertices.
     * @param ind index of the triangle. 
     * @param pts given vertices for vertices.
     * @param ptInd indices of the vertices.
     * @param triInd indices of neighbors.
     */
    public Triangle(int ind, Point[] pts, int[] ptInd, int[] triInd) {
        this.index = ind;
        this.vertices = pts;
        this.vertInd = ptInd;
        this.neighInd = triInd;
        this.radius = this.calculateRadius();
    }
    
    //========================================================================//
    
    
    //Accessors===============================================================//
    
    /**
     * Return the Points used as vertices.
     * @return an array of Points.
     */
    public Point[] getVertices() {
        if (this.vertices == null) {
            throw new IllegalStateException("Vertices have not been set yet."
                    + " Please set using indicies.");
        }
        return this.vertices;
    }
    
    /**
     * Give the Triangle a set of Points to use as vertices.
     * @param verts vertices of the triangle. Must be of length 3.
     */
    public void setVertices(Point[] verts) {
        if (verts.length != 3) {
            throw new IllegalArgumentException("Array must be of length 3");
        } 
        this.vertices = verts;
        this.centroid = Point.calculateAvgPt(verts);
        this.radius = this.calculateRadius();
    }
    
    /**
     * Return the indices of the Points used as vertices.
     * @return an array of Points.
     */
    public int[] getVertIndices() {
        return this.vertInd;
    }
          
    /**
     * Return the Triangles that are neighbors.
     * @return an array of Points.
     */
    public Triangle[] getNeighbors() {
        if (this.neighbors == null) {
            throw new IllegalStateException("Neighbors have not been set yet."
                    + " Please set using indicies.");
        }
        return this.neighbors;
    }
    
    /**
     * Give the Triangle a set of Triangles to use as vertices.
     * @param neighs neighbors of the triangle. Must be of length 3.
     */
    public void setNeighbors(Triangle[] neighs) {
        if (neighs.length != 3) {
            throw new IllegalArgumentException("Array must be of length 3");
        } 
        this.neighbors = neighs;
    }
         
    /**
     * Return the indices of the Triangles used as vertices.
     * @return an array of Points.
     */
    public int[] getNeighIndices() {
        return this.neighInd;
    }
      
    
    /**
     * Get the index of the current triangle.
     * @return an int representing the index.
     */
    public int getTriangleIndex() {
        return this.index;
    }
    
    /**
     * Get the centroid of each triangle's vertices.
     * @return a centroid Point.
     */
    public Point getCentroid() {
        return this.centroid;
    }
    
    /**
     * Get the radius of the triangle.
     * @return the radius.
     */
    public double getRadius() {
        return Math.abs(this.radius);
    }
    //========================================================================//
    
    
    //Methods=================================================================//
    
    /**
     * Calculate the radius of bounding sphere.
     * @return a double representing the radius.
     */
    private double calculateRadius() {
        if (this.vertices == null) {
            throw new IllegalStateException();
        }
        double a = Point.magnitude(Point.subtract(this.centroid, 
                this.vertices[0]));
        double b = Point.magnitude(Point.subtract(this.centroid, 
                this.vertices[1]));
        double c = Point.magnitude(Point.subtract(this.centroid, 
                this.vertices[2]));
        double fMax = Math.max(a, b);
        double max = Math.max(fMax, c);
        return max;
    }
    
    //========================================================================//
    
    
    //Print===================================================================//
    
    /**
     * Print the data of the current triangle.
     */
    public void printTriangle() {
        String print = "Index: " + this.index;
        print += "\nVertices:";
        for (int i = 0; i < 3; i++) {
            print += "\n" + this.vertInd[i];
            if (this.vertices != null) {
                print += " {";
                print += this.vertices[i].toString();
                print += "}";
            }
        }
        if (Arrays.deepEquals(this.neighbors, new Triangle[3])) {
            print += "\nNo valid neighbors.";
        } else if (this.neighbors == null) {
            print += "\nNo valid neighbors.";
        } else {
            print += "\nNeighbors:";
            for (int i = 0; i < 3; i++) {
                print += "\n" + this.neighInd[i];
            }
        }
        
        System.out.println(print);
    }
    
}