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

 Copyright (C) 2010-2013 Alex Buloichik
               2013-2014 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.core.search;

/**
 * Class for store info about matching position.
 * 
 * @author Alex Buloichik (alex73mail@gmail.com)
 */
public class SearchMatch implements Comparable<SearchMatch> {
    protected int start, end;

    public SearchMatch(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public int compareTo(SearchMatch o) {
        int diff = start - o.start;
        if (diff == 0) {
            diff = end - o.end;
        }
        return diff;
    }

    /**
     * Merge current object with an other one
     * 
     * @param other
     *           Other search match to merge with
     * @param mustKeep
     *           If true, always returns a new object; else, try to modify current object and return this
     * @param defaultReplacement
     *           If merged with a replace match, the text to be put in the place of current one.
     * 
     * @return merged object
     */
    public SearchMatch merge(SearchMatch other, boolean mustKeep, String defaultReplacement) {
        try {
            // If merge with a replace match, the object is necessarily new
            return new ReplaceMatch (Math.min (this.start, other.start), Math.max(this.end, other.end), defaultReplacement + ((ReplaceMatch) other).getReplacement());
        } catch (ClassCastException cce) {
            if (defaultReplacement != null) return new ReplaceMatch (Math.min (this.start, other.start), Math.max(this.end, other.end), defaultReplacement + defaultReplacement);
            else if (mustKeep) return new SearchMatch (Math.min (this.start, other.start), Math.max(this.end, other.end));
            else {  
                this.start = Math.min (this.start, other.start); this.end = Math.max(this.end, other.end); 
                return this;
            }
        }
    }

    /** merge overlapped matches for better performance to mark on UI **/
    public static void collapse (java.util.List<SearchMatch> foundMatches, String defaultReplacement) {
        for (int i = 1; i < foundMatches.size();) {
            SearchMatch pr = foundMatches.get(i - 1), cu = foundMatches.get(i);
            // check for overlapped
            if (pr.getStart() <= cu.getStart() && pr.getEnd() >= cu.getStart()) {
                foundMatches.set(i-1, pr.merge(cu, false, defaultReplacement));
                foundMatches.remove(i);
            } else {
                i++;
            }
        }		
    }

    public int getStart() {
        return start;
    }

    public int getEnd() {
        return end;
    }

    public int getLength() {
        return end - start;
    }

    public int addLength(int count) {
        return end += count;
    }

    public void move(int offset) {
        start += offset;
        end += offset;
    }

    /** Returns a match where the words are kept entirely **/
    public SearchMatch englobeWord(String source) {
        int newStart = this.start, newEnd = this.end;
        if (Character.isLetter(source.charAt(start))) while ((newStart > 0) && (Character.isLetter(source.charAt(newStart - 1)))) newStart--; 
        if ((end > 0) && Character.isLetter(source.charAt(end - 1))) while ((newEnd < source.length()) && (Character.isLetter(source.charAt(newEnd)))) newEnd++;
        return new SearchMatch (newStart, newEnd);
    }
}
