package seng302.Mock;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import seng302.Model.Boat;
import seng302.XMLReader;

import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Joseph on 24/04/2017.
 */
public class BoatXMLReader extends XMLReader {
    private final Map<Integer, StreamedBoat> streamedBoatMap = new HashMap<>();
    private Map<Integer, StreamedBoat> participants = new HashMap<>();

    /**
     * Constructor for Boat XML Reader
     * @param filePath path of the file
     * @throws IOException error
     * @throws SAXException error
     * @throws ParserConfigurationException error
     */
    public BoatXMLReader(String filePath) throws IOException, SAXException, ParserConfigurationException {
        this(filePath, true);
    }

    /**
     * Constructor for Boat XML Reader
     * @param filePath file path to read
     * @param read whether or not to read and store the files straight away.
     * @throws IOException error
     * @throws SAXException error
     * @throws ParserConfigurationException error
     */
    public BoatXMLReader(String filePath, boolean read) throws IOException, SAXException, ParserConfigurationException {
        super(filePath);
        if (read) {
            read();
        }
    }

    /**
     * Constructor for Boat XML Reader
     * @param xmlString sting to read
     * @throws IOException error
     * @throws SAXException error
     * @throws ParserConfigurationException error
     */
    public BoatXMLReader(InputStream xmlString) throws IOException, SAXException, ParserConfigurationException {
        super(xmlString);
        read();
    }

    public void read() {
        readSettings();
        readShapes();
        readBoats();
    }

    /**
     * Reads boats settings.
     * INFORMATION FROM HERE IS IGNORED FOR NOW
     */
    private void readSettings() {

    }

    /**
     * Reads different kinds of boat.
     * INFORMATION FROM HERE IS IGNORED FOR NOW
     */
    private void readShapes() {

    }

    /**
     * Reads the boats in the race
     */
    private void readBoats() {
        Element nBoats = (Element) doc.getElementsByTagName("Boats").item(0);
        for (int i = 0; i < nBoats.getChildNodes().getLength(); i++) {
            Node boat = nBoats.getChildNodes().item(i);
            if (boat.getNodeName().equals("Boat") && boat.getAttributes().getNamedItem("Type").getTextContent().equals("Yacht")) {
                readSingleBoat(boat);
            }
        }
    }

    /**
     * Reads the information about one boat
     * Ignored values: ShapeID, StoweName, HullNum, Skipper, Type
     * @param boat The node to read boat data from.
     */
    private void readSingleBoat(Node boat) {
        StreamedBoat streamedBoat;
        String country = null;
        int sourceID = Integer.parseInt(boat.getAttributes().getNamedItem("SourceID").getTextContent());
        String boatName = boat.getAttributes().getNamedItem("BoatName").getTextContent();
        String shortName = boat.getAttributes().getNamedItem("ShortName").getTextContent();
        if (exists(boat, "Country")) country = boat.getAttributes().getNamedItem("Country").getTextContent();

        // Ignore all non participating boats
        if (participants.containsKey(sourceID)) {

            if (!streamedBoatMap.containsKey(sourceID)) {
                if (country != null) {
                    streamedBoat = new StreamedBoat(sourceID, boatName, country);
                } else {
                    streamedBoat = new StreamedBoat(sourceID, boatName, shortName);
                }
                streamedBoatMap.put(sourceID, streamedBoat);
                // Override boat with new boat
                participants.put(sourceID, streamedBoat);
            }

            for (int i = 0; i < boat.getChildNodes().getLength(); i++) {
                Node GPSPosition = boat.getChildNodes().item(i);
                if (GPSPosition.getNodeName().equals("GPSposition")) readBoatPositionInformation(sourceID, GPSPosition);
            }
        }
    }


    /**
     * Reads the positional information about a boat
     * Ignored values: FlagPosition, MastTop, Z value of GPSposition
     * @param sourceID The source ID of the boat.
     * @param GPSPosition The relative GPS position of the boat.
     */
    private void readBoatPositionInformation(int sourceID, Node GPSPosition) {
        // TODO Get relative point before implementing. (GPSposition is based off a relative point).
    }

    public void setParticipants(Map<Integer, StreamedBoat> participants) {
        this.participants = participants;
    }

    public List<Boat> getBoats() {
        return new ArrayList<>(streamedBoatMap.values());
    }
}
