/**************************************************************************
 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
               2015-2023 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.ArrayList;
import java.util.List;

import javax.swing.event.DocumentEvent;
import javax.swing.text.Highlighter.HighlightPainter;

import org.omegat.core.Core;
import org.omegat.core.data.TMXEntry;
import org.omegat.core.data.SourceTextEntry;
import org.omegat.core.glossaries.GlossaryEntryStore;
import org.omegat.gui.editor.UnderlineFactory;
import org.omegat.gui.editor.mark.IFastMarker;
import org.omegat.gui.editor.mark.Mark;
import org.omegat.gui.editor.MarkerController;
import org.omegat.util.Preferences;
import org.omegat.util.gui.Styles;

/**
 * Marker for TransTips.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Thomas Cordonnier
 */
public class TransTipsMarker implements IFastMarker {
    protected static final HighlightPainter transTipsUnderliner = new UnderlineFactory.SolidBoldUnderliner(Styles.EditorColor.COLOR_TRANSTIPS.getColor());

    @Override
    public List<Mark> getMarksForEntry(SourceTextEntry ste, String sourceText, String translationText, boolean isActive) {
        if (!isActive || sourceText == null) {
            return null;
        }
        if (! (Preferences.isPreference(Preferences.TRANSTIPS_SRC) || Preferences.isPreference(Preferences.TRANSTIPS_TRA))) {
            return null;
        }
        List<GlossaryEntryView> glossaryEntries = Core.getGlossary().getDisplayedEntries();
        if (glossaryEntries == null || glossaryEntries.isEmpty()) {
            return null;
        }

        final List<Mark> marks = new ArrayList<Mark>();
        // Get the index of the current segment in the whole document
        //sourceText = sourceText.toLowerCase();

        class MarkerCallback implements TransTips.Search {
            private Mark.ENTRY_PART mark;
            
            public MarkerCallback (boolean isSource) { 
                if (isSource) mark = Mark.ENTRY_PART.SOURCE; else mark = Mark.ENTRY_PART.TRANSLATION; 
            }
        
            public void found(GlossaryEntryStore ge, int start, int end) {
                Mark m = new Mark(mark, start, end);
                m.painter = TransTipsMarker.transTipsUnderliner;
                m.toolTipText = ge.toStyledString(true).toHTML();
                marks.add(m);
            }
        }
        MarkerCallback srcCallback = new MarkerCallback(true), traCallback = new MarkerCallback(false);

        for (GlossaryEntryView ent : glossaryEntries) {
            if (Preferences.isPreference(Preferences.TRANSTIPS_SRC))
                TransTips.search(sourceText, ent.getMergedEntries().iterator().next(), srcCallback, true);
            if (Preferences.isPreference(Preferences.TRANSTIPS_TRA))
                for (GlossaryEntryStore enst : ent.getMergedEntries())
                    TransTips.search(translationText, enst, traCallback, false);
        }
        return marks;
    }
    
    /** Remove all markers which are, even partially, in the doc event bounds **/
    public List<MarkerController.MarkInfo> toRemove (DocumentEvent ev, List<MarkerController.MarkInfo> oriList) {
        if (! Preferences.isPreference(Preferences.TRANSTIPS_TRA)) return java.util.Collections.EMPTY_LIST; // only translation tips may need refresh
        
        List<MarkerController.MarkInfo> res = new ArrayList<MarkerController.MarkInfo> (1 + ev.getLength() / 5);
        for (MarkerController.MarkInfo ori: oriList)
            if (ori.highlight != null)
                if (ori.highlight.getEndOffset() < ev.getOffset()) continue;
                else if ((ev.getType() == DocumentEvent.EventType.INSERT) && (ori.highlight.getStartOffset() > ev.getOffset() + ev.getLength())) continue;
                else if ((ev.getType() == DocumentEvent.EventType.REMOVE) && (ori.highlight.getStartOffset() > ev.getOffset())) continue;
                else res.add (ori);
        return res;
    }
    
    /** Calls spellchecker only for words which are, even partially, in the doc event bounds **/
    public List<Mark> toAdd (final DocumentEvent ev) throws Exception {
        if (! Preferences.isPreference(Preferences.TRANSTIPS_TRA)) return java.util.Collections.EMPTY_LIST; // only translation tips may need refresh
    
        List<GlossaryEntryView> glossaryEntries = Core.getGlossary().getDisplayedEntries();
        if (glossaryEntries == null || glossaryEntries.isEmpty()) return java.util.Collections.EMPTY_LIST;
        
        final org.omegat.gui.editor.Document3 doc = (org.omegat.gui.editor.Document3) ev.getDocument();
        String translationText = doc.getText(doc.getTranslationStart(), doc.getTranslationEnd() - doc.getTranslationStart());
        final List<Mark> marks = new ArrayList<Mark>();
        TransTips.Search traCallback = new TransTips.Search() {	
            public void found(GlossaryEntryStore ge, int start, int end) {
                if (end < ev.getOffset() - doc.getTranslationStart()) return;
                if (start > ev.getOffset() + ev.getLength() - doc.getTranslationStart()) return;
                
                Mark m = new Mark(Mark.ENTRY_PART.TRANSLATION, start, end);
                m.painter = TransTipsMarker.transTipsUnderliner;
                marks.add(m);
            }
        };
        
        for (GlossaryEntryView ent : glossaryEntries)
            for (GlossaryEntryStore enst : ent.getMergedEntries())
                TransTips.search(translationText, enst, traCallback, false);
        
        return marks;        
    }
    
    public List<MarkerController.MarkInfo> toRemove (String newTra, List<MarkerController.MarkInfo> oriList) {
        if (! Preferences.isPreference(Preferences.TRANSTIPS_TRA)) return java.util.Collections.EMPTY_LIST; // only translation tips may need refresh
        if (! Preferences.isPreference(Preferences.TRANSTIPS_SRC)) return oriList; // we rebuild completely
        
        // find which entries are in translation
        List<MarkerController.MarkInfo> res = new ArrayList<MarkerController.MarkInfo> (oriList.size() / 2);
        for (MarkerController.MarkInfo ori: oriList)
            if (ori.highlight != null)
                if ( ori.highlight.getStartOffset() == ori.highlight.getEndOffset() ) 
                    res.add (ori);
        return res;
    }
    
    /** Calls spellchecker only for words which are, even partially, in the doc event bounds **/
    public List<Mark> toAdd (final String newTra) throws Exception {
        if (! Preferences.isPreference(Preferences.TRANSTIPS_TRA)) return java.util.Collections.EMPTY_LIST; // only translation tips may need refresh
    
        List<GlossaryEntryView> glossaryEntries = Core.getGlossary().getDisplayedEntries();
        if (glossaryEntries == null || glossaryEntries.isEmpty()) return java.util.Collections.EMPTY_LIST;
        
        final List<Mark> marks = new ArrayList<Mark>();
        TransTips.Search traCallback = new TransTips.Search() {	
            public void found(GlossaryEntryStore ge, int start, int end) {
                Mark m = new Mark(Mark.ENTRY_PART.TRANSLATION, start, end);
                m.painter = TransTipsMarker.transTipsUnderliner;
                marks.add(m);
            }
        };
        
        for (GlossaryEntryView ent : glossaryEntries)
            for (GlossaryEntryStore enst : ent.getMergedEntries())
                TransTips.search(newTra, enst, traCallback, false);
        
        return marks;        
    }		
    
}
