package seng302.utilities;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import seng302.model.stream.xml.generator.RaceXMLTemplate;
import seng302.model.stream.xml.generator.RegattaXMLTemplate;
import seng302.gameServer.messages.XMLMessageSubType;

/**
 * An XML generator to generate the Race, Boat, and Regatta XML dynamically
 */
public class XMLGenerator {
    private static final String XML_TEMPLATE_DIR = "/server_config/xml_templates";
    private static final String REGATTA_TEMPLATE_NAME = "regatta.ftlh";
    private static final String BOATS_TEMPLATE_NAME = "boats.ftlh";
    private static final String RACE_TEMPLATE_NAME = "race.ftlh";
    private Configuration configuration;
    private RegattaXMLTemplate regatta;
    private RaceXMLTemplate race;

    /**
     * Set up a configuration instance for Apache Freemake
     */
    private void setupConfiguration() {
        configuration = new Configuration(Configuration.VERSION_2_3_26);

        try {
            configuration.setClassForTemplateLoading(getClass(), XML_TEMPLATE_DIR);
        } catch (NullPointerException e){
            System.out.println("[FATAL] Server could not load XML Template directory, ensure this directory isn't empty");
        }
    }

    /**
     * Create an instance of the XML Generator
     */
    public XMLGenerator(){
        setupConfiguration();
    }

    /**
     * Set the race regatta to send to players
     * Note: This must be set before a regatta message can be generated
     * @param regatta The race regatta
     */
    public void setRegattaTemplate(RegattaXMLTemplate regatta) {
        this.regatta = regatta;
    }

    /**
     * Set the race to send to players
     * Note: This must be set before a boat or race message can be generated
     * @param race The race
     */
    public void setRaceTemplate(RaceXMLTemplate race) {
        this.race = race;
    }

    /**
     * Parse an XML template and generate the output as a string
     * @param templateName The templates file name
     * @param type The XML message sub type
     */
    private String parseToXmlString(String templateName, XMLMessageSubType type) throws IOException, TemplateException {
        Template template;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        OutputStreamWriter writer = new OutputStreamWriter(os);

        template = configuration.getTemplate(templateName);

        switch (type) {
            case REGATTA:
                template.process(regatta, writer);
                break;

            case BOAT:
                template.process(race, writer);
                break;

            case RACE:
                template.process(race, writer);
                break;

            default:
                throw new UnsupportedOperationException();
        }

        try {
            return os.toString("UTF-8");
        } catch (UnsupportedEncodingException e) {
            System.out.println("[FATAL] UTF-8 Not supported");
            return null;
        }
    }

    /**
     * Get the race regatta as a string
     * Note: Regatta must be set before calling this
     * @return String containing the regatta XML, null if there was an error
     */
    public String getRegattaAsXml(){
        String result = null;

        if (regatta == null) return null;

        try {
            result = parseToXmlString(REGATTA_TEMPLATE_NAME, XMLMessageSubType.REGATTA);
        } catch (TemplateException e) {
            System.out.println("[FATAL] Error parsing regatta");
        } catch (IOException e) {
            System.out.println("[FATAL] Error reading regatta");
        }

        return result;
    }

    /**
     * Get the boats XML as a string
     * Note: Race must be set before calling this
     * @return String containing the boats XML, null if there was an error
     */
    public String getBoatsAsXml() {
        String result = null;

        if (race == null) return null;

        try {
            result = parseToXmlString(BOATS_TEMPLATE_NAME, XMLMessageSubType.BOAT);
        } catch (TemplateException e) {
            System.out.println("[FATAL] Error parsing boats");
        } catch (IOException e) {
            System.out.println("[FATAL] Error reading boats");
        }

        return result;
    }

    /**
     * Get the race XML as a string
     * Note: Race must be set before calling this
     * @return String containing the race XML, null if there was an error
     */
    public String getRaceAsXml() {
        String result = null;

        if (race == null) return null;

        try {
            result = parseToXmlString(RACE_TEMPLATE_NAME, XMLMessageSubType.RACE);
        } catch (TemplateException e) {
            System.out.println("[FATAL] Error parsing race");
        } catch (IOException e) {
            System.out.println("[FATAL] Error reading race");
        }

        return result;
    }
}