/**************************************************************************
 OmegaT - Computer Assisted Translation (CAT) tool 
          with fuzzy matching, translation memory, keyword search, 
          glossaries, and translation leveraging into updated projects.

 Copyright (C) 2010 Alex Buloichik, Didier Briel
               2011 Didier Briel, Guido Leenders
               2019 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This file is part of OmegaT.

 OmegaT is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 OmegaT is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 **************************************************************************/

package org.omegat.gui.glossary;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.omegat.core.Core;
import org.omegat.util.Language;
import org.omegat.util.Log;
import org.omegat.util.OStrings;
import org.omegat.util.Preferences;

import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;

/**
 * Reader for TBX glossaries.
 * 
 * @author Alex Buloichik <alex73mail@gmail.com>
 * @author Didier Briel
 * @author Guido Leenders
 * @author Thomas Cordonnier
 */
public class GlossaryReaderTBX {
    private static final XMLInputFactory factory = XMLInputFactory.newInstance();	

    public static List<GlossaryEntry> read(final File file, boolean priorityGlossary) throws Exception {
        return readMartif(new FileInputStream(file), priorityGlossary);
    }

    public static List<GlossaryEntry> read(final String data, boolean priorityGlossary) throws Exception {
        return readMartif(new ByteArrayInputStream(data.getBytes()), priorityGlossary);
    }

    private static List<GlossaryEntry> readMartif(final InputStream is, boolean priorityGlossary) throws Exception {
		try {
			XMLStreamReader reader = factory.createXMLStreamReader(is);
			List<GlossaryEntry> result = new ArrayList<GlossaryEntry>();
			while (reader.hasNext()) {
				if (reader.next() == XMLStreamConstants.START_ELEMENT)
					if (reader.getLocalName().equals("termEntry")) readTermEntry(result, reader, priorityGlossary);
			}
            Log.log("Read " + result.size() + " entries");
			return result;
		} finally {
			try { is.close(); } catch (Exception e) {}
		}
	}
	
    private static void readTermEntry(List<GlossaryEntry> result, final XMLStreamReader reader, boolean priorityGlossary) throws Exception {
        String sLang = Core.getProject().getProjectProperties().getSourceLanguage().getLanguageCode();
        String tLang = Core.getProject().getProjectProperties().getTargetLanguage().getLanguageCode();

        StringBuilder note = new StringBuilder();
        StringBuilder descTerm = new StringBuilder();
        StringBuilder descTig = new StringBuilder();
        List<String> sTerms = new ArrayList<String>();
        List<String> tTerms = new ArrayList<String>();
		while (reader.hasNext()) {
            int next = reader.next();
			if ((next == XMLStreamConstants.END_ELEMENT) && (reader.getLocalName().equals("termEntry"))) {
				StringBuilder comment = new StringBuilder();
				appendLine(comment, descTerm.toString());
				appendLine(comment, descTig.toString());
				appendLine(comment, note.toString());
				
				for (String s : sTerms) {
					boolean addedForLang = false;
					for (String t : tTerms) {
						result.add(new GlossaryEntry(s, t, comment.toString(), priorityGlossary));
						addedForLang = true;
					}
					if (!addedForLang) { // An entry is created just to get the definition
						result.add(new GlossaryEntry(s, "", comment.toString(), priorityGlossary));
					}
				}
				return;
			}
			
			if (next == XMLStreamConstants.START_ELEMENT)
				if (reader.getLocalName().equals("descripGrp")) appendDescOrNote(reader, descTerm);
				else if (reader.getLocalName().equals("descrip")) appendDescOrNote(reader, descTerm);
				else if (reader.getLocalName().equals("note")) appendDescOrNote(reader, descTerm);
				else if (reader.getLocalName().equals("langSet")) {
					String lang = reader.getAttributeValue(null, "lang");
					lang = new Language(lang).getLanguageCode();
					appendDescOrNote(reader, descTig);
				TIGLOOP:
					while (reader.hasNext()) {
                        next = reader.next();
						if ((next == XMLStreamConstants.END_ELEMENT) && (reader.getLocalName().equals("langSet"))) break TIGLOOP;
						
						if (next == XMLStreamConstants.START_ELEMENT)
							if (reader.getLocalName().equals("tig") || reader.getLocalName().equals("ntig")) {
								if (sLang.equalsIgnoreCase(lang)) sTerms.add(readContent(reader, "term"));
								else if (tLang.equalsIgnoreCase(lang)) tTerms.add(readContent(reader, "term"));
							}
							else if (reader.getLocalName().equals("termNote")) {
								if (tLang.equalsIgnoreCase(lang)) appendDescOrNote(reader, note);
							}
							else if (reader.getLocalName().equals("descripGrp")) appendDescOrNote(reader, descTig);
							else if (reader.getLocalName().equals("descrip")) appendDescOrNote(reader, descTig);
					}
				}
		}
    }

    /**
     * Add description or note into StringBuilder.
     */
    protected static void appendDescOrNote(final XMLStreamReader reader, StringBuilder str) throws XMLStreamException {
		String line = null;
		if (reader.getLocalName().equals("descrip"))
			if ("context".equals(reader.getAttributeValue(null, "type"))) {
				if (Preferences.isPreferenceDefault(Preferences.GLOSSARY_TBX_DISPLAY_CONTEXT, true)) {
					line = "context: " + readContent(reader, "descrip");
				}
			} else {
				line = "" + reader.getAttributeValue(null, "type") + ": " + readContent(reader, "descrip");
			}
		if (reader.getLocalName().equals("descripGrp"))
			while (reader.hasNext()) {
                int next = reader.next();
				if (next == XMLStreamConstants.END_ELEMENT)
					if (reader.getLocalName().equals("descripGrp")) break;
				if (next == XMLStreamConstants.START_ELEMENT)
					if (reader.getLocalName().equals("descrip")) appendDescOrNote(reader, str);
			}
		if (reader.getLocalName().equals("termNote")) line = readContent(reader, "termNote");
		if (reader.getLocalName().equals("note")) line = readContent(reader, "note");
		if (reader.getLocalName().equals("admin")) line = readContent(reader, "admin");
		
		if (line != null) appendLine(str, line);
	}

    protected static void appendLine(final StringBuilder str, String line) {
        if (line.isEmpty()) { // No need to append empty lines
            return;
        }
        if (str.length() > 0) {
            str.append('\n');
        }
        str.append(line);
    }
	
    protected static String readContent(final XMLStreamReader reader, String mark) throws XMLStreamException {
        StringBuilder res = new StringBuilder(); boolean in = reader.getLocalName().equals(mark); int next;
		while (reader.hasNext()) {
            next = reader.next();
			if (next == XMLStreamConstants.END_ELEMENT)
				if (reader.getLocalName().equals(mark)) break;
				else if (reader.getLocalName().equals("hi")) if (in) res.append("* ");
			if (next == XMLStreamConstants.START_ELEMENT)
				if (reader.getLocalName().equals(mark)) in = true;
				else if (reader.getLocalName().equals("hi")) if (in) res.append(" *");
			if (next == XMLStreamConstants.CHARACTERS)
				if (in) res.append(reader.getText());
		}
        return res.toString();
    }
	
}
