/**************************************************************************
 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, Didier Briel
               2012 Didier Briel, Thomas Cordonnier
               2015 Thomas Cordonnier
               2017 Thomas Cordonnier
               Home page: http://www.omegat.org/
               Support center: http://groups.yahoo.com/group/OmegaT/

 This program 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 2 of the License, or
 (at your option) any later version.

 This program 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, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 **************************************************************************/

package org.omegat.gui.search;

import java.awt.Color;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JRadioButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JTextField;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.text.JTextComponent;

import java.text.SimpleDateFormat;
import java.util.Set;
import java.util.TreeSet;

import org.openide.awt.Mnemonics;

import org.omegat.gui.main.MainWindow;
import org.omegat.gui.editor.filter.SearchFilter;
import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;
import org.omegat.util.Preferences;
import org.omegat.core.search.ProjectSearcher;
import org.omegat.core.search.FullProjectSearcher;
import org.omegat.core.search.TextExpression;
import org.omegat.core.search.TranslationStateFilter;
import org.omegat.core.Core;

/**
 * This is a window that appears when user'd like to search for something. For
 * each new user's request new window is created. Actual search is done by
 * FullProjectSearcher.
 * 
 * @author Keith Godfrey
 * @author Henry Pijffers (henry.pijffers@saxnot.com)
 * @author Didier Briel
 * @author Martin Fleurke
 * @author Antonio Vilei
 * @author Thomas Cordonnier
 */
@SuppressWarnings("serial")
public class FullProjectSearchWindow extends ProjectSearchWindow {
    private static final java.util.Map<String,Set<String>> theMap = new java.util.HashMap<String,Set<String>>();
    static {		
        for (String field: new String[] { "SW_SEARCH_SOURCE", "SW_SEARCH_TRANSLATION", "SW_SEARCH_NOTES" })
            for (String mode: new String[] { "EXACT", "KEYWORD", "REGEXP" })
                theMap.put (field + ":" + mode, new TreeSet<String>());
        TreeSet<String> fileSet = new TreeSet<String>();
        theMap.put("SW_SEARCH_FILE:EXACT", fileSet); theMap.put("SW_SEARCH_FILE:KEYWORD", fileSet); theMap.put("SW_SEARCH_FILE:REGEXP", fileSet);
        org.omegat.core.CoreEvents.registerProjectChangeListener(new SearchesLoader<Set<String>>("FullProjectSearchWindow", theMap));
    }

    public void refreshLists(boolean removeContents) {
        m_searchSource.changeValuesSet(theMap.get("SW_SEARCH_SOURCE:" + m_modePanel.searchTypeString()), removeContents);
        m_searchTarget.changeValuesSet(theMap.get("SW_SEARCH_TRANSLATION:" + m_modePanel.searchTypeString()), removeContents);
        m_searchNotes.changeValuesSet(theMap.get("SW_SEARCH_NOTES:" + m_modePanel.searchTypeString()), removeContents);
        m_FileNameField.changeValuesSet(theMap.get("SW_SEARCH_FILE:" + m_modePanel.searchTypeString()), removeContents);
    }

    public FullProjectSearchWindow(MainWindow par, String startText) {
        super(par, startText);
        refreshLists(true); // after all components, in particular the m_modePanel, have been built
		for (MemoryTextField box: new MemoryTextField[]{ m_searchSource, m_searchTarget, m_searchNotes }) {
            JTextComponent searchFieldArea = (JTextComponent) box.getTextField().getEditor().getEditorComponent();
            searchFieldArea.addMouseListener(new PopupFactory(searchFieldArea));
        }		
		
        if (startText != null) getMainSearchTextField().setSelectedItem(startText);
        m_modePanel.addModeChangeListener(new RegexModeSwitchListener(m_modePanel, ((JTextComponent) m_searchSource.getTextField().getEditor().getEditorComponent())));
        m_modePanel.addModeChangeListener(new RegexModeSwitchListener(m_modePanel, ((JTextComponent) m_searchTarget.getTextField().getEditor().getEditorComponent())));
        m_modePanel.addModeChangeListener(new RegexModeSwitchListener(m_modePanel, ((JTextComponent) m_searchNotes.getTextField().getEditor().getEditorComponent())));
        getMainSearchTextField().requestFocus();
    }
    
    // Build help text
    protected String getScopeText() { return OStrings.getString("SW_HELP_SCOPE_PROJECT"); }
    protected boolean isReplace() { return false; }    
    
    
    @Override
    protected JComponent textPanel(String startText) {
        Box bSearch = Box.createVerticalBox();
        bSearch.setBorder (BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), OStrings.getString("SW_ZONE_TEXT")));

        m_searchNotes = new MemoryTextField ("SW_SEARCH_NOTES", true, theMap.get("SW_SEARCH_NOTES:EXACT"));
        m_searchTarget = new MemoryTextField ("SW_SEARCH_TRANSLATION", true, theMap.get("SW_SEARCH_TRANSLATION:EXACT"));
        m_searchSource = new MemoryTextField ("SW_SEARCH_SOURCE", true, theMap.get("SW_SEARCH_SOURCE:EXACT"));
        m_searchAllSame = new JCheckBox();
        Mnemonics.setLocalizedText(m_searchAllSame, OStrings.getString("SW_ZONE_TEXT_ALL"));
        m_AND = new JRadioButton ("AND"); m_OR = new JRadioButton ("OR"); m_ANDTHEN = new JRadioButton ("AND THEN"); 

        ButtonGroup gOptions = new ButtonGroup();
        gOptions.add (m_AND); gOptions.add(m_OR); gOptions.add(m_ANDTHEN);
        
        bSearch.add(m_searchSource);
        bSearch.add(Box.createHorizontalStrut(H_MARGIN));
        bSearch.add(m_searchTarget);
        bSearch.add(Box.createHorizontalStrut(H_MARGIN));
        bSearch.add(m_searchNotes);
        bSearch.add(Box.createHorizontalStrut(H_MARGIN));
        
        Box bOptions = Box.createHorizontalBox();
        bSearch.add (bOptions);		
        bOptions.add(m_searchAllSame);
        bOptions.add(m_AND); bOptions.add(m_OR); bOptions.add(m_ANDTHEN); 
        m_AND.setSelected(true);
        
        m_searchAllSame.addActionListener (new ActionListener() {
            private final KeyListener dispatch = new KeyListener() {
                public void keyPressed(KeyEvent e) { }
                public void keyTyped(KeyEvent e) { }
                public void keyReleased(KeyEvent e) { 
                    m_searchTarget.setValue(m_searchSource.getValue()); 
                    m_searchNotes.setValue(m_searchSource.getValue()); 
                }
            };
            
            public void actionPerformed (ActionEvent e) {
                if (m_searchAllSame.isSelected()) {
                    m_searchTarget.setValue (m_searchSource.getValue());
                    m_searchNotes.setValue (m_searchSource.getValue());
                    
                    m_searchTarget.getTextField().setEnabled(false);
                    m_searchNotes.getTextField().setEnabled (false);
                    
                    m_searchSource.getTextField().addKeyListener (dispatch);
					m_ANDTHEN.setEnabled(false); m_AND.setSelected(true);
                } else {
                    m_searchTarget.getTextField().setEnabled (m_searchTarget.isOpen());
                    m_searchNotes.getTextField().setEnabled (m_searchNotes.isOpen());
                    m_searchSource.getTextField().removeKeyListener (dispatch);
					
					m_ANDTHEN.setEnabled(true);
                }
            }
        });
        
        return bSearch;
    }
    
    public javax.swing.JComboBox getMainSearchTextField() {
        for (MemoryTextField field: new MemoryTextField[] { m_searchSource, m_searchTarget, m_searchNotes, m_authorField })
            if (field.isOpen()) return field.getTextField();
        return m_searchSource.getTextField();
    }		
    
    @Override
    protected Box optionsPanel() {
        // <Box 3 - Search options>
        m_searchTranslated = new JRadioButton(); 
        m_searchUntranslated = new JRadioButton(); 
        m_searchTranslatedUntranslated = new JRadioButton();
        Mnemonics.setLocalizedText(m_searchTranslated, OStrings.getString("SW_SEARCH_TRANSLATED"));
        Mnemonics.setLocalizedText(m_searchUntranslated, OStrings.getString("SW_SEARCH_UNTRANSLATED"));
        Mnemonics.setLocalizedText(m_searchTranslatedUntranslated, OStrings.getString("SW_SEARCH_TRANSLATEDUNTRANSLATED"));

        // box OptionsBox bOB
        Box bOB = super.optionsPanel();
        bOB.add(m_searchTranslated); bOB.add(m_searchUntranslated); bOB.add(m_searchTranslatedUntranslated); 

        addFocusToSearchListener(m_searchTranslated); addFocusToSearchListener(m_searchUntranslated); addFocusToSearchListener(m_searchTranslatedUntranslated); 

        ButtonGroup gOptions = new ButtonGroup();
        gOptions.add (m_searchTranslated); gOptions.add (m_searchUntranslated); gOptions.add (m_searchTranslatedUntranslated);
        
        return bOB;
    }	
    
    public static final Set<String> L_FILES = new java.util.TreeSet<String>();
    
    @Override
    public Box wherePanel() {
        Box bWhere = Box.createHorizontalBox();
        
        m_sourceSearchCB = new JCheckBox();
        m_tmSearchCB = new JCheckBox();
        m_ongoingSearchCB = new JCheckBox();
        m_glossaryCB = new JCheckBox();

        Mnemonics.setLocalizedText(m_sourceSearchCB, OStrings.getString("SW_SEARCH_IN_SOURCE"));
        Mnemonics.setLocalizedText(m_tmSearchCB, OStrings.getString("SW_SEARCH_IN_TMS"));
        Mnemonics.setLocalizedText(m_ongoingSearchCB, OStrings.getString("SW_SEARCH_IN_MEMORY"));
        Mnemonics.setLocalizedText(m_glossaryCB, OStrings.getString("SW_SEARCH_IN_GLOSSARIES"));
        
        bWhere.add(m_sourceSearchCB); bWhere.add(m_ongoingSearchCB); bWhere.add(m_tmSearchCB); bWhere.add(m_glossaryCB); 
        bWhere.setBorder (BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), OStrings.getString("SW_SEARCH_SCOPE")));

        bWhere.add(Box.createHorizontalStrut(H_MARGIN));
        bWhere.add (m_FileNameField = new MemoryTextField("SW_SEARCH_FILE", false, L_FILES));
        
        addFocusToSearchListener(m_sourceSearchCB);
        addFocusToSearchListener(m_tmSearchCB);
        addFocusToSearchListener(m_ongoingSearchCB);
        addFocusToSearchListener(m_glossaryCB);
        
        return bWhere;	
    }
        
    protected Box buttonsPanel() {
        Box bCB = super.buttonsPanel();

        m_removeFilterButton = new JButton();
        m_removeFilterButton.setEnabled(false);
        m_filterButton = new JButton();
        m_filterButton.setEnabled(false);

        Mnemonics.setLocalizedText(m_filterButton, OStrings.getString("BUTTON_FILTER"));
        Mnemonics.setLocalizedText(m_removeFilterButton, OStrings.getString("BUTTON_REMOVEFILTER"));
        
        bCB.add(m_removeFilterButton);
        bCB.add(m_filterButton);
            
        m_filterButton.addActionListener(e -> {
                Core.getEditor().setFilter(new SearchFilter(m_viewer.getEntryList()));
                m_removeFilterButton.setEnabled(true);
            });
        m_removeFilterButton.addActionListener(e -> {
                m_removeFilterButton.setEnabled(false);
                Core.getEditor().removeFilter();
            });		

        return bCB;
    }
    
    /**
     * Loads the position and size of the search window and the button selection
     * state.
     */
    @Override
    protected void loadPreferences() {
        super.loadPreferences();

        // search boxes
        m_searchSource.setOpen(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_SOURCE, true));
        m_searchTarget.setOpen(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_TRANSLATION, false));
        m_searchNotes.setOpen(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_NOTES, false));

        m_FileNameField.setOpen(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_FOLDERNAME, false));

        // Search location		
        m_sourceSearchCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_SOURCE_FILES, false));
        m_ongoingSearchCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_ONGOING, true));
        m_tmSearchCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_TM_SEARCH, true));
        m_glossaryCB.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_GLOSSARY_SEARCH, true));
        
        String searchState = Preferences.getPreference(Preferences.SEARCHWINDOW_SEARCH_STATE);
        if (StringUtil.isEmpty(searchState)) searchState = "TRANSLATED_AND_UNTRANSLATED";
        m_searchTranslated.setSelected(TranslationStateFilter.TRANSLATED_ONLY.toString().equals(searchState)); 
        m_searchUntranslated.setSelected(TranslationStateFilter.UNTRANSLATED_ONLY.toString().equals(searchState));  
        m_searchTranslatedUntranslated.setSelected(TranslationStateFilter.TRANSLATED_AND_UNTRANSLATED.toString().equals(searchState));
    }
    
    /**
     * Saves the size and position of the search window and the button selection
     * state
     */
    protected void savePreferences() {
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_SOURCE, Boolean.toString(m_searchSource.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TRANSLATION, Boolean.toString(m_searchTarget.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_NOTES, Boolean.toString(m_searchNotes.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_SOURCE_FILES, Boolean.toString(m_sourceSearchCB.isSelected())); 		
        Preferences.setPreference(Preferences.SEARCHWINDOW_ONGOING, Boolean.toString(m_ongoingSearchCB.isSelected())); 
        Preferences.setPreference(Preferences.SEARCHWINDOW_TM_SEARCH, Boolean.toString(m_tmSearchCB.isSelected())); 
        Preferences.setPreference(Preferences.SEARCHWINDOW_GLOSSARY_SEARCH, Boolean.toString(m_glossaryCB.isSelected())); 
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_STATE,buildTranslationStateFilter().toString());
        super.savePreferences();
    }
    
    @Override
    protected ProjectSearcher buildSearcher() throws Exception {
        TextExpression exprSource = null, exprTarget = null, exprNote = null;
        if (m_searchSource.isUsed()) exprSource = m_modePanel.buildExpression (m_searchSource.getValue(), m_searchSource.isNot(), true);
        if (m_searchTarget.isUsed()) exprTarget = m_modePanel.buildExpression (m_searchTarget.getValue(), m_searchTarget.isNot(), false);
        if (m_searchNotes.isUsed()) exprNote = m_modePanel.buildExpression (m_searchNotes.getValue(), m_searchNotes.isNot(), false);
    
        if ((exprSource == null) && (exprTarget == null) && (exprNote == null)) throw new Exception(OStrings.getString("SW_ERROR_ALMOST_ONE_FIELD"));
        int whereFlags = (m_sourceSearchCB.isSelected() ? ProjectSearcher.SEARCH_SCOPE_SOURCE_FILES : 0)
                + (m_ongoingSearchCB.isSelected() ? ProjectSearcher.SEARCH_SCOPE_ONGOING : 0)
                + (m_tmSearchCB.isSelected() ? FullProjectSearcher.SEARCH_SCOPE_TM : 0)
                + (m_glossaryCB.isSelected() ? FullProjectSearcher.SEARCH_SCOPE_GLOSSARIES : 0);
        if (whereFlags == 0) throw new Exception (OStrings.getString("SW_ERROR_ALMOST_ONE_SCOPE"));
    
        return new FullProjectSearcher (this, Core.getProject(), 
            m_removeDupCB.isSelected(), ((Integer) m_numberOfResults.getValue()), 
            buildTranslationStateFilter(), whereFlags, m_FileNameField.isUsed() ? m_FileNameField.getValue() : null,
            exprSource, exprTarget, exprNote, m_AND.isSelected(), m_ANDTHEN.isSelected(), 
            m_authorField.isUsed() ? m_modePanel.buildExpression(m_authorField.getValue(), m_authorField.isNot(), false) : null, 
            m_translatorField.isUsed() ? m_modePanel.buildExpression(m_translatorField.getValue(), m_translatorField.isNot(), false) : null, 
            m_dateFrom.isUsed() ? m_dateFrom.getValue().getTime() : Long.MAX_VALUE,
            m_dateTo.isUsed() ? m_dateTo.getValue().getTime() : Long.MIN_VALUE
        );
    }
    
    private TranslationStateFilter buildTranslationStateFilter() {
        return	m_searchTranslated.isSelected() ? TranslationStateFilter.TRANSLATED_ONLY
            : m_searchUntranslated.isSelected() ? TranslationStateFilter.UNTRANSLATED_ONLY
            : TranslationStateFilter.TRANSLATED_AND_UNTRANSLATED;	
    }
    
    protected JComponent[] componentsEnabledWhenResults() {
        if (m_viewer.getEntryList().size() > 0)
            return new JComponent[] { m_filterButton };
        else
            // do not enable filtering, because it may throw an exception
            return new JComponent[0];
    }

	protected JTextComponent[] textFieldsList() {
		return new JTextComponent [0];
	}
	
    protected boolean isSearchPossible() {
        return (m_tmSearchCB.isSelected() || m_ongoingSearchCB.isSelected() || m_sourceSearchCB.isSelected() || m_glossaryCB.isSelected())
            // && (m_searchSource.isUsed() || m_searchTarget.isUsed() || m_searchNotes.isUsed()) 
            && super.isSearchPossible();
    }

    private JCheckBox m_sourceSearchCB;
    private JCheckBox m_tmSearchCB;
    private JCheckBox m_ongoingSearchCB;
    private JCheckBox m_glossaryCB;
    private MemoryTextField m_FileNameField;
    
    private JRadioButton m_searchTranslated, m_searchUntranslated, m_searchTranslatedUntranslated;
    
    private MemoryTextField m_searchSource, m_searchTarget, m_searchNotes;
    private JCheckBox m_searchAllSame;
    private JRadioButton m_AND, m_OR, m_ANDTHEN;

    private JButton m_filterButton;
    private JButton m_removeFilterButton;

    private SimpleDateFormat m_dateFormat;

    private final static String SAVED_DATE_FORMAT = "yyyy/MM/dd HH:mm";
    
}
