/*
 * $RCSfile: GeneralizedVertexList.java,v $
 *
 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * - Redistribution of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * - Redistribution in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in
 *   the documentation and/or other materials provided with the
 *   distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any
 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or
 * intended for use in the design, construction, operation or
 * maintenance of any nuclear facility.
 *
 * $Revision: 1.3 $
 * $Date: 2007/02/09 17:20:22 $
 * $State: Exp $
 */

package com.sun.j3d.utils.geometry.compression;

import java.util.ArrayList;
import javax.media.j3d.GeometryArray;
import javax.media.j3d.GeometryStripArray;
import javax.media.j3d.LineStripArray;
import javax.media.j3d.PointArray;
import javax.media.j3d.TriangleArray;
import javax.media.j3d.TriangleFanArray;
import javax.media.j3d.TriangleStripArray;
import javax.vecmath.Color3f;
import javax.vecmath.Color4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3f;

/**
 * The GeneralizedVertexList class is a variable-size list used to
 * collect the vertices for a generalized strip of points, lines, or
 * triangles.  This is used by the GeometryDecompressor.  This class
 * implements the GeneralizedStripFlags interface and provides methods
 * for copying instance vertex data into various fixed-size
 * GeometryArray representations.
 *
 * @see GeneralizedStrip
 * @see GeometryDecompressor
 */
class GeneralizedVertexList implements GeneralizedStripFlags {

    // The ArrayList containing the vertices.
    private ArrayList vertices ;

    // Booleans for individual vertex components.
    private boolean hasColor3 = false ;
    private boolean hasColor4 = false ;
    private boolean hasNormals = false ;
    
    // Indicates the vertex winding of front-facing triangles in this strip.
    private int frontFace ;
    
    /**
     * Count of number of strips generated after conversion to GeometryArray.
     */
    int stripCount ;

    /**
     * Count of number of vertices generated after conversion to GeometryArray.
     */
    int vertexCount ;

    /**
     * Count of number of triangles generated after conversion to GeometryArray.
     */
    int triangleCount ;

    /**
     * Bits describing the data bundled with each vertex.  This is specified
     * using the GeometryArray mask components.
     */
    int vertexFormat ;

    /**
     * Creates a new GeneralizedVertexList for the specified vertex format.
     * @param vertexFormat a mask indicating which components are
     * present in each vertex, as used by GeometryArray.
     * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
     * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
     * @param initSize initial number of elements
     * @see GeometryArray
     */
    GeneralizedVertexList(int vertexFormat, int frontFace, int initSize) {
	this.frontFace = frontFace ;
	setVertexFormat(vertexFormat) ;
	
	if (initSize == 0)
	    vertices = new ArrayList() ;
	else
	    vertices = new ArrayList(initSize) ;

	stripCount = 0 ;
	vertexCount = 0 ;
	triangleCount = 0 ;
    }

    /**
     * Creates a new GeneralizedVertexList for the specified vertex format.
     * @param vertexFormat a mask indicating which components are
     * present in each vertex, as used by GeometryArray.
     * @param frontFace a flag, either GeneralizedStripFlags.FRONTFACE_CW or
     * GeneralizedStripFlags.FRONTFACE_CCW, indicating front face winding
     * @see GeometryArray
     */
    GeneralizedVertexList(int vertexFormat, int frontFace) {
	this(vertexFormat, frontFace, 0) ;
    }

    /**
     * Sets the vertex format for this vertex list.
     * @param vertexFormat a mask indicating which components are
     * present in each vertex, as used by GeometryArray.
     */
    void setVertexFormat(int vertexFormat) {
	this.vertexFormat = vertexFormat ;

	if ((vertexFormat & GeometryArray.NORMALS) != 0)
	    hasNormals = true ;

        if ((vertexFormat & GeometryArray.COLOR_4) == GeometryArray.COLOR_4)
            hasColor4 = true ;
        else if ((vertexFormat & GeometryArray.COLOR_3) == GeometryArray.COLOR_3)
            hasColor3 = true ;
    }
    
    /**
     * A class with fields corresponding to all the data that can be bundled
     * with the vertices of generalized strips.
     */
    class Vertex {
	int flag ;
	Point3f coord ;
	Color3f color3 ;
	Color4f color4 ;
	Vector3f normal ;
	    
	Vertex(Point3f p, Vector3f n, Color4f c, int flag) {
	    this.flag = flag ;
	    coord = new Point3f(p) ;
		
	    if (hasNormals)
		normal = new Vector3f(n) ;

	    if (hasColor3)
		color3 = new Color3f(c.x, c.y, c.z) ;

	    else if (hasColor4)
		color4 = new Color4f(c) ;
	}
    }

    /**
     * Copy vertex data to a new Vertex object and add it to this list.
     */
    void addVertex(Point3f pos, Vector3f norm, Color4f color, int flag) {
	vertices.add(new Vertex(pos, norm, color, flag)) ;
    }
				
    /**
     * Return the number of vertices in this list.
     */
    int size() {
	return vertices.size() ;
    }

    // GeneralizedStripFlags interface implementation
    public int getFlagCount() {
	return vertices.size() ;
    }

    // GeneralizedStripFlags interface implementation
    public int getFlag(int index) {
	return ((Vertex)vertices.get(index)).flag ;
    }

    // Copy vertices in the given order to a fixed-length GeometryArray.
    // Using the array versions of the GeometryArray set() methods results in
    // a significant performance improvement despite needing to create
    // fixed-length arrays to hold the vertex elements.
    private void copyVertexData(GeometryArray ga,
				GeneralizedStrip.IntList indices) {
	Vertex v ;
	Point3f p3f[] = new Point3f[indices.count] ;

	if (hasNormals) {
	    Vector3f v3f[] = new Vector3f[indices.count] ;
	    if (hasColor3) {
		Color3f c3f[] = new Color3f[indices.count] ;
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		    v3f[i] = v.normal ;
		    c3f[i] = v.color3 ;
		}
		ga.setColors(0, c3f) ;

	    } else if (hasColor4) {
		Color4f c4f[] = new Color4f[indices.count] ;
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		    v3f[i] = v.normal ;
		    c4f[i] = v.color4 ;
		}
		ga.setColors(0, c4f) ;

	    } else {
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		    v3f[i] = v.normal ;
		}
	    }
	    ga.setNormals(0, v3f) ;

	} else {
	    if (hasColor3) {
		Color3f c3f[] = new Color3f[indices.count] ;
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		    c3f[i] = v.color3 ;
		}
		ga.setColors(0, c3f) ;
	    
	    } else if (hasColor4) {
		Color4f c4f[] = new Color4f[indices.count] ;
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		    c4f[i] = v.color4 ;
		}
		ga.setColors(0, c4f) ;

	    } else {
		for (int i = 0 ; i < indices.count ; i++) {
		    v = (Vertex)vertices.get(indices.ints[i]) ;
		    p3f[i] = v.coord ;
		}
	    }
	}
	ga.setCoordinates(0, p3f) ;
    }

    /**
     * Output a PointArray.
     */
    PointArray toPointArray() {
	int size = vertices.size() ;

	if (size > 0) {
	    PointArray pa = new PointArray(size, vertexFormat) ;
	    GeneralizedStrip.IntList il = new GeneralizedStrip.IntList(size) ;

	    il.fillAscending() ;
	    copyVertexData(pa, il) ;

	    vertexCount += size ;
	    return pa ;
	}
	else
	    return null ;
    }

    /**
     * Output a TriangleArray.
     */
    TriangleArray toTriangleArray() {
	int vertices[] = GeneralizedStrip.toTriangles(this, frontFace) ;

	if (vertices != null) {
	    TriangleArray ta ;
	    GeneralizedStrip.IntList il ;

	    ta = new TriangleArray(vertices.length, vertexFormat) ;
	    il = new GeneralizedStrip.IntList(vertices) ;
	    copyVertexData(ta, il) ;

	    vertexCount += vertices.length ;
	    triangleCount += vertices.length/3 ;
	    return ta ;
	} else
	    return null ;
    }

    /**
     * Output a LineStripArray.
     */
    LineStripArray toLineStripArray() {
	GeneralizedStrip.StripArray stripArray =
	    GeneralizedStrip.toLineStrips(this) ;

	if (stripArray != null) {
	    LineStripArray lsa ;
	    lsa = new LineStripArray(stripArray.vertices.count,
				     vertexFormat,
				     stripArray.stripCounts.trim()) ;

	    copyVertexData(lsa, stripArray.vertices) ;

	    vertexCount += stripArray.vertices.count ;
	    stripCount += stripArray.stripCounts.count ;
	    return lsa ;
	} else
	    return null ;
    }

    /**
     * Output a TriangleStripArray.
     */
    TriangleStripArray toTriangleStripArray() {
	GeneralizedStrip.StripArray stripArray =
	    GeneralizedStrip.toTriangleStrips(this, frontFace) ;

	if (stripArray != null) {
	    TriangleStripArray tsa ;
	    tsa = new TriangleStripArray(stripArray.vertices.count,
					 vertexFormat,
					 stripArray.stripCounts.trim()) ;

	    copyVertexData(tsa, stripArray.vertices) ;

	    vertexCount += stripArray.vertices.count ;
	    stripCount += stripArray.stripCounts.count ;
	    return tsa ;
	} else
	    return null ;
    }

    /**
     * Output triangle strip and triangle fan arrays.
     * @return a 2-element array of GeometryStripArray; element 0 if non-null
     * will contain a TriangleStripArray, and element 1 if non-null will
     * contain a TriangleFanArray.
     */
    GeometryStripArray[] toStripAndFanArrays() {
	GeneralizedStrip.StripArray stripArray[] =
	    GeneralizedStrip.toStripsAndFans(this, frontFace) ;

	GeometryStripArray gsa[] = new GeometryStripArray[2] ;

	if (stripArray[0] != null) {
	    gsa[0] = new TriangleStripArray(stripArray[0].vertices.count,
					    vertexFormat,
					    stripArray[0].stripCounts.trim()) ;

	    copyVertexData(gsa[0], stripArray[0].vertices) ;

	    vertexCount += stripArray[0].vertices.count ;
	    stripCount += stripArray[0].stripCounts.count ;
	}

	if (stripArray[1] != null) {
	    gsa[1] = new TriangleFanArray(stripArray[1].vertices.count,
					  vertexFormat,
					  stripArray[1].stripCounts.trim()) ;

	    copyVertexData(gsa[1], stripArray[1].vertices) ;

	    vertexCount += stripArray[1].vertices.count ;
	    stripCount += stripArray[1].stripCounts.count ;
	}
	return gsa ;
    }

    /**
     * Output triangle strip and and triangle arrays.
     * @return a 2-element array of GeometryArray; element 0 if non-null
     * will contain a TriangleStripArray, and element 1 if non-null will
     * contain a TriangleArray.
     */
    GeometryArray[] toStripAndTriangleArrays() {
	GeneralizedStrip.StripArray stripArray[] =
	    GeneralizedStrip.toStripsAndTriangles(this, frontFace, 4, 12) ;

	GeometryArray ga[] = new GeometryArray[2] ;

	if (stripArray[0] != null) {
	    ga[0] = new TriangleStripArray(stripArray[0].vertices.count,
					   vertexFormat,
					   stripArray[0].stripCounts.trim()) ;

	    copyVertexData(ga[0], stripArray[0].vertices) ;

	    vertexCount += stripArray[0].vertices.count ;
	    stripCount += stripArray[0].stripCounts.count ;
	}

	if (stripArray[1] != null) {
	    ga[1] = new TriangleArray(stripArray[1].vertices.count,
				      vertexFormat) ;

	    copyVertexData(ga[1], stripArray[1].vertices) ;
	    triangleCount += stripArray[1].vertices.count/3 ;
	}
	return ga ;
    }
}
