package ca.sfu.horizons.xml;

import ca.sfu.horizons.model.*;
import java.io.*;
import org.apache.ecs.Doctype;
import org.apache.ecs.xml.PI;
import org.apache.ecs.xml.XML;
import org.apache.ecs.xml.XMLDocument;

/**
 * Generates an HML file from the current state of Horizons.  Another approach
 * would be to have each class in the model generate its own XML in order to
 * localize changes.  I felt that might be including too much into each class.
 */
public class Horizons2XML {

    /** How clients access the HML writer */
    public static final Horizons2XML WRITER = new Horizons2XML();

    /** Currently open legislature - for convenience */
    private Legislature openLeg;
    
    /** Currently selected file - for convenience */
    private File destinationFile;

    /**
     * Prevent public instantiation
     */
    private Horizons2XML() { }

    /**
     * Writes the legislature to the specified file.
     *
     * @return whether or not it was successful.
     */
    public boolean writeXML(Legislature leg, File file) {

	openLeg = leg;
	destinationFile = file;

	XMLDocument hmlDoc = new XMLDocument(1.0, false, "UTF-8");

	Doctype dtdRef = new Doctype("horizons", "SYSTEM", "\"etc/horizons.dtd\"", "");
	dtdRef.setPrettyPrint(true);
	hmlDoc.addToProlog(dtdRef);

	XML eHorizons = new XML("horizons");
	eHorizons.setPrettyPrint(true);

	eHorizons.addElement(createLegislature());

	hmlDoc.addElement(eHorizons);

	try {
	    PrintWriter out = new PrintWriter(new FileWriter(file));
	    hmlDoc.output(out);
	    out.close();
	} catch (IOException e) {
	    // FIX ME - Display an error?
	    return false;
	}

	return true;

    }

    /**
     * Creates an open legislature element
     */
    private XML createLegislature() {

	XML eLegislature = new XML("legislature");
	eLegislature.setPrettyPrint(true);
	
	eLegislature.addXMLAttribute("next_result",
				     String.valueOf(openLeg.getResult()));

	eLegislature.addXMLAttribute("country_name",
				     String.valueOf(openLeg.getCountryName()));

	eLegislature.addXMLAttribute("country_number",
				     String.valueOf(openLeg.getCountryNumber()));

	eLegislature.addXMLAttribute("seats",
				     String.valueOf(openLeg.getTotalSeats()));

	eLegislature.addXMLAttribute("year",
				     String.valueOf(openLeg.getYear()));

	XML eParties = new XML("parties");
	eParties.setPrettyPrint(true);
	    
	for (int i = 0; i < openLeg.getParties().length; i++) {
	    eParties.addElement(createParty(openLeg.getParties()[i]));
	}

	eLegislature.addElement(eParties);

	XML eSimulations = new XML("simulations");
	eSimulations.setPrettyPrint(true);

	for (int i = 0; i < openLeg.getResults().length; i++) {
	    eSimulations.addElement(
	        createSimulation(openLeg.getResults()[i]));
	}

	eLegislature.addElement(eSimulations);

	return eLegislature;
    }

    /**
     * XXX
     */
    private XML createParty(Party p) {

	XML eParty = new XML("party");
	eParty.setPrettyPrint(true);
	
	eParty.addXMLAttribute("name",
			       p.getName());

	eParty.addXMLAttribute("short_name",
			       p.getShortName());

	eParty.addXMLAttribute("first_form",
			       String.valueOf(p.isFirstForm()));

	eParty.addXMLAttribute("seats",
			       String.valueOf(p.getSeats()));

	eParty.addElement(createIdealPoint(p.getIdealPoint()));

	eParty.addElement(createHorizon(p.getHorizon()));

	return (eParty);
    }

    /**
     * XXX
     */
    private XML createIdealPoint(IdealPoint ip) {

	XML eIdeal = new XML("ideal_point");
	eIdeal.setPrettyPrint(true);

	eIdeal.addXMLAttribute("error_dist",
			       ip.getErrorDistribution().toString());

	XML eLocation = new XML("location", true);
	eLocation.setPrettyPrint(true);

	eLocation.addXMLAttribute("z",
				  String.valueOf(ip.getZCenter()));
	eLocation.addXMLAttribute("y",
				  String.valueOf(ip.getYCenter()));
	eLocation.addXMLAttribute("x",
				  String.valueOf(ip.getXCenter()));

	eIdeal.addElement(eLocation);

	XML eError = new XML("error", true);
	eError.setPrettyPrint(true);

	eError.addXMLAttribute("z",
			       String.valueOf(ip.getZError()));
	eError.addXMLAttribute("y",
			       String.valueOf(ip.getYError()));
	eError.addXMLAttribute("x",
			       String.valueOf(ip.getXError()));

	eIdeal.addElement(eError);

	return (eIdeal);
    }

    /**
     * XXX
     */
    private XML createHorizon(Horizon hz) {

	XML eHorizon = new XML("horizon");
	eHorizon.setPrettyPrint(true);

	eHorizon.addXMLAttribute("error_dist",
				 hz.getErrorDistribution().toString());

	eHorizon.addXMLAttribute("type",
				 hz.getType().toString());

	eHorizon.addXMLAttribute("bound",
				 hz.getBoundType().toString());

	XML eUpper = new XML("upper", false);
	eUpper.setPrettyPrint(true);

	eUpper.addXMLAttribute("z",
			       String.valueOf(hz.getZUpper()));
	eUpper.addXMLAttribute("y",
			       String.valueOf(hz.getYUpper()));
	eUpper.addXMLAttribute("x",
			       String.valueOf(hz.getXUpper()));

	eHorizon.addElement(eUpper);

	XML eLower = new XML("lower", false);
	eLower.setPrettyPrint(true);

	eLower.addXMLAttribute("z",
			       String.valueOf(hz.getZLower()));
	eLower.addXMLAttribute("y",
			       String.valueOf(hz.getYLower()));
	eLower.addXMLAttribute("x",
			       String.valueOf(hz.getXLower()));

	eHorizon.addElement(eLower);

	XML eUpperError = new XML("upper_error", false);
	eUpperError.setPrettyPrint(true);

	eUpperError.addXMLAttribute("z",
				    String.valueOf(hz.getZUpperError()));
	eUpperError.addXMLAttribute("y",
				    String.valueOf(hz.getYUpperError()));
	eUpperError.addXMLAttribute("x",
				    String.valueOf(hz.getXUpperError()));

	eHorizon.addElement(eUpperError);

	XML eLowerError = new XML("lower_error", false);
	eLowerError.setPrettyPrint(true);

	eLowerError.addXMLAttribute("z",
				    String.valueOf(hz.getZLowerError()));
	eLowerError.addXMLAttribute("y",
				    String.valueOf(hz.getYLowerError()));
	eLowerError.addXMLAttribute("x",
				    String.valueOf(hz.getXLowerError()));

	eHorizon.addElement(eLowerError);

	return (eHorizon);
    }

    /**
     * XXX
     */
    private XML createSimulation(CalculationResult cr) {
	
	XML eSimulation = new XML("simulation");
	eSimulation.setPrettyPrint(true);

	XML eParams = new XML("parameters");
	eParams.setPrettyPrint(true);

	eParams.addXMLAttribute("name",
				cr.params.title);
	eParams.addXMLAttribute("iterations",
				String.valueOf(cr.params.iterations));

	/*
	  FIX ME - HAX()R to get around the fact that Apache's XML processor
	  doesn't *appear* to allow spaces in DTD enumerated attribute
	  lists, as the XML spec allows you to.
	*/
	if ("ideal point".equals(cr.params.deviation)) {
	    eParams.addXMLAttribute("deviation",
				    "idealPoint");
	} else {
	    eParams.addXMLAttribute("deviation",
				    cr.params.deviation.toString());
	}

	// FIX ME - What if it's not any of them?
	if (cr.params.partyProcessor instanceof NoErrorProcessor) {
	    XML eNone = new XML("none", false);
	    eNone.setPrettyPrint(true);
	    eParams.addElement(eNone);
	} else if (cr.params.partyProcessor instanceof PredefinedProcessor) {
	    XML ePredefined = new XML("predefined", false);
	    ePredefined.setPrettyPrint(true);
	    ePredefined.addXMLAttribute("multiplier",
                String.valueOf(
                    ((PredefinedProcessor)cr.params.partyProcessor).multiplier));
	    eParams.addElement(ePredefined);
	} else if (cr.params.partyProcessor instanceof GlobalProcessor) {
	    XML eGlobal = new XML("global", false);
	    eGlobal.setPrettyPrint(true);
	    eGlobal.addXMLAttribute("error_dist",
	        ((GlobalProcessor)
		    (cr.params.partyProcessor)).errorDistribution.toString());
	    eGlobal.addXMLAttribute("range",
		String.valueOf(((GlobalProcessor)
                    (cr.params.partyProcessor)).range));
	    eParams.addElement(eGlobal);
	}

	eSimulation.addElement(eParams);

	XML eResults = new XML("results");
	eResults.setPrettyPrint(true);

	eResults.addXMLAttribute("number",
				 String.valueOf(cr.number));

	for (int i = 0; i < cr.results.getRowCount(); i++) {
	    eResults.addElement(createRow(cr, i));
	}

	eSimulation.addElement(eResults);

	return (eSimulation);
    }

    /**
     * XXX
     */
    private XML createRow(CalculationResult cr, int rowIndex) {

	XML eRow = new XML("row");
	eRow.setPrettyPrint(true);

	eRow.addXMLAttribute("coalition",
			     cr.results.getValueAt(rowIndex,0).toString());
	eRow.addXMLAttribute("majority",
			     cr.results.getValueAt(rowIndex,1).toString());
	eRow.addXMLAttribute("minimum_winning",
			     cr.results.getValueAt(rowIndex,2).toString());
	eRow.addXMLAttribute("intersection",
			     cr.results.getValueAt(rowIndex,3).toString());
	eRow.addXMLAttribute("first_form",
			     cr.results.getValueAt(rowIndex,4).toString());
	eRow.addXMLAttribute("x_distance",
			     cr.results.getValueAt(rowIndex,5).toString());
	eRow.addXMLAttribute("y_distance",
			     cr.results.getValueAt(rowIndex,6).toString());
	eRow.addXMLAttribute("z_distance",
			     cr.results.getValueAt(rowIndex,7).toString());
	eRow.addXMLAttribute("max_distance",
			     cr.results.getValueAt(rowIndex,8).toString());
	eRow.addXMLAttribute("equilibrium",
			     cr.results.getValueAt(rowIndex,9).toString());
	eRow.addXMLAttribute("encompassed_weighted_mean",
			     cr.results.getValueAt(rowIndex,10).toString());

	return eRow;
    }

}
