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

 Copyright (C) 2000-2006 Keith Godfrey and Maxym Mykhalchuk
               2013 Aaron Madlon-Kay, Alex Buloichik
               2014-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.util.Collection;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.List;
import java.util.Map;

import javax.swing.text.AttributeSet;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyleConstants;
import java.awt.Color;
import javax.script.*;

import org.omegat.core.Core;
import org.omegat.core.glossaries.IGlossary;
import org.omegat.core.glossaries.GlossaryEntryStore;
import org.omegat.util.StringUtil;
import org.omegat.util.Preferences;

/**
 * An entry as displayed in the matches pane: can be a merge of existing stored entries.
 * 
 * @author Keith Godfrey
 * @author Aaron Madlon-Kay
 * @author Alex Buloichik
 * @author Thomas Cordonnier
 */
public abstract class GlossaryEntryView {
    protected GlossaryEntryView(String src) {
        mSource = StringUtil.normalizeUnicode(src);
    }

    public String getSrcText() {
        return mSource;
    }

    /**
     * Returns contained entries
     */
    public abstract Collection<GlossaryEntryStore> getMergedEntries();

    public abstract Collection<String> getLocTerms(boolean unique);
    
    public abstract Iterable<GlossaryEntryStore> getEntriesFor(String tra);    
    
    public abstract boolean hasPriorities(String tra);
		
	public static ScriptEngine engine = null;

    public final StyledString toStyledString(boolean inTooltip) {
        StyledString result = new StyledString();
		try {
			if (engine == null) {
				engine = new ScriptEngineManager().getEngineByName("Groovy");
				String template = Preferences.getPreferenceDefault(Preferences.GLOSSARY_PANE_TEMPLATE, GlossaryTextArea.DEFAULT_TEMPLATE);
				if (template.startsWith("file:")) {
					template = template.substring(5);
					java.io.File tplFile = new java.io.File (template);
					if (! tplFile.exists()) {
						template = Preferences.getPreferenceDefault(Preferences.SCRIPTS_DIRECTORY, org.omegat.util.StaticUtils.installDir() + "/scripts") + "/layout/glossary/" + template;
						tplFile = org.omegat.gui.scripting.ScriptingWindow.toJavaFile (template);
					}
					if (tplFile.exists()) 
						template = new String(java.nio.file.Files.readAllBytes(tplFile.toPath()));				
					else {
						result.appendError("Cannot find " + tplFile + " ; using default\n");
						template = GlossaryTextArea.DEFAULT_TEMPLATE;
					} 							
				}
				// Forbid access to Core, but gives access to source and target language, which may enable different algorithm per language
				engine.eval ("package org.omegat.core; public class Core {}"); // forbids access to core from the script
				if (template.contains("sourceLang")) engine.put ("sourceLang", Core.getProject().getProjectProperties().getSourceLanguage().toString()); 
				if (template.contains("targetLang")) engine.put ("targetLang", Core.getProject().getProjectProperties().getTargetLanguage().toString()); 
				if (template.contains("inTooltip")) engine.put ("inTooltip", inTooltip); 
				engine.eval ("def format(entry,result) {\n" + template + "\n}");
			}
			engine.put("result", result); engine.put("entry", this);
			engine.eval ("format(entry, result)");
		} catch (Exception e) {
			result.appendError (e.getMessage());
		}
        return result;
    }
    
    /**
     * If a combined glossary entry contains ',', it needs to be bracketed by
     * quotes, to prevent confusion when entries are combined. However, if the
     * entry contains ';' or '"', it will automatically be bracketed by quotes.
     * 
     * @param entry
     *            A glossary text entry
     * @return A glossary text entry possibly bracketed by quotes
     */
    private static String bracketEntry(String entry) {
        if (entry.contains(",") && !(entry.contains(";") || entry.contains("\"")))
            entry = '"' + entry + '"';
        return entry;
    }
	
    static class StyledString {
        static class Part {
            protected int start,len;

            public Part(int start, int len) {
                this.start = start; this.len = len; 
            }
			
            public void setEnd (int pos) {
                this.len = pos - start;
            }
        }
        static class AttributesPart extends Part {
            private AttributeSet attr;
            
            public AttributesPart(int start, int len, AttributeSet attr) {
                super(start, len); this.attr = attr;
            }
            
            public void apply (StyledDocument doc) {
                doc.setCharacterAttributes(start, len, attr, false);
            }

            public AttributesPart shift (int start) {
                return new AttributesPart (this.start + start, this.len, this.attr);
            }
			
            void insertHtml(StringBuilder sb, AttributesPart[] next) {
                int shift1 = 0, shift2 = 0;
                Color color = StyleConstants.getForeground(attr);
                if (color != Color.black) {
                    String colorString = String.format("%02x%02x%02x", color.getRed(), color.getGreen(), color.getBlue());
                    sb.insert(start + len, "</font>");
                    sb.insert(start, "<font color=#" + colorString + ">");
                    shift1 = 20; // "<font color=#xxxxxx>".length()
                    shift2 = 7; // "</font>".length()
                }
                else if (StyleConstants.isBold(attr)) {
                    sb.insert(start + len, "</b>");
                    sb.insert(start, "<b>");
                    shift1 = 3; // "<b>".length()
                    shift2 = 4; // "</b>".length()
                }
                if (next == null) return; 
                for (int i = 0; i < next.length; i++)
                    if (next[i].start >= this.start + this.len) next[i] = next[i].shift(shift1 + shift2);
                    else if (next[i].start >= this.start) next[i] = next[i].shift(shift1);
            }
        }
        static class TooltipPart extends Part {
            private String text;
            
            public TooltipPart(int start, int len, String text) {
                super(start, len); this.text = text;
            }
            
            public TooltipPart shift (int start) {
                return new TooltipPart (this.start + start, this.len, this.text);
            }

            public boolean contains(int pos) { return (pos >= start) && (pos <= start + len); }
            
            public String getText() { return text; }
        }
    
        public StringBuilder text = new StringBuilder();
        public LinkedList<AttributesPart> attrParts = new LinkedList<AttributesPart>();
        public LinkedList<AttributesPart> attrBolds = new LinkedList<AttributesPart>();
        public LinkedList<TooltipPart> tooltips = new LinkedList<TooltipPart>();

        public void markBoldStart() {
            attrBolds.add(new AttributesPart(text.length(), 0, GlossaryTextArea.PRIORITY_ATTRIBUTES));
        }

        public void markBoldEnd() {
            attrBolds.getLast().setEnd (text.length());
        }
		
        public void startTooltip(String tip) {
            tooltips.add(new TooltipPart(text.length(), 0, tip));
        }

        public void endTooltip() {
            tooltips.getLast().setEnd (text.length());
        }		
        
        public StyledString append(StyledString str) {
            int off = text.length();
            text.append(str.text);
            for (AttributesPart attr: str.attrParts) this.attrParts.add (attr.shift(off));
            for (AttributesPart attr: str.attrBolds) this.attrBolds.add (attr.shift(off));	
            for (TooltipPart tt: str.tooltips) this.tooltips.add (tt.shift(off));	
            return this;
        }

        public StyledString append(String str) {
            text.append(str); return this;
        }
		
        public StyledString appendError(String src) {
            attrParts.add (new AttributesPart(text.length(), src.length(), org.omegat.util.gui.Styles.createAttributeSet(java.awt.Color.RED, null, true, null)));
            text.append(src); return this;
        }
     
        public StyledString appendSource(String src) {
            attrParts.add (new AttributesPart(text.length(), src.length(), GlossaryTextArea.SOURCE_ATTRIBUTES));
            text.append(bracketEntry(src)); return this;
        }

        public StyledString appendTarget(String src) {
            attrParts.add (new AttributesPart(text.length(), src.length(), GlossaryTextArea.TARGET_ATTRIBUTES));
            text.append(bracketEntry(src)); return this;
        }

        public StyledString appendTarget(String src, boolean priority) {
            attrParts.add (new AttributesPart(text.length(), src.length(), GlossaryTextArea.TARGET_ATTRIBUTES));
            if (priority) markBoldStart(); text.append(bracketEntry(src)); if (priority) markBoldEnd(); return this;
        }
		
        public StyledString appendComment(String src) {
			if (src.startsWith("\u0007")) return appendComment(src.substring(1), true);
            attrParts.add (new AttributesPart(text.length(), src.length(), GlossaryTextArea.NOTES_ATTRIBUTES));
            text.append(src); return this;
        }

        public StyledString appendComment(String src, boolean priority) {
            attrParts.add (new AttributesPart(text.length(), src.length(), GlossaryTextArea.NOTES_ATTRIBUTES));
            if (priority) markBoldStart(); text.append(src); if (priority) markBoldEnd(); return this;
        }

        public String toHTML() {
            StringBuilder sb = new StringBuilder(text);
            AttributesPart[] attrPartsTmp = attrParts.toArray(new AttributesPart[0]);
            for (ListIterator<AttributesPart> li = attrBolds.listIterator(attrBolds.size()); li.hasPrevious(); ) li.previous().insertHtml(sb, attrPartsTmp);
            for (int i = attrPartsTmp.length - 1; i >= 0; i--) attrPartsTmp[i].insertHtml(sb, null);
            sb.insert(0, "<html><p>").append("</p></html>");
            return sb.toString().replaceAll("\n", "<br>").replaceAll("\t", "&#009;");			
        }
    }
    
    private String mSource;
}
