/*
 * VrmlWriter.java
 *
 * Created on 19 september 2000, 13:35
 */

import javax.media.j3d.*;
import java.awt.*;
import javax.vecmath.*;
import java.io.*;

/** This class saves the scenegraph in a WRL file. */
public class VrmlWriter extends Object {

    /*

    //All the node types that are not valid as children nodes
    public static final int WRL_APPEARANCE          = 1;
    public static final int WRL_AUDIOCLIP           = 2;
    public static final int WRL_BOX                 = 3;
    public static final int WRL_COLOR               = 4;
    public static final int WRL_CONE                = 5;
    public static final int WRL_COORDINATE          = 6;
    public static final int WRL_CYLINDER            = 7;
    public static final int WRL_ELEVATIONGRID       = 8;
    public static final int WRL_EXTRUSION           = 9;
    public static final int WRL_IMAGETEXTURE        = 10;
    public static final int WRL_INDEXEDFACESET      = 11;
    public static final int WRL_INDEXEDLINESET      = 12;
    public static final int WRL_MATERIAL            = 13;
    public static final int WRL_MOVIETEXTURE        = 14;
    public static final int WRL_NORMAL              = 15;
    public static final int WRL_POINTSET            = 16;
    public static final int WRL_SPHERE              = 17;
    public static final int WRL_TEXT                = 18;
    public static final int WRL_TEXTURECOORDINATE   = 19;
    public static final int WRL_TEXTURETRANSFORM    = 20;

    //All the children nodes:

    public static final int WRL_ANCHOR                  = 21;
    public static final int WRL_BACKGROUND              = 22;
    public static final int WRL_BILLBOARD               = 23;
    public static final int WRL_COLLISION               = 24;
    public static final int WRL_COLORINTERPOLATOR       = 25;
    public static final int WRL_CYLINDERSENSOR          = 26;
    public static final int WRL_DIRECTIONALLIGHT        = 27;
    public static final int WRL_FOG                     = 28;
    public static final int WRL_GROUP                   = 29;
    public static final int WRL_INLINE                  = 30;
    public static final int WRL_LOD                     = 31;
    public static final int WRL_NAVIGATIONINFO          = 32;
    public static final int WRL_NORMALINTERPOLATOR      = 33;
    public static final int WRL_PLANESENSOR             = 34;
    public static final int WRL_POINTLIGHT              = 35;
    public static final int WRL_POSITIONINTERPOLATOR    = 36;
    public static final int WRL_PROXIMITYSENSOR         = 37;
    public static final int WRL_SCALARINTERPOLATOR      = 38;
    public static final int WRL_SCRIPT                  = 39;
    public static final int WRL_SHAPE                   = 40;
    public static final int WRL_SOUND                   = 41;
    public static final int WRL_SPOTLIGHT               = 42;
    public static final int WRL_SPHERESENSOR            = 43;
    public static final int WRL_SWITCH                  = 44;
    public static final int WRL_TIMESENSOR              = 45;
    public static final int WRL_TOUCHSENSOR             = 46;
    public static final int WRL_TRANSFORM               = 47;
    public static final int WRL_VIEWPOINT               = 48;
    public static final int WRL_VISIBILITYSENSOR        = 49;
    public static final int WRL_WORLDINFO               = 50;

    //ALL THE GROUP NODES

    public static final int WRL_ANCHOR_GRP              = 51;
    public static final int WRL_BILLBOARD_GRP           = 52;
    public static final int WRL_COLLISION_GRP           = 53;
    public static final int WRL_GROUP_GRP               = 54;
    public static final int WRL_INLINE_GRP              = 55;
    public static final int WRL_LOD_GRP                 = 56;
    public static final int WRL_SWITCH_GRP              = 57;
    public static final int WRL_TRANSFORM_GRP           = 58;
     */
    /** The prinwriter object used to write to a file
     */
    private PrintWriter pw;


    /** Creates new VrmlWriter
     * @param group The root of the scenegraph
     * @param fileName the output filename
     * @param path the output path
     */
    public VrmlWriter(Group group, String fileName, String path) {
        super();
        initialiseFile(path + fileName);
        putHeader();
        //SetCapability.settingCap(group,SetCapability.SHAPE3D_CAPABILITY );
        nodeTraversal(group);        
        pw.close();        
    }

    //looks for any known nodes
    /** Method that is used to traverse the scenegraph.  This method is recursively used
     * @param group The root of the scenegraph that have to be saved
     */
    public void nodeTraversal(Group group){
        for(int i=0;i<group.numChildren();i++){
            if(group.getChild(i) instanceof Leaf){
                checkNode(group.getChild(i));
            }
            if(group.getChild(i) instanceof Group){
                if(group.getChild(i) instanceof TransformGroup){
                    getTransformNode((TransformGroup)group.getChild(i));
                }else{
                    nodeTraversal((Group)group.getChild(i));
                }
            }

        }
    }

    //looks for the transformparameters

    /** Searches for a transformGroup and writes its content to the WRL file
     * @param tGroup The root
     */
    public void getTransformNode(Group tGroup){

        Transform3D t3d = new Transform3D();
        Quat4d q1 = new Quat4d();
        Vector3d t1 = new Vector3d();
        Vector3d s1 = new Vector3d();
        AxisAngle4d r1 = new AxisAngle4d();
        Point3d c1 = new Point3d();
        ((BoundingSphere)(tGroup.getBounds())).getCenter(c1);
        ((TransformGroup)tGroup).getTransform(t3d);
        t3d.getScale(s1);
        t3d.get(q1,t1);
        r1.set(q1);
        pw.println("Transform {");
        //pw.println("center " + c1.x + " " + c1.y + " " + c1.z);
        pw.println("translation " + t1.x + " " + t1.y + " " + t1.z);
        pw.println("scale " + s1.x + " " + s1.y + " " + s1.z);
        pw.println("rotation 1 0 0 " + (Math.rint(r1.angle*1000))/1000);
        pw.println("rotation 0 1 0 " + (Math.rint(r1.angle*1000))/1000);
        pw.println("rotation 0 0 1 " + (Math.rint(r1.angle*1000))/1000);
        pw.println("children[");
        nodeTraversal(tGroup);
        pw.println("]}");
    }

    //Makes a printwriter to print to a file (Buffered)
    /** Puts the header and initialises the output file
     * @param filename Initialises a file and puts the header
     */
    public void initialiseFile(String filename){
        try{
            pw = new PrintWriter(new BufferedOutputStream(new FileOutputStream(filename)));
            //pw = System.out;
        }catch(IOException e){
            e.printStackTrace();
        }
    }

    /** Puts the header
     */
    public void putHeader(){
        pw.print("#VRML V2.0 utf8 \n");

    }



    /** Looks if this node is appropiate
     * @param node The node that has to be checked
     */
    public void checkNode(Node node){        
        if(node instanceof Shape3D){         
            System.out.println("name: " + ((Shape3D)node).getUserData());
            if(((Shape3D)node).getUserData() == null){
            pw.println("Shape {");    
            }else{
            pw.println("DEF ID" + ((String)((Shape3D)node).getUserData()).substring(2) + " Shape {");
        }
            //Check and process geometry of shape3d node

            
            checkNodeComponent(((Shape3D)node).getGeometry());
            //check and process appearance of shape3d node              
            checkNodeComponent(((Shape3D)node).getAppearance());
            pw.println("}");
        }

    }

    /** Looks if this nodecomponent is appropiate
     * @param nodec The nodeComponent
     */
    public void checkNodeComponent(NodeComponent nodec){
        if(nodec instanceof Geometry){
            Geometry geom = (Geometry)nodec;
            if(geom instanceof TriangleArray || geom instanceof QuadArray){
                writeArray(geom);
            }
            geom = null;
        }
        if(nodec instanceof Appearance){
            Appearance app = (Appearance)nodec;
            if(app != null){
                writeAppearance(app);
            }
        }


    }
    
    /** Writes the array to disk
     * @param geom The geommetry of the current node
     */
    public void writeArray(Geometry geom){

        pw.println("geometry IndexedFaceSet {");
        if(geom instanceof TriangleArray){

            TriangleArray ta = (TriangleArray)geom;

            Point3d test = new Point3d();
            Vector3f vector = new Vector3f();

            pw.println("coord Coordinate { \n point [ ");

            for (int i = 0; i < ta.getVertexCount();i++){
                ta.getCoordinate(i, test);
                if(true/*checkForEqual(test,ta,i)==-1){*/){
                    pw.println((Math.rint(test.x*1000))/1000 + " " + (Math.rint(test.y*1000))/1000 + " " + (Math.rint(test.z*1000))/1000 + ",");
                    //System.out.println(i +":" + test.x + " " + test.y + " " + test.z + "," );
                }
            }

            pw.println("]\n}\n coordIndex [");

            String s = "";
            int teller = 0;
            for (int i = 0; i < ta.getVertexCount();i++){
                ta.getCoordinate(i, test);
                if(true)/*checkForEqual(test,ta,i)==-1){*/{
                    s = s + (i-teller) +" ";
                }else{
                    //teller++;
                    s = s + i/*checkForEqual(test,ta,i)*/ + " ";
                }
                if(((i+1)%3)==0){
                    s = s + "-1 \n";
                    pw.print(s);
                s="" ;}


            }
            pw.println("]");
        }
        if(geom instanceof QuadArray){

            QuadArray qa = (QuadArray)geom;
            Point3d test = new Point3d();
            Vector3f vector = new Vector3f();

            pw.println("coord Coordinate { \n point [ ");

            for (int i = 0; i < qa.getVertexCount();i++){
                qa.getCoordinate(i, test);
                if(checkForEqual(test,qa,i)==-1){
                    pw.println(Math.abs(test.x/1000)*1000 + " " + Math.abs(test.y/1000)*1000 + " " + Math.abs(test.z/1000)*1000 + ",");
                }
            }

            pw.println("]\n}\n coordIndex [");

            String s = "";
            int teller = 0;
            for (int i = 0; i < qa.getVertexCount();i++){
                qa.getCoordinate(i, test);
                if(checkForEqual(test,qa,i)==-1){
                    s = s + (i-teller) +" ";
                }else{
                    teller++;
                    s = s + checkForEqual(test,qa,i) + " ";
                }
                if(((i+1)%4)==0){
                    s = s + "-1 \n";
                    pw.print(s);
                s="" ;}

            }


        }
        pw.println("}");
    }

    /** Writes the appearance to disk
     * @param app The appearance that has to be written
     */
    public void writeAppearance(Appearance app){

        pw.println("appearance Appearance {");
        if(app.getMaterial() instanceof Material){
            Color3f  diffCol = new Color3f();
            Color3f  emmCol = new Color3f();
            Color3f  specCol = new Color3f();
            Color3f  difCol = new Color3f();
            float transp = 0;
            float  shin;
            Material mat = app.getMaterial();                     
            if(app.getTransparencyAttributes() instanceof TransparencyAttributes){
                transp = app.getTransparencyAttributes().getTransparency();
            }
            pw.println("material Material {");
            mat.getDiffuseColor(diffCol);
            mat.getEmissiveColor(emmCol);
            mat.getSpecularColor(specCol);
            shin = mat.getShininess();

            pw.println("diffuseColor " + diffCol.x + " " + diffCol.y + " " + diffCol.z);
            pw.println("emissiveColor " + emmCol.x + " " + emmCol.y + " " + emmCol.z);
            pw.println("specularColor " + specCol.x + " " + specCol.y + " " + specCol.y);
            pw.println("shininess " + shin);
            pw.println("transparency " + transp);

            pw.println("}");
        }
        pw.println("}");


    }

    /** not used yet
     * @param p not used yet
     * @param g not used yet
     * @param index not used yet
     * @return not used yet
     */
    public int checkForEqual(Point3d p, GeometryArray g, int index){

        Point3d test = new Point3d();
        if(index!=0){
            for (int i = index-1; i >= 0 ;i--){
                g.getCoordinate(i, test);
                if(test.equals(p)){
                    if(checkForEqual(test,g,i)==-1){
                    return i;}
                    else{
                        return checkForEqual(test,g,i);
                    }
                }
            }
        }
        return -1;
    }

}