package meshTools;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.media.opengl.GL;
import javax.media.opengl.GLAutoDrawable;
import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

/**
 * Half edge data structure.
 * Maintains a list of faces (i.e., one half edge of each) to allow
 * for easy display of geometry.
 */
public class HEDS {

    /**
     * List of faces where each face is defined implicity by a half edge.
     */
    public List<HalfEdge> faces = new ArrayList<HalfEdge>();
    
    /** the map which is a half edge data structure
     * 
     * 
     */
    Map<String,HalfEdge> theMap = new HashMap<String,HalfEdge>();
    
    /**
     * Constructs an empty mesh (used when building a mesh with subdivision)
     */
    public HEDS() {
        // do nothing
    }
        
    /**
     * Builds a half edge data structure from the polygon soup   
     * @param soup
     */
    public HEDS( PolygonSoup soup ) {
    	/**
    	 *  a half edge knows 3 things for now, and those are the twin next and head.  We have a Map() which
    	 *  is organizing the half edges.  We are going to take the Map() as the half edge data structure.
    	 */
    	String newHeadTail;
    	String headTail;
    	String thePrev;
    	String twinHeadTail;
    	int[] theVerts= new int[4];
    	HalfEdge anEdge= new HalfEdge();
    	
		for (int k=0;k< soup.faceList.size();k++){
			theVerts[0]=soup.faceList.get(k)[0];
			theVerts[1]=soup.faceList.get(k)[1];
			theVerts[2]=soup.faceList.get(k)[2];
			theVerts[3]=soup.faceList.get(k)[0];
			for (int i=0; i<=2; i++){
				headTail=""+theVerts[i]+","+theVerts[i+1];
				twinHeadTail=""+theVerts[i+1]+","+theVerts[i]; //invert the nodes to get the twin mapKey
        		theMap.put(headTail, new HalfEdge());
        		theMap.get(headTail).head=soup.vertexList.get(theVerts[i+1]);
        		if(i>0){
        			thePrev=""+theVerts[i-1]+","+theVerts[i];
        			theMap.get(thePrev).next=theMap.get(headTail);
        		}
        		if(i==2){
        			thePrev=headTail;
        			newHeadTail=""+theVerts[0]+","+theVerts[1];
        			theMap.get(thePrev).next=theMap.get(newHeadTail);
        		}
        		if( theMap.containsKey(twinHeadTail)){
        			theMap.get(twinHeadTail).twin=theMap.get(headTail); 
        			theMap.get(headTail).twin=theMap.get(twinHeadTail);
        		}
        		if (i==0){
        			faces.add(theMap.get(headTail));
        		}
 
			}
		}		
		
    	
    
        // TODO implement this method!
               
    }
    
    
    
    /**
     * Draws the half edge data structure by drawing each of its faces.
     * Per vertex normals are used to draw the smooth surface when available,
     * otherwise a face normal is computed. 
     * @param drawable
     */
    public void display(GLAutoDrawable drawable) {
        GL gl = drawable.getGL();

        // assume triangular faces (we're doing loop after all!
        Vector3d v1 = new Vector3d();
        Vector3d v2 = new Vector3d();
        Vector3d n = new Vector3d();
        gl.glBegin( GL.GL_TRIANGLES );
        for ( HalfEdge he: faces ) {
            Point3d p0 = he.head.p;
            Point3d p1 = he.next.head.p;
            Point3d p2 = he.next.next.head.p;
            if ( he.head.n == null ) {
                v1.sub( p1,p0 );
                v2.sub( p2,p1 );
                n.cross( v1, v2 );            
                gl.glNormal3d( n.x, n.y, n.z );
                gl.glVertex3d( p0.x, p0.y, p0.z );
                gl.glVertex3d( p1.x, p1.y, p1.z );
                gl.glVertex3d( p2.x, p2.y, p2.z );
            } else {
                Vector3d n0 = he.head.n;
                Vector3d n1 = he.next.head.n;
                Vector3d n2 = he.next.next.head.n;
                gl.glNormal3d( n0.x, n0.y, n0.z );
                gl.glVertex3d( p0.x, p0.y, p0.z );
                gl.glNormal3d( n1.x, n1.y, n1.z );
                gl.glVertex3d( p1.x, p1.y, p1.z );
                gl.glNormal3d( n2.x, n2.y, n2.z );
                gl.glVertex3d( p2.x, p2.y, p2.z );
            }
        }
        gl.glEnd();
    }
    
    /** 
     * Draws all child vertices to help with debugging and evaluation.
     * (this will draw each points multiple times)
     * @param drawable
     */ 
    public void drawChildVertices( GLAutoDrawable drawable ) {
        GL gl = drawable.getGL();
        gl.glDisable( GL.GL_LIGHTING );
        gl.glPointSize(3);
        gl.glBegin( GL.GL_POINTS );
        for ( HalfEdge he: faces ) {
            HalfEdge loop = he;
            do {
                if ( loop.head.child != null ) {
                    Point3d p = loop.head.child.p;
                    gl.glColor3f(1,0,0);
                    gl.glVertex3d( p.x, p.y, p.z );
                }      
                if ( loop.child1 != null && loop.child1.head != null ) {
                    Point3d p = loop.child1.head.p;
                    gl.glColor3f(0,1,0);
                    gl.glVertex3d( p.x, p.y, p.z );
                }
                loop = loop.next;
            } while ( loop != he );
        }
        gl.glEnd();
        gl.glEnable( GL.GL_LIGHTING );
    }
}
