/*
 * $Id: TdfDocConverter.java,v 1.18 2006/07/17 13:04:03 lawern Exp $
 *
 * Copyright (c) Siemens AG 2008 
 *           (c) Universität Paderborn 2008 
 * Licensed under EUPL 1.1. 
 *
 * $Log: TdfDocConverter.java,v $
 * Revision 1.18  2006/07/17 13:04:03  lawern
 * Neuer typografiegewichteter Index und k-NN-Klassifikation aus meiner Diss.
 *
 * Revision 1.17  2006/06/08 07:18:22  lawern
 * Formatierung nach Java Code Convention
 *
 * Revision 1.16  2006/06/06 20:36:00  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.15  2006/03/23 11:36:42  marbobo
 * + Mögliche NPE abgefangen
 *
 * Revision 1.14  2005/07/05 07:33:18  lawern
 * Bugfix
 *
 * Revision 1.13  2005/03/30 08:47:45  lawern
 * UTF-8-Anpassung
 *
 * Revision 1.12  2005/03/24 12:57:05  lawern
 * Umstellung des TDF-Formats auf Version 1.1 in UTF-8-Kodierung.
 *
 * Revision 1.11  2005/03/21 14:49:50  marbobo
 * + Bugfix
 *
 * Revision 1.10  2004/11/22 12:50:49  koppy
 * +getSection(String section, Set sectionEndPhrases)
 *
 * Revision 1.9  2004/10/08 13:57:32  koppy
 * + getSection(...)
 *
 * Revision 1.8  2004/06/27 17:12:46  marbobo
 * + Quellcode maintenance
 *
 * Revision 1.7  2003/11/30 13:02:34  koppy
 * ein br.close() auskommentiert
 *
 * Revision 1.6  2003/11/29 22:13:52  koppy
 * InputStreams in VKCInputStream gekappselt
 *
 * Revision 1.5  2003/09/22 07:35:42  lawern
 * Codierung von < und > in TDF-Dokumenten erweitert.
 *
 * Revision 1.4  2003/09/09 08:39:21  lawern
 * getAsciiStream ergänzt
 *
 * Revision 1.3  2003/06/06 11:39:11  lawern
 * Titel wird jetzt korrekt ausgelesen
 *
 * Revision 1.2  2003/04/23 10:19:23  lawern
 * *** empty log message ***
 *
 * Revision 1.1  2003/03/20 09:11:34  lawern
 * Konverter und Analyse überarbeitet
 *
 *
 */
package vkc.converter;

import cern.colt.list.IntArrayList;

import cern.colt.map.OpenIntIntHashMap;

import vkc.misc.VKCLogManager;

import vkc.util.FileUtils;
import vkc.util.StringUtils;
import vkc.util.VKCInputStream;
import vkc.util.VKCUtils;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.UnsupportedEncodingException;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;


/**
 * Klasse zum Konvertieren von TDF-Dokumenten.
 *
 * @author lawern
 */
public class TdfDocConverter extends DocConverter {
    private String title = null;
    private ArrayList elements;
    private boolean separated = false;
    private OpenIntIntHashMap weights = new OpenIntIntHashMap();
    public int mostUsedWeight = -1;
    public int minWeight = Integer.MAX_VALUE;

    public TdfDocConverter() {
        elements = new ArrayList();
    }

    /**
     * Diese Methode konvertiert das übergebene Dokument in die in der
     * Collection definierten Formate. Der Parameter conversions wird zur Zeit
     * noch nicht ausgewertet.
     *
     * @param docStream
     *            InputStream mit dem zu konvertierenden Dokument
     * @param conversions
     *            Collection von VKCDocumentTypes welche die zu erzeugenden
     *            Konvertierungen beschreibt.
     */
    public void convert(InputStream docFile, Collection conversions) {
        BufferedReader br;
        byte[] version = new byte[6];

        try {
            docFile.read(version);

            if (new String(version).equals("<V:1.0")) {
                br = new BufferedReader(new InputStreamReader(docFile));
            } else {
                br = new BufferedReader(new InputStreamReader(docFile, "UTF-8"));
            }

            br.readLine();
            convert(br, conversions);
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "4", "Error in TDF-Stream.", e);
        }
    }

    /**
     * Diese Methode konvertiert das übergebene Dokument in die in der
     * Collection definierten Formate. Der Parameter conversions wird zur Zeit
     * noch nicht ausgewertet.
     *
     * @param reader
     *            Reader mit dem zu konvertierenden Dokument
     * @param conversions
     *            Collection von VKCDocumentTypes welche die zu erzeugenden
     *            Konvertierungen beschreibt.
     */
    public void convert(Reader reader, Collection conversions) {
        String line;
        char command;
        char lastCommand = '0';

        BufferedReader br = new BufferedReader(reader);

        try {
            while ((line = br.readLine()) != null) {
                line = line.trim();

                if (line.length() > 1) {
                    command = line.charAt(1);

                    switch (command) {
                    // Versionsnummer auswerten
                    case 'V':

                        String version = line.substring(line.indexOf(":") + 1,
                                line.length() - 1);

                        if (!version.equals("1.0") && !version.equals("1.1")) {
                            VKCLogManager.errorLog(this, "2",
                                "Wrong TDF-Version: " + version);

                            return;
                        }

                        break;

                    // Titel auswerten
                    case 'T':
                        title = VKCUtils.tdfDecode(line.substring(line.indexOf(
                                        ":") + 1, line.length() - 1));

                        break;

                    // neuer Absatz
                    case 'P':
                        break;

                    // neuer Satz
                    case 'S':

                        if (lastCommand == 'P') {
                            separated = true;
                        } else {
                            separated = false;
                        }

                        parseLine(line);

                        break;

                    // undefined
                    default:
                        VKCLogManager.errorLog(this, "1",
                            "Can't parse this line: " + line);

                        return;
                    }

                    lastCommand = command;
                }
            }

            // br.close();
        } catch (IOException ioe) {
            VKCLogManager.errorLog(this, "1", "Error in TDF-Stream.", ioe);
            FileUtils.closeReader(br);
        }

        //Bestimmen, welches Gewicht am meisten verwendet wird.
        IntArrayList usedWeights = weights.keys();
        int maxWeightCounter = -1;
        int tmpWeight;
        int tmpWeightCounter;

        for (int i = usedWeights.size() - 1; i >= 0; i--) {
            tmpWeight = usedWeights.getQuick(i);
            tmpWeightCounter = weights.get(tmpWeight);

            if (tmpWeightCounter > maxWeightCounter) {
                mostUsedWeight = tmpWeight;
                maxWeightCounter = tmpWeightCounter;
            }
        }
    }

    /**
     * Parst eine S-Zeile
     *
     * @param line
     *            Zeile die geparst werden soll
     */
    private final void parseLine(String line) {
        int elementSeparatorIndex;
        String element = null;
        int weight;
        String text = null;
        int separatorIndex;

        boolean newLine = true;

        try {
            do {
                elementSeparatorIndex = line.indexOf("><S:");

                if (elementSeparatorIndex < 0) {
                    // letztes Element
                    element = line.substring(0, line.length() - 1);
                } else {
                    element = line.substring(0, elementSeparatorIndex);
                    line = line.substring(elementSeparatorIndex + 4);
                }

                // erstes Element
                if (element.startsWith("<S:")) {
                    element = element.substring(3);
                }

                // Element zerlegen
                separatorIndex = element.indexOf(':');
                weight = new Integer(element.substring(0, separatorIndex)).intValue();
                text = VKCUtils.tdfDecode(element.substring(separatorIndex + 1));

                if (weight < minWeight) {
                    minWeight = weight;
                }

                //Element der Collection hinzufügen
                elements.add(new TdfElement(text, weight, newLine, separated));

                //weights erhöhen
                if (weights.containsKey(weight)) {
                    int weightCounts = weights.get(weight);
                    weights.put(weight, weightCounts + text.length());
                } else {
                    weights.put(weight, text.length());
                }

                newLine = false;
            } while (elementSeparatorIndex > 0);
        } catch (Exception e) {
            VKCLogManager.errorLog(this, "3", "Error in TDF-Sentence: " + line,
                e);
        }
    }

    /**
     * Liefert eine Collection mit Objekten vom Typ TdfElement zurück, welche
     * das Dokument beschreiben.
     *
     * @return Collection mit Objekten vom Typ Td
     */
    public Collection getElements() {
        return elements;
    }

    /**
     * Liefert den Titel des Dokuments zurück.
     *
     * @return Titel des Dokuments oder <code>null</code>
     */
    public String getTitle() {
        return title;
    }

    /**
     * Liefert nach dem Aufruf der convert-Methode den Stream der
     * ASCII-Konvertierung.
     *
     * @return Stream der ASCII-Konvertierung
     */
    public VKCInputStream getAsciiStream() {
        TdfElement tmp = null;
        String line = "";

        if (elements != null) {
            StringBuffer text = new StringBuffer(1024);

            // Text erzeugen
            for (int i = 0; i < elements.size(); i++) {
                tmp = (TdfElement) elements.get(i);

                if (tmp.newLine) {
                    text.append(line.trim());
                    line = "";

                    text.append('\n');

                    if (tmp.separated) {
                        text.append('\n');
                    }
                }

                line += (tmp.text + " ");
            }

            // letzte Zeile schreiben
            text.append(line.trim());

            // System.out.write(text.toString().getBytes(), 0, text.length());
            byte[] asciiArray = null;

            try {
                asciiArray = text.toString().getBytes("UTF-8");
            } catch (UnsupportedEncodingException e) {
                asciiArray = new byte[0];
            }

            return new VKCInputStream(new ByteArrayInputStream(asciiArray),
                asciiArray.length);
        }

        return null;
    }

    /**
     * Gibt einen Abschnitt des TDF Dokumentes zurück.
     *
     * @param section
     *            Name des Abschnitts, z.B. "Stand der Technik"
     * @return
     */
    public StringBuffer getSection(String section) {
        return this.getSection(section, null);
    }

    /**
     * Gibt einen Abschnitt des TDF Dokumentes zurück.
     *
     * @param section
     *            Name des Abschnitts, z.B. "Stand der Technik"
     * @param sectionEndPhrases
     *            Menge von Phrasen, die das Ende des zu extrahierenden
     *            Abschnittes markieren (exklusiv)
     * @return
     */
    public StringBuffer getSection(String section, Set sectionEndPhrases) {
        if (elements == null) {
            return null;
        }

        TdfElement tdf = null;
        String line = "";

        StringBuffer sectionText = new StringBuffer(1024);

        boolean sectionFound = false;
        int sectionTextWeight = -1;

        // Text erzeugen
        for (int i = 0; i < elements.size(); i++) {
            tdf = (TdfElement) elements.get(i);

            if (tdf == null) {
                continue;
            }

            if (!sectionFound && !tdf.text.trim().equals(section)) {
                continue;
            }

            if (!sectionFound) {
                sectionTextWeight = ((TdfElement) elements.get(i + 1)).weight;
                sectionFound = true;

                continue;
            }

            if (StringUtils.isEmpty(tdf.text)) {
                continue;
            }

            // Abbruchbedingung
            if (sectionEndPhrases == null) {
                if (tdf.weight > sectionTextWeight) {
                    break;
                }
            } else {
                if (sectionEndPhrases.contains(tdf.text.trim())) {
                    break;
                }
            }

            if (tdf.newLine) {
                sectionText.append(line.trim());
                line = "";

                sectionText.append('\n');

                if (tdf.separated) {
                    sectionText.append('\n');
                }
            }

            line += (tdf.text + " ");
        }

        // letzte Zeile schreiben
        sectionText.append(line.trim());

        return sectionText;
    }
}
