// Copyright 2000-2005, FreeHEP.

package hep.graphics.heprep.xml;

import java.io.*;
import java.util.*;
import java.util.zip.*;

import org.freehep.xml.util.XMLWriter;
import org.freehep.util.ScientificFormat;

import hep.graphics.heprep.HepRep;
import hep.graphics.heprep.HepRepAction;
import hep.graphics.heprep.HepRepAttDef;
import hep.graphics.heprep.HepRepAttribute;
import hep.graphics.heprep.HepRepAttValue;
import hep.graphics.heprep.HepRepConstants;
import hep.graphics.heprep.HepRepDefinition;
import hep.graphics.heprep.HepRepInstance;
import hep.graphics.heprep.HepRepInstanceTree;
import hep.graphics.heprep.HepRepPoint;
import hep.graphics.heprep.HepRepTreeID;
import hep.graphics.heprep.HepRepType;
import hep.graphics.heprep.HepRepTypeTree;
import hep.graphics.heprep.HepRepWriter;
import hep.graphics.heprep.ref.DefaultHepRepAttValue;
import hep.graphics.heprep.util.HepRepUtil;

/**
 *
 * @author M.Donszelmann
 *
 * @version $Id: XMLHepRepWriter.java,v 1.44 2005/01/10 18:49:31 duns Exp $
 */

public class XMLHepRepWriter implements HepRepWriter {
    public static final String cvsId = "$Id: XMLHepRepWriter.java,v 1.44 2005/01/10 18:49:31 duns Exp $";

    private static final String nameSpace = "heprep";
    private boolean bitEncoding;        // flags if the doubles should be HexBit encoded
    private ScientificFormat scientific;
    private ZipOutputStream zip;
    private XMLWriter xml;
    protected Map/*<String, String>*/ properties;

    XMLHepRepWriter(OutputStream out, boolean randomAccess, boolean compress) throws IOException {
        super();
        if (randomAccess) {
            zip = new ZipOutputStream(out);
            zip.setLevel(compress ? Deflater.DEFAULT_COMPRESSION : Deflater.NO_COMPRESSION);
            out = zip;
        } else if (compress) {
            out = new GZIPOutputStream(out);
        }
        init(new BufferedWriter(new OutputStreamWriter(out)));
    }

    XMLHepRepWriter(Writer out) {
        super();
        init(out);
    }

    private void init(Writer out) {
        xml = new XMLWriter(out, "  ", nameSpace);
        bitEncoding = false;
        scientific = new ScientificFormat();
        properties = new HashMap();
    }

    public void setBitEncoding(boolean bitEncoding) {
        this.bitEncoding = bitEncoding;
    }

    public void addProperty(String key, String value) throws IOException {
        properties.put(key, value);
    }

    public void close() throws IOException {
        try {
            xml.closeDoc();
        } finally {
            if (zip != null) {
                zip.putNextEntry(new ZipEntry("heprep.properties"));
                PrintStream ps = new PrintStream(zip);
                for (Iterator i=properties.keySet().iterator(); i.hasNext(); ) {
                    String key = (String)i.next();
                    ps.println(key+"="+(String)properties.get(key));
                }
                zip.closeEntry();
                zip.close();
            }
            xml.close();
        }
    }

    public void write(Object object) throws IOException {
        if (object instanceof HepRep) {
            write((HepRep)object, "HepRep");
        } else if (object instanceof HepRepTreeID) {
            write((HepRepTreeID)object);
        } else if (object instanceof List/*<String>*/) {
            write((List/*<String>*/)object);
        } else if (object instanceof HepRepAction) {
            write((HepRepAction)object);
        } else if (object instanceof HepRepTypeTree) {
            write((HepRepTypeTree)object);
        } else if (object instanceof HepRepType) {
            write((HepRepType)object);
        } else if (object instanceof HepRepInstanceTree) {
            write((HepRepInstanceTree)object);
        } else if (object instanceof HepRepInstance) {
            write((HepRepInstance)object);
        } else if (object instanceof HepRepPoint) {
            write((HepRepPoint)object);
        } else {
            System.err.println("XMLHepRepWriter: do not know how to write object of type: "+object.getClass());
        }
    }

    public void write(HepRep heprep, String name) throws IOException {
        if (zip != null) {
            zip.putNextEntry(new ZipEntry(name));
        }
        xml.openDoc();
        xml.setAttribute("version", "2.0");
        String schemaLocation = "http://java.freehep.org/schemas/heprep/2.0";
        xml.setAttribute("xmlns", schemaLocation);
        xml.setAttribute("xmlns", "xsi", "http://www.w3.org/2001/XMLSchema-instance");
        xml.setAttribute("xsi", "schemaLocation", schemaLocation+" "+schemaLocation+"/HepRep.xsd");
        xml.openTag(nameSpace, "heprep");
        write(heprep.getLayerOrder());
        for (Iterator i=heprep.getTypeTreeList().iterator(); i.hasNext(); ) {
            write((HepRepTypeTree)i.next());
        }
        for (Iterator i=heprep.getInstanceTreeList().iterator(); i.hasNext(); ) {
            write((HepRepInstanceTree)i.next());
        }
        xml.closeTag();
        xml.closeDoc();
        if (zip != null) {
            zip.closeEntry();
        }
    }

    public void write(List/*<String>*/ layers) {
        StringBuffer layerOrder = new StringBuffer();
        String layerName = null;
        for (Iterator i=layers.iterator(); i.hasNext(); ) {
            if (layerName != null) layerOrder.append(", ");
            layerName = (String)i.next();
            layerOrder.append(layerName);
        }
        xml.setAttribute("order", layerOrder.toString());
        xml.printTag(nameSpace, "layer");
    }

    public void write(HepRepTypeTree typeTree) {
        xml.setAttribute("name", typeTree.getName());
        xml.setAttribute("version", typeTree.getVersion());
        xml.openTag(nameSpace, "typetree");
        for (Iterator i=typeTree.getTypeList().iterator(); i.hasNext(); ) {
            write((HepRepType)i.next());
        }
        xml.closeTag();
    }

    public void write(HepRepType type) {
        if (type == null) return;
        xml.setAttribute("name", type.getName());
        xml.openTag(nameSpace, "type");
        write((HepRepDefinition)type);
        write((HepRepAttribute)type);
        for (Iterator i=type.getTypeList().iterator(); i.hasNext(); ) {
            write((HepRepType)i.next());
        }
        xml.closeTag();
    }

    public void write(HepRepTreeID treeID) {
        xml.setAttribute("qualifier", treeID.getQualifier());
        xml.setAttribute("name", treeID.getName());
        xml.setAttribute("version", treeID.getVersion());
        xml.printTag(nameSpace, "treeid");
    }

    public void write(HepRepAction action) {
        xml.setAttribute("name", action.getName());
        xml.setAttribute("expression", action.getExpression());
        xml.printTag(nameSpace, "action");
    }

    public void write(HepRepInstanceTree instanceTree) {
        xml.setAttribute("name", instanceTree.getName());
        xml.setAttribute("version", instanceTree.getVersion());
        xml.setAttribute("typetreename", instanceTree.getTypeTree().getName());
        xml.setAttribute("typetreeversion", instanceTree.getTypeTree().getVersion());
        xml.openTag(nameSpace, "instancetree");

        // refs
        for (Iterator i=instanceTree.getInstanceTreeList().iterator(); i.hasNext(); ) {
            write((HepRepTreeID)i.next());
        }

        // instances
        for (Iterator i=instanceTree.getInstances().iterator(); i.hasNext(); ) {
            write((HepRepInstance)i.next());
        }
        xml.closeTag();
    }

    public void write(HepRepInstance instance) {
        // FIXME FREEHEP-356
        xml.setAttribute("type", instance.getType().getFullName());
        xml.openTag(nameSpace, "instance");
        write((HepRepAttribute)instance);
        for (Iterator i=instance.getPoints().iterator(); i.hasNext(); ) {
            write((HepRepPoint)i.next());
        }
        for (Iterator i=instance.getInstances().iterator(); i.hasNext(); ) {
            write((HepRepInstance)i.next());
        }
        xml.closeTag();
    }

    public void write(HepRepPoint point) {
        if (bitEncoding) {
            xml.setAttribute("x", encodeNumber(point.getX()));
            xml.setAttribute("y", encodeNumber(point.getY()));
            xml.setAttribute("z", encodeNumber(point.getZ()));
        } else {
            xml.setAttribute("x", scientific.format(point.getX()));
            xml.setAttribute("y", scientific.format(point.getY()));
            xml.setAttribute("z", scientific.format(point.getZ()));
        }
        if (point.getAttValuesFromNode().iterator().hasNext()) {
            xml.openTag(nameSpace, "point");
            write((HepRepAttribute)point);
            xml.closeTag();
        } else {
            xml.printTag(nameSpace, "point");
        }
    }

    public void write(HepRepAttribute attribute) {
//        HepRepAttValue layerAtt = attribute.getAttValueFromNode("layer");
//        if (layerAtt!=null) write(layerAtt);

        for (Iterator i = attribute.getAttValuesFromNode().iterator(); i.hasNext(); ) {
            HepRepAttValue attValue = (HepRepAttValue)i.next();
            write(attValue);
        }
    }

    public void write(HepRepDefinition definition) {
        for (Iterator i = definition.getAttDefsFromNode().iterator(); i.hasNext(); ) {
            HepRepAttDef attDef = (HepRepAttDef)i.next();
            write(attDef);
        }
    }

    private String encodeNumber(double d) {
        return "0ds"+HepRepUtil.encodeSpecial(Double.doubleToLongBits(d));
//        return "0d0x"+Long.toHexString(Double.doubleToLongBits(d));
//        return "0d"+Double.doubleToLongBits(d);
    }

    private String labelStrings[] = {"NAME", "DESC", "VALUE", "EXTRA"};

    public void write(HepRepAttValue attValue) {
        String name = attValue.getName();
        xml.setAttribute("name", name);

        if (attValue.getType() == HepRepAttValue.TYPE_DOUBLE) {
            if (bitEncoding) {
                xml.setAttribute("value", encodeNumber(attValue.getDouble()));
            } else {
                xml.setAttribute("value", scientific.format(attValue.getDouble()));
            }
        } else {
            xml.setAttribute("value", attValue.getAsString());
        }

        if (attValue.getType() != HepRepAttValue.TYPE_STRING) {
            xml.setAttribute("type", attValue.getTypeName());
        }
        
        if (attValue.showLabel() != HepRepConstants.SHOW_NONE) {
            String label = DefaultHepRepAttValue.toShowLabel(attValue.showLabel());
            xml.setAttribute("showlabel", label);
        }
        xml.printTag(nameSpace, "attvalue");
    }

    public void write(HepRepAttDef attDef) {
        xml.setAttribute("name", attDef.getName());
        xml.setAttribute("desc", attDef.getDescription());
        xml.setAttribute("category", attDef.getCategory());
        xml.setAttribute("extra", attDef.getExtra());
        xml.printTag(nameSpace, "attdef");
    }
}

