/*
 * Upload.java
 *
 * Autor: Piotr Achinger <piotr.achinger at gmail.com>
 */
package dendrarium.portal.admin;

import dendrarium.core.entities.Packet;
import dendrarium.core.entities.Task;
import dendrarium.core.entities.TaskBusinessProcess;
import dendrarium.muzg.MUZG;
import dendrarium.trees.Forest;
import dendrarium.trees.xml.TreeXMLParser;
import dendrarium.utils.StreamUtils;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.hibernate.Session;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.faces.FacesMessages;
import org.jboss.seam.log.Log;

/**
 * Komponent odpowiedzialny za dodawanie zdan i paczek do systemu.
 * 
 * Upload paczki polega na wywolaniu setData(plikPaczkaZip) oraz upload().
 * 
 * Upload zadania do paczki packet polega na wywolaniu uploadForest(plikForestXml, packet).
 *
 * @author Piotr Achinger <piotr.achinger at gmail.com>
 */
@Name("upload")
@Scope(ScopeType.EVENT)
public class Upload implements Serializable {

    private byte[] data;

    @In
    Session session;

    @In
    private FacesMessages facesMessages;

    @Logger
    Log log;

    @In(create = true)
    MUZG muzg;

    private int sentenceNumber;

    /**
     * Dane wrzucanej paczki
     */
    public byte[] getData() {
        return data;
    }

    /**
     * Dane wrzucanej paczki.
     */
    public void setData(byte[] data) {
        this.data = data;
    }

    /**
     * Parsuje forest.xml dany jako byte array i dodaje nowe zadanie do paczki packet.
     */
    public void uploadForest(byte[] forest, Packet packet) {
        Task newTask = new Task();
        try {
            newTask.setForestXML(new String(forest, "UTF-8"));
        } catch (UnsupportedEncodingException ex) {
        }

        /* sparsowanie XML tylko po to, zeby wydobyc zdanie; ale dobrze jest
         * sie upewnic, ze bledny XML nie zostanie dodany
         */
        Forest f = null;
        try {
            f = new TreeXMLParser().parse(new ByteArrayInputStream(forest));
        } catch (Exception ex) {
            log.error("Blad podczas dodawania zadania: ", ex);
            facesMessages.add("Nie udało się dodać zadania");
            return;
        }
        newTask.setSentence(f.getText());
        newTask.setSent_id(f.getSent_id());
        newTask.setGrammar_no(f.getGrammar_no());
        if (newTask.getGrammar_no() == 0) {
            facesMessages.add("Zdanie: " + newTask.getSent_id() + " nie posiada identyfikatora wersji gramatyki");

            /* FIXME tymczasowe, zamienic na
             return; */

            newTask.setGrammar_no(1);
        }

        TaskBusinessProcess previousVersion = getPreviousVersion(newTask.getSent_id());
        if (previousVersion != null) {
            facesMessages.add("Zdanie " + newTask.getSent_id() + " jest już w systemie");
            return;
        }

        TaskBusinessProcess tbp = new TaskBusinessProcess(packet, newTask);
//        packet.getTasks().add(tbp);
        tbp.setIndex(sentenceNumber);
        sentenceNumber++;
        packet.getParagraphText().add(newTask.getSentence());
        tbp.setCurrent(true); /* ! */
        tbp.updateState();

        session.persist(tbp);
        facesMessages.add("Dodano zdanie " + tbp.getIndex() + "/" + tbp.getPacket().getName());
    }

    public void updateForest(byte[] forest) {
        Task newTask = new Task();
        try {
            newTask.setForestXML(new String(forest, "UTF-8"));
        } catch (UnsupportedEncodingException ex) {
        }

        /* sparsowanie XML tylko po to, zeby wydobyc zdanie; ale dobrze jest
         * sie upewnic, ze bledny XML nie zostanie dodany
         */
        Forest f = null;
        try {
            f = new TreeXMLParser().parse(new ByteArrayInputStream(forest));
        } catch (Exception ex) {
            log.error(ex);
            facesMessages.add("Nie udało się dodać zadania");
        }
        newTask.setSentence(f.getText());
        newTask.setSent_id(f.getSent_id());
        newTask.setGrammar_no(f.getGrammar_no());
        if (newTask.getGrammar_no() == 0) {
            facesMessages.add("Zdanie: " + newTask.getSent_id() + " nie posiada identyfikatora wersji gramatyki");
            return;
        }

        TaskBusinessProcess previousVersion = getPreviousVersion(newTask.getSent_id());
        if (previousVersion == null) {
            facesMessages.add("Zdania " + newTask.getSent_id() + " nie znaleziono w systemie");
            return;
        }
        if (newTask.getGrammar_no() <= previousVersion.getTask().getGrammar_no()) {
            facesMessages.add("Zdanie: " + newTask.getSent_id() + " jest w systemie w takiej samej lub nowszej wersji gramatyki");
            return;
        }
        Packet packet = previousVersion.getPacket();

        TaskBusinessProcess tbp = new TaskBusinessProcess(packet, newTask);
//        packet.getTasks().add(tbp);
        tbp.setIndex(previousVersion.getIndex()); /* ! */
        tbp.setCurrent(true); /* ! */

        session.persist(tbp);
        previousVersion.setCurrent(false);

        String muzgMsg = muzg.adapt(previousVersion, tbp);

        facesMessages.add("Uaktualniono zdanie " + tbp.getIndex() + "/" + tbp.getPacket().getName());
        if (!muzgMsg.equals("")) {
            facesMessages.add("Komunikat MUZGu: " + muzgMsg);
        }
    }

    /**
     * Dodaje paczke (byte[] data) do systemu.
     *
     * Paczka jest w formacie zip, zawiera plik paczka.xml (format opisany
     * w PacketXMLParser) oraz pliki forest.xml dla kolejnych zdan.
     */
    public void upload() throws Exception {
        ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(data)));
        ZipEntry entry;
        List<PacketXML> packetXMLs = new LinkedList();
        while ((entry = zis.getNextEntry()) != null) {
            if (entry.getName().endsWith(".packet.xml") || entry.getName().equals("packet.xml")) {
                byte[] packetxmldata = StreamUtils.obtainByteDataFromStream(zis);
                InputStream is = new ByteArrayInputStream(packetxmldata);
                PacketXML packetXML = new PacketXMLParser().parse(is);
                packetXMLs.add(packetXML);
            }
        }
        if (packetXMLs.isEmpty()) {
            throw new Exception("Brak plików packet.xml");
        }

        /* mapa (nazwa pliku forest.xml) -> zawartosc pliku */
        Map<String, byte[]> forestData = new HashMap<String, byte[]>();

        /* wypelniamy forestData -- wczytujemy pliki forest.xml */
        zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(data)));
        while ((entry = zis.getNextEntry()) != null) {
            if (!entry.getName().endsWith("packet.xml")) {
                byte[] forest = StreamUtils.obtainByteDataFromStream(zis);
                forestData.put(entry.getName(), forest);
            }
        }

        /* wrzucamy kolejne paczki */
        for (PacketXML packetXML : packetXMLs) {
            Packet packet = new Packet();

            packet.setName(packetXML.getName());
            session.persist(packet);

            /* wrzucamy w kolejnosci podanej w paczka.xml */
            sentenceNumber = 0;
            for (String forestName : packetXML.getForestFiles()) {
                byte[] forest = forestData.get(forestName);
                uploadForest(forest, packet);
            }

            facesMessages.add("Dodano paczkę " + packet.getName());
        }
    }

    /**
     * Dodaje zbior nowych wersji zdan
     */
    public void update() {

        try {
            ZipInputStream zis;// = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(data)));
            ZipEntry entry;

            /* uaktualniamy pliki forest.xml */
            zis = new ZipInputStream(new BufferedInputStream(new ByteArrayInputStream(data)));
            while ((entry = zis.getNextEntry()) != null) {
                String fileName = entry.getName();

                if (fileName.endsWith(".xml")) {
                    byte[] forest = StreamUtils.obtainByteDataFromStream(zis);
                    log.info("Updating forest: " + fileName);

                    updateForest(forest);
                }
            }

            facesMessages.add("Uaktualnienie powiodło się");
        } catch (Exception e) {
            log.error(e);
            facesMessages.add("Uaktualnienie nie powiodło się: " + e.getMessage());
        }


    }

    private TaskBusinessProcess getPreviousVersion(String sent_id) {
        List<TaskBusinessProcess> prev =
                                  session.createQuery("from TaskBusinessProcess tbp where tbp.task.sent_id=:sid order by grammar_no desc").setString("sid", sent_id).setMaxResults(1).list();

        if (!prev.isEmpty()) {
            return prev.get(0);
        } else {
            return null;
        }

    }
}
