import java.awt.*;

import java.io.*;
import java.util.*;

/**
 * Reads a 3D object (comprised iof triangles) from a file
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class TriFileReader
{
    private BufferedReader       in;    
    private Vector<Color>        backColors, frontColors;
    private Vector<double[]>     coordinates, normals;    
    private Vector<int[]>        triangles;    


    /**
     * Explicit Value Constructor
     *
     * @param name  The name of the file to read from
     */
    public TriFileReader(String name) throws IOException
    {
       in = new BufferedReader(
          new InputStreamReader(
             new FileInputStream(name)));

       backColors  = new Vector<Color>();
       frontColors = new Vector<Color>();
       coordinates = new Vector<double[]>();
       normals     = new Vector<double[]>();
       triangles   = new Vector<int[]>();
       
       
       read();       
    }
    

    /**
     * Get the colors for the backs of the triangles
     *
     * @return  The array of Color objects
     */
    public Color[] getBackColors()
    {
       return toArray(backColors);       
    }

    
    /**
     * Get all of the coordinates used to define
     * the object.
     *
     * Note: The coordinates are in row-major form.
     * That is, each row of the matrix corresponds to
     * a point.
     *
     * @return  The coordinates in row-major form
     */
    public double[][] getCoordinates()
    {
       return toArray(coordinates);
    }
    

    /**
     * Get the colors for the fronts of the triangles
     *
     * @return  The array of Color objects
     */
    public Color[] getFrontColors()
    {
       return toArray(frontColors);       
    }


    /**
     * Get the normals for each vertex.
     *
     * Note: The normals are in the same order
     * as the coordinates.
     */
    public double[][] getNormals()
    {
       return toArray(normals);
    }
    
    
    /**
     * Get the vertices of the faces
     *
     * Note: Each row in the matrix corresponds
     * to a face.  Each value is a pointer into the
     * matrix of coordinates (i.e., corresponds to a 
     * row in the coordinates matrix)
     *
     * @return The vertices of the faces
     */
    public int[][] getTriangles()
    {
       return toArray(triangles);       
    }
    

    /**
     * Read the model
     */
    private void read()
    {
       boolean       keepReading;       
       int           n;       
       int[]         triangle;       
       String[]      lines;
       
       keepReading = true;       
       n           = 0;       
       lines       = new String[4];       

       while (keepReading)
       {
          try
          {
             in.readLine(); // Triangle Indicator
          }
          catch (IOException ioe)
          {
             keepReading = false;             
          }
          
          
          for (int i=0; i<lines.length && keepReading; i++)
          {
             try
             {
                lines[i] = in.readLine();
                if (lines[i] == null) keepReading = false;
             }
             catch (IOException ioe)
             {
                keepReading = false;                
             }
          }
          
          if (keepReading)
          {
             parseLines(lines);                
             triangle = new int[3];
             for (int v=0; v<triangle.length; v++)
             {
                triangle[v] = n*3 + v;
             }
             triangles.add(triangle);                
             n++;                
          }
       }
    }


    /**
     * Parse a String representation of a Color
     *
     * @param st  The String tokenizer containing the tokens
     */
    private Color parseColor(StringTokenizer st)
    {
       Color               c;       
       int[]               rgb;       
       String              token;       

       rgb = new int[3];
       for (int i=0; i<rgb.length; i++)
       {
          try
          {
             token = st.nextToken();
             rgb[i] = Integer.parseInt(token);          
          }
          catch (Exception e)
          {
             rgb[i] = 0;             
          }
       }
       c = new Color(rgb[0], rgb[1], rgb[2]);

       return c;       
    }
    
    

    /**
     * Parse a group of lines corresponding to an
     * individual triangle
     *
     * @param lines   The lines
     */
    private void parseLines(String[] lines)
    {
       StringTokenizer     st;
       
       st = new StringTokenizer(lines[0]);
       frontColors.add(parseColor(st));
       backColors.add(parseColor(st));

       for (int i=1; i<=3; i++)
       {
          st = new StringTokenizer(lines[i]);
          coordinates.add(parseVector3D(st));
          normals.add(parseVector3D(st));
       }
    }
    

    /**
     * Parse a String representation of a 3D point/vector
     *
     * @param st  The String tokenizer containing the tokens
     * @return    The point/vector in homogenous coordinates
     */
    private double[] parseVector3D(StringTokenizer st)
    {
       double[]            p;       
       String              token;       

       p = new double[4];
       for (int i=0; i<p.length-1; i++)
       {
          try
          {
             token = st.nextToken();
             p[i] = Double.parseDouble(token);          
          }
          catch (Exception e)
          {
             p[i] = 0.0;             
          }
       }
       p[3] = 1.0; // Homogeneous coordinates

       return p;       
    }


    /**
     * Create an array of Color objects from a 
     * Vector of Color objects
     */
    private static Color[] toArray(Vector<Color> v)
    {
       Color[]                 result;       
       Enumeration<Color>      e;
       int                     i;       

       result = new Color[v.size()];

       i = 0;       
       e = v.elements();
       while (e.hasMoreElements())
       {
          result[i] = e.nextElement();          
          i++;          
       }

       return result;
    }
    


    /**
     * Create an array of double[] objects from a 
     * Vector of double[] objects
     */
    private static double[][] toArray(Vector<double[]> v)
    {       
       double[][]              result;       
       Enumeration<double[]>   e;
       int                     i;       

       result = new double[v.size()][];

       i = 0;       
       e = v.elements();
       while (e.hasMoreElements())
       {
          result[i] = e.nextElement();          
          i++;          
       }

       return result;
    }



    /**
     * Create an array of int[] objects from a 
     * Vector of int[] objects
     */
    private static int[][] toArray(Vector<int[]> v)
    {       
       Enumeration<int[]>      e;
       int                     i;       
       int[][]                 result;       

       result = new int[v.size()][];

       i = 0;       
       e = v.elements();
       while (e.hasMoreElements())
       {
          result[i] = e.nextElement();          
          i++;          
       }

       return result;
    }
    
}
