package seng302.Data;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import seng302.Exceptions.InvalidBoatDataException;
import seng302.Model.Boat;


import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
import java.util.List;

/**
 * Created by esa46 on 25/04/17.
 */
public class BoatData {

    Document doc;
    private List<Boat> boatData;


    public BoatData(List<Boat> boatData) {
        this.boatData = boatData;
    }

    /**
     * Creates an AC35 officially formatted xml description of boats competing in a race
     *
     * @return String containing xml-formatted boats description
     */
    public String createXML() {

        try {
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();

            //root element
            doc = docBuilder.newDocument();
            Element rootElement = doc.createElement("BoatConfig");
            doc.appendChild(rootElement);

            //Boats element
            Element boats = doc.createElement("Boats");
            rootElement.appendChild(boats);

            appendIndividualBoats(boats);

            // write the content into xml file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            DOMSource source = new DOMSource(doc);

            //Serialize document.
            StringWriter stringWriter = new StringWriter();
            StreamResult result = new StreamResult(stringWriter);
            transformer.transform(source, result);


            System.out.println(stringWriter.toString());
            return stringWriter.toString();


        } catch (ParserConfigurationException pce) {
            throw new InvalidBoatDataException();
        } catch (TransformerException tfe) {
            throw new InvalidBoatDataException();
        }
    }


    /**
     * Runs through competing boats, creating an element for each
     *
     * @param boatsElement boats element to be added to
     */
    private void appendIndividualBoats(Element boatsElement) {

        for (int i = 0; i < boatData.size(); i++) {
            Element boat = doc.createElement("Boat");
            appendType(boat);
            appendSourceID(boat, i);
            appendShapeID(boat);
            appendHullNum(boat);
            appendStoweName(boat, i);
            appendShortName(boat, i);
            appendBoatName(boat, i);
            appendGPSCoords(boat, i);
            //Write boat to boats
            boatsElement.appendChild(boat);
        }
    }

    /**
     * Creates and appends type attribute of a boat
     *
     * @param boat element being added to
     */
    private void appendType(Element boat) {
        //Type attribute
        Attr attrType = doc.createAttribute("Type");
        attrType.setValue("Yacht");
        boat.setAttributeNode(attrType);
    }

    /**
     * Creates and appends sourceID attribute of a boat
     *
     * @param boat element being added to
     * @param i    boat number
     */
    private void appendSourceID(Element boat, int i) {
        //SourceID attribute
        Attr attrSourceID = doc.createAttribute("SourceID");
        attrSourceID.setValue(Integer.toString(boatData.get(i).getSourceID()));
        boat.setAttributeNode(attrSourceID);
    }

    /**
     * Creates and appends shapeID attribute of a boat
     *
     * @param boat element being added to
     */
    private void appendShapeID(Element boat) {
        //ShapeID attribute
        Attr attrShapeID = doc.createAttribute("ShapeID");
        attrShapeID.setValue("0");
        boat.setAttributeNode(attrShapeID);
    }

    /**
     * Creates and appends hull name attribute of a boat
     *
     * @param boat element being added to
     */
    private void appendHullNum(Element boat) {
        //HullNum attribute
        Attr attrHullNum = doc.createAttribute("HullNum");
        attrHullNum.setValue("RG01");
        boat.setAttributeNode(attrHullNum);
    }

    /**
     * Creates and appends stow name attribute of a boat
     *
     * @param boat element being added to
     * @param i    boat number
     */
    private void appendStoweName(Element boat, int i) {
        //StoweName attribute
        Attr attrStoweName = doc.createAttribute("StoweName");
        attrStoweName.setValue(boatData.get(i).getAbbrev());
        boat.setAttributeNode(attrStoweName);
    }

    /**
     * Creates and appends short name attribute of a boat
     *
     * @param boat element being added to
     * @param i    boat number
     */
    private void appendShortName(Element boat, int i) {
        //ShortName attribute
        Attr attrShortName = doc.createAttribute("ShortName");
        attrShortName.setValue(boatData.get(i).getAbbrev());
        boat.setAttributeNode(attrShortName);
    }

    /**
     * Creates and appends boat name attribute of a boat
     *
     * @param boat element being added to
     * @param i    boat number
     */
    private void appendBoatName(Element boat, int i) {
        //BoatName attribute
        Attr attrBoatName = doc.createAttribute("BoatName");
        attrBoatName.setValue(boatData.get(i).getName());
        boat.setAttributeNode(attrBoatName);
    }

    /**
     * Creates and appends gps attributes of a boat
     *
     * @param boat element being added to
     * @param i    boat number
     */
    private void appendGPSCoords(Element boat, int i) {
        //GPSCoord for element
        Element GPSCoord = doc.createElement("GPSposition");
        //Z axis attribute
        Attr attrZCoord = doc.createAttribute("Z");
        attrZCoord.setValue("0");
        GPSCoord.setAttributeNode(attrZCoord);

        //Y axis attribute
        Attr attrYCoord = doc.createAttribute("Y");
        attrYCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLatitude()));
        GPSCoord.setAttributeNode(attrYCoord);

        //X axis attribute
        Attr attrXCoord = doc.createAttribute("X");
        attrXCoord.setValue(Double.toString(boatData.get(i).getCurrentPosition().getLongitude()));
        GPSCoord.setAttributeNode(attrXCoord);

        //Write GPSCoord to boat
        boat.appendChild(GPSCoord);
    }

}
