/*---

    iGeo - http://igeo.jp

    Copyright (c) 2002-2012 Satoru Sugihara

    This file is part of iGeo.

    iGeo is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation, version 3.

    iGeo is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with iGeo.  If not, see <http://www.gnu.org/licenses/>.

---*/

package igeo.io;

import igeo.*;
import java.io.File;
import java.util.ArrayList;

/*
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import com._3ds.xsd._3dxml.*;
import com._3ds.xsd.osm.*;
*/

import org.w3c.dom.*;
import javax.xml.parsers.*;

//import org.apache.xerces.parsers.*;

/**
   3DXML Importer
      
   @author Satoru Sugihara
   @version 0.7.0.0;
*/
public class I3DXMLImporter{
    
    public ArrayList<IVec> positions;
    public ArrayList<IVec> normals;
    public ArrayList<IVec2> texture;
    
    public ArrayList<IVertex> vertices;
    
    public ArrayList<Face> faces;
    
    
    
    public I3DXMLImporter(){
	positions = new ArrayList<IVec>();
	normals = new ArrayList<IVec>();
	texture = new ArrayList<IVec2>();
	faces = new ArrayList<Face>();
    }
    
    public void createMesh(){
	
	vertices = new ArrayList<IVertex>();
	
	for(int i=0; i<positions.size(); i++){
	    //IG.p("vertex "+i);
	    IVertex vtx = new IVertex(positions.get(i));
	    vertices.add(vtx);
	}
	
	if(vertices.size()==normals.size()){
	    for(int i=0; i<vertices.size(); i++){
		//IG.p("normal "+i);
		vertices.get(i).nml(normals.get(i));
	    }
	}
	
	if(vertices.size()==texture.size()){
	    for(int i=0; i<vertices.size(); i++){
		//IG.p("texture "+i);
		vertices.get(i).texture(texture.get(i));
	    }
	}
	
	for(int i=0; i<faces.size(); i++){
	    
	    Face f = faces.get(i);
	    
	    ArrayList<IFace> ifcs = new ArrayList<IFace>();
	    
	    for(int j=0; j<f.triangles.size(); j++){
		Triangle tr = f.triangles.get(j);
		IFace ifc = new IFace(vertices.get(tr.i),
				      vertices.get(tr.j),
				      vertices.get(tr.k));
		ifcs.add(ifc);
	    }
	    for(int j=0; j<f.strips.size(); j++){
		Strip st = f.strips.get(j);
		for(int k=0; k<st.index.size()-2; k++){
		    IFace ifc=null;
		    if(k%2==0){
			ifc = new IFace(vertices.get(st.index.get(k)),
					vertices.get(st.index.get(k+1)),
					vertices.get(st.index.get(k+2)));
		    }
		    else{
			ifc = new IFace(vertices.get(st.index.get(k)),
					vertices.get(st.index.get(k+2)),
					vertices.get(st.index.get(k+1)));
		    }
		    ifcs.add(ifc);
		}
	    }
	    for(int j=0; j<f.fans.size(); j++){
		Fan fn = f.fans.get(j);
		for(int k=1; k<fn.index.size()-1; k++){
		    IFace ifc = new IFace(vertices.get(fn.index.get(0)),
					  vertices.get(fn.index.get(k)),
					  vertices.get(fn.index.get(k+1)));
		    ifcs.add(ifc);
		}
	    }
	    
	    IMesh mesh = new IMesh();
	    for(int j=0; j<ifcs.size(); j++){
		mesh.addFace(ifcs.get(j));
	    }
	    
	}
	
    }
    
    
    static public class Face{
	//public enum Type { TRIANGLE, STRIP, FAN };
	//public ArrayList<Integer> indices;
	//public Type type;
	
	public ArrayList<Triangle> triangles;
	public ArrayList<Strip> strips;
	public ArrayList<Fan> fans;
	
	//public Face(){ indices = new ArrayList<Integer>(); }
	//public Face(Type t){ this(); type = t; }
	
	public Face(){
	    triangles = new ArrayList<Triangle>();
	    strips = new ArrayList<Strip>();
	    fans = new ArrayList<Fan>();
	}
	
	//public void add(int i){ indices.add(i); }
	
	public void addFan(String value){
	    //IG.p(); //
	    
	    String[] v = value.split("\\s+");
	    if(v!=null){
		Fan fan = new Fan();
		for(int i=0; i<v.length; i++){
		    fan.add(Integer.parseInt(v[i]));
		}
		fans.add(fan);
	    }
	    
	    IG.p(fans.size() + " fans"); //
	}
	public void addTriangle(String value){
	    //IG.p(); //
	    String[] v = value.split("\\s+");
	    if(v!=null){
		for(int i=0; i<v.length; i+=3){
		    int i1 = Integer.parseInt(v[i]);
		    int i2 = Integer.parseInt(v[i+1]);
		    int i3 = Integer.parseInt(v[i+2]);
		    Triangle tr = new Triangle(i1,i2,i3);
		    triangles.add(tr);
		}
	    }
	    
	    IG.p(triangles.size() + " triangles"); //
	}
	public void addStrip(String value){
	    //IG.p(); //
	    
	    String[] v = value.split("\\s+");
	    if(v!=null){
		Strip str = new Strip();
		for(int i=0; i<v.length; i++){
		    str.add(Integer.parseInt(v[i]));
		}
		strips.add(str);
	    }
	    
	    IG.p(strips.size() + " strips"); //
	}
	
    }
    
    static public class Triangle{
	public int i,j,k;
	public Triangle(int i, int j, int k){ this.i=i; this.j=j; this.k=k; }
    }
    
    static public class Strip{
	public ArrayList<Integer> index;
	public Strip(){ index = new ArrayList<Integer>(); }
	public void add(int i){ index.add(i); }
    }
    
    static public class Fan{
	public ArrayList<Integer> index;
	public Fan(){ index = new ArrayList<Integer>(); }
	public void add(int i){ index.add(i); }
    }
    
    
    
    public void trace(Node top){
	recurse(top);
    }
    
    
    public void recurse(Node node){
	
	int type = node.getNodeType();
	String name = node.getNodeName();
	
	boolean skip=false;
	if(type == Node.TEXT_NODE){
	}
	else if(type == Node.ELEMENT_NODE){
	    if(name.equals("PolygonalLOD")){
		skip = true;
	    }
	    else if(name.equals("Faces")){
		readFaces(node);
		skip = true;
	    }
	    else if(name.equals("VertexBuffer")){
		readVertexBuffer(node);
		skip = true;
	    }
	}
	
	if(skip) return;
	
	if(node.hasAttributes()){
	    NamedNodeMap attr = node.getAttributes();
	    for(int i=0; i<attr.getLength(); i++){
		Node a = attr.item(i);
		int atype = a.getNodeType();
		String aname = a.getNodeName();
	    }
	}
	
	String value = node.getNodeValue();
	if(value!=null){
	    
	}
	
	NodeList children = node.getChildNodes();
	for(int i=0; i<children.getLength(); i++){
	    Node child = children.item(i);
	    recurse(child);
	}
	
    }
    
    public void readFaces(Node node){
	IG.p();
	
	NodeList children = node.getChildNodes();
	for(int i=0; i<children.getLength(); i++){
	    Node child = children.item(i);
	    if(child.getNodeName().equals("Face")){
		readFace(child);
	    }
	}
    }
    
    public void readFace(Node node){
	Face face = new Face();
	
	NamedNodeMap attr = node.getAttributes();
	for(int i=0; i<attr.getLength(); i++){
	    Node a = attr.item(i);
	    int atype = a.getNodeType();
	    String aname = a.getNodeName();
	    
	    if(aname.equals("fans")){
		String value = a.getNodeValue();
		String[] seg = value.split("\\s*,\\s*");
		for(int j=0; j<seg.length; j++){
		    face.addFan(seg[j]);
		}
	    }
	    else if(aname.equals("strips")){
		String value = a.getNodeValue();
		String[] seg = value.split("\\s*,\\s*");
		for(int j=0; j<seg.length; j++){
		    face.addStrip(seg[j]);
		}
	    }
	    else if(aname.equals("triangles")){
		String value = a.getNodeValue();
		String[] seg = value.split("\\s*,\\s*");
		for(int j=0; j<seg.length; j++){
		    face.addTriangle(seg[j]);
		}
	    }
	    
	}
	
	faces.add(face);
	
    }


    public void readVertexBuffer(Node node){
	NodeList children = node.getChildNodes();
	for(int i=0; i<children.getLength(); i++){
	    Node child = children.item(i);
	    if(child.getNodeName().equals("Positions")){
		readPositions(child);
	    }
	    else if(child.getNodeName().equals("Normals")){
		readNormals(child);
	    }
	    else if(child.getNodeName().equals("TextureCoordinates")){
		readTextureCoordinates(child);
	    }
	}
    }
    
    public void readPositions(Node node){
	IG.p(); //
	Node child = node.getFirstChild();
	if(child!=null && child.getNodeType()==Node.TEXT_NODE){
	    String value = child.getNodeValue();
	    String[] coord = value.split("\\s*,\\s*");
	    
	    for(int i=0; coord!=null && i<coord.length; i++){
		String[] xyz = coord[i].split("\\s+");
		if(xyz.length>=3){
		    double x = Double.parseDouble(xyz[0]);
		    double y = Double.parseDouble(xyz[1]);
		    double z = Double.parseDouble(xyz[2]);
		    IVec v = new IVec(x,y,z);
		    positions.add(v);
		}
	    }
	    
	}
    }
    
    public void readNormals(Node node){
	IG.p(); //
	Node child = node.getFirstChild();
	if(child!=null && child.getNodeType()==Node.TEXT_NODE){
	    String value = child.getNodeValue();
	    String[] coord = value.split("\\s*,\\s*");
	    for(int i=0; coord!=null && i<coord.length; i++){
		String[] xyz = coord[i].split("\\s+");
		if(xyz.length>=3){
		    double x = Double.parseDouble(xyz[0]);
		    double y = Double.parseDouble(xyz[1]);
		    double z = Double.parseDouble(xyz[2]);
		    IVec v = new IVec(x,y,z);
		    normals.add(v);
		}
	    }
	}
    }
    
    public void readTextureCoordinates(Node node){
	IG.p(); //
	Node child = node.getFirstChild();
	if(child!=null && child.getNodeType()==Node.TEXT_NODE){
	    String value = child.getNodeValue();
	    String[] coord = value.split("\\s*,\\s*");
	    for(int i=0; coord!=null && i<coord.length; i++){
		String[] xy = coord[i].split("\\s+");
		double x=0,y=0;
		if(xy!=null){
		    if(xy.length>0) x = Double.parseDouble(xy[0]);
		    if(xy.length>1) y = Double.parseDouble(xy[1]);
		    texture.add(new IVec2(x,y));
		}
	    }
	}
    }
    
    public static void recursivePrint(Node node, int depth){
	//if(node.getNodeType()==Node.TEXT_NODE) return;
	
	String indent = "  ";
	for(int i=0; i<depth; i++) System.out.print(indent); //
	System.out.println("node type: "+node.getNodeType());
	for(int i=0; i<depth; i++) System.out.print(indent); //
	System.out.println("node name: "+node.getNodeName());
	
	if(node.hasAttributes()){
	    for(int i=0; i<depth; i++) System.out.print(indent); //
	    System.out.println("node attr:"); 
	    
	    NamedNodeMap attr = node.getAttributes();
	    for(int i=0; i<attr.getLength(); i++){
		
		recursivePrint(attr.item(i), depth+1);
		
	    }
	}
	
	String value = node.getNodeValue();
	if(value!=null){
	    if(value.length()>100){ value = value.substring(0,100); }
	    for(int i=0; i<depth; i++) System.out.print(indent); //
	    System.out.println("node valu: "+value);
	}
	//System.out.println("node cont: "+node.getTextContent());
	
	NodeList children = node.getChildNodes();
	//IG.p(children.getLength()+" children"); //
	for(int i=0; i<children.getLength(); i++){
	    Node child = children.item(i);
	    recursivePrint(child, depth+1);
	}
	
    }
    
    public static void main(String[] args){
	
	String filename = null;
	
	if(args.length>0){ filename = args[0]; }
	
	if(filename==null){ return; }
	
	IG.debugLevel(100);
	IG.init();
	
	try{
	    DocumentBuilderFactory factory =
		DocumentBuilderFactory.newInstance();
	    DocumentBuilder builder = factory.newDocumentBuilder();
	    
	    Node root = builder.parse(new File(filename));
	    
	    I3DXMLImporter importer = new I3DXMLImporter();
	    importer.trace(root);
	    
	    IG.p("createMesh");
	    importer.createMesh();
	    IG.p("createMesh done");
	    
	    //IG.save("c:/users/s.sugihara.Morphosis/Documents/java/data/FacadeTest_Surface.3dm"); //
	    IG.save("c:/users/s.sugihara.Morphosis/Documents/java/data/FacadeTest_Surface.obj"); //
	    
	    /*
	    if (root.getNodeType() == Node.DOCUMENT_NODE) {
		System.out.println("Root is Document!");
	    }
	    recursivePrint(root, 0);
	    */
	    
	    
	    /*
	    Node child = root.getFirstChild();
 	    System.out.println("child node name: "+child.getNodeName());
	    System.out.println("child node valu: "+child.getNodeValue());
	    
	    NamedNodeMap attributes = child.getAttributes();
	    
	    
	    NodeList gchildren = child.getChildNodes();
	    for (int i = 0; i < gchildren.getLength(); i++) {
		Node gchild = gchildren.item(i);
		System.out.println("Name: " + gchild.getNodeName());
	    }
	    */
	    
	}catch(Exception e){
	    e.printStackTrace();
	}
	
	
	
	/*
	String packageName = "com._3ds.xsd._3dxml";

	
	Model3Dxml model = null;
	try{
	    JAXBContext context =
		JAXBContext.newInstance(packageName);
	    
	    Unmarshaller unmarshaller = context.createUnmarshaller();
	    
	    model = (Model3Dxml)unmarshaller.unmarshal(new File(filename));
	    
	}catch(JAXBException e){
	    e.printStackTrace();
	}
	*/
    }
    
}
