/**************************************************************************
 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
               2006 Henry Pijffers
               2009 Didier Briel
               2010 Martin Fleurke, Antonio Vilei, Alex Buloichik, Didier Briel
               2012 Thomas Cordonnier
               2013 Aaron Madlon-Kay, Alex Buloichik
               2014 Alex Buloichik, Piotr Kulik, Aaron Madlon-Kay
               2015 Aaron Madlon-Kay
               2017 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;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import org.omegat.core.Core;
import org.omegat.core.data.IProject;
import org.omegat.core.threads.LongProcessThread;
import org.omegat.core.threads.LongProcessInterruptedException;
import org.omegat.filters2.TranslationException;
import org.omegat.gui.search.SearchWindow;
import org.omegat.util.Log;
import org.omegat.util.OStrings;
import org.omegat.util.StaticUtils;

/**
 * This thread implements search functionality, i.e. the algorithm used by search screens. 
 * Abstract class, implemented by ProjectSearcher and DirectorySearcher.
 * It is non-reentrant: each search action must be used by a single thread, even if several searches are started from the same window.
 * 
 * @author Keith Godfrey
 * @author Maxym Mykhalchuk
 * @author Henry Pijffers
 * @author Didier Briel
 * @author Martin Fleurke
 * @author Antonio Vilei
 * @author Alex Buloichik (alex73mail@gmail.com)
 * @author Aaron Madlon-Kay
 * @author Piotr Kulik
 * @author Thomas Cordonnier
 */
public abstract class Searcher extends LongProcessThread {

    /**
     * Create new searcher instance (abstract)
     */
    public Searcher(final SearchWindow window, boolean removeDup, int maxResults) {
        m_window = window;

        // Can do init here, remember that this object is non-reentrant!
        m_maxResults = maxResults;
        if (removeDup)
            m_searchResults = new TreeSet<SearchResultEntry>();
        else
            m_searchResults = new ArrayList<SearchResultEntry>();
        m_numFinds = 0;
    }

    // /////////////////////////////////////////////////////////
    // thread main loop
    @Override
    public void run() {
        try {
			startTime = System.currentTimeMillis();
            doSearch();

            checkInterrupted();

            m_window.displaySearchResult(m_searchResults);
        } catch(LongProcessInterruptedException ex) {
            // interrupted - nothing to do
        } catch (PatternSyntaxException e) {
            // bad regexp input
            // alert user to badness
            m_window.displayErrorRB(e, "ST_REGEXP_ERROR");
        } catch (IOException e) {
            // something bad happened
            // alert user to badness
            Log.logErrorRB(e, "ST_FILE_SEARCH_ERROR");
            Core.getMainWindow().displayErrorRB(e, "ST_FILE_SEARCH_ERROR");
        } catch (TranslationException te) {
            // something bad happened
            // alert user to badness
            Log.logErrorRB(te, "ST_FILE_SEARCH_ERROR");
            Core.getMainWindow().displayErrorRB(te, "ST_FILE_SEARCH_ERROR");
        } catch (Exception re) {
            Log.logErrorRB(re, "ST_FATAL_ERROR");
            Core.getMainWindow().displayErrorRB(re, "ST_FATAL_ERROR");
        }
    }

    /**
     * Returns list of search results
     */
    public Collection<SearchResultEntry> getSearchResults() {
        return m_searchResults;
    }

    /** Search-type specific part of the search **/
    protected abstract void doSearch() throws Exception;
    
    // ////////////////////////////////////////////////////////////
    // internal functions

    protected void addEntry(SearchResultEntry entry) {
        if (m_searchResults instanceof TreeSet) 
            for (SearchResultEntry se: m_searchResults)
                if (se.equals(entry)) {		// Equivalent in the sence of equals method
                    se.addMore();
                    return;
                }
        if (m_numFinds >= m_maxResults) return;	
        if (isInterrupted()) return; // avoid concurrent exception
        m_searchResults.add(entry);
        m_numFinds++;
    }

    protected volatile Collection<SearchResultEntry> m_searchResults;

    protected int m_numFinds;
    protected int m_maxResults;

    protected final SearchWindow m_window;
	
	public long startTime = System.currentTimeMillis();

}
