/**************************************************************************
 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
               2016 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 javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyAdapter;
import javax.swing.SpinnerDateModel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.JComboBox;

import java.text.SimpleDateFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.Set;

import org.openide.awt.Mnemonics;

import org.omegat.gui.main.MainWindow;
import org.omegat.gui.editor.IEditorFilter;
import org.omegat.gui.editor.EditorController;
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.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
 * SearchThread.
 * 
 * @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 abstract class ProjectSearchWindow extends SearchWindow {
    public ProjectSearchWindow(MainWindow par, String startText) {
        super(par, startText);	
        
        addWindowListener(new java.awt.event.WindowAdapter() {
            private final int initialEntry = Core.getEditor().getCurrentEntryNumber();
            private final EditorController.CaretPosition initialCaret = Core.getEditor().getCurrentPositionInEntryTranslationInEditor();
        
            @Override
            public void windowClosed(java.awt.event.WindowEvent e) {
                // save user preferences
                savePreferences();

                if (m_thread != null) m_thread.fin();

                // back to the initial segment
                int currentEntry = Core.getEditor().getCurrentEntryNumber();
                if (initialEntry > 0 && returnInitial.isSelected() && initialEntry != currentEntry) {
                    IEditorFilter filter = Core.getEditor().getFilter();
                    if ((filter == null) || (filter.allowed(Core.getProject().getAllEntries().get(initialEntry - 1))))
                        // Restore caretPosition too
                        ((EditorController) Core.getEditor()).gotoEntry(initialEntry, initialCaret);
                    else
                        // The segment is not displayed (maybe filter on). Ignore caretPosition.
                        Core.getEditor().gotoEntry(initialEntry);
                }
            }
        });		
    }
    
    @Override
    protected Box buttonsPanel() {
        Box bCB = Box.createHorizontalBox();		// we do not use super because checkboxes must be before close btn
        
        this.auto = new JCheckBox(); bCB.add (auto);
        org.openide.awt.Mnemonics.setLocalizedText(auto, OStrings.getString("SW_AUTO_SYNC")); // NOI18N
        auto.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_AUTO_SYNC, false));
        auto.addActionListener(e -> {
                // update auto-sync flag in EntryListPane
                m_viewer.setAutoSyncWithEditor(auto.isSelected());
            });
        
        this.returnInitial = new JCheckBox(); bCB.add (returnInitial);
        org.openide.awt.Mnemonics.setLocalizedText(returnInitial, OStrings.getString("SW_BACK_TO_INITIAL_SEGMENT")); // NOI18N
        returnInitial.setSelected(Preferences.isPreferenceDefault(Preferences.SEARCHWINDOW_BACK_TO_INITIAL_SEGMENT, false));	

        // box CheckBox
        JButton m_dismissButton = new JButton();
        Mnemonics.setLocalizedText(m_dismissButton, OStrings.getString("BUTTON_CLOSE"));
        bCB.add(m_dismissButton);

        // ///////////////////////////////////
        // action listeners
        m_dismissButton.addActionListener(getRootPane().getActionMap().get("ESCAPE"));
        
        return bCB;
    }
    
    protected String[] getFormatVariablesList() {
        return new String[] {
            SearchVarExpansion.VAR_PREAMBLE, SearchVarExpansion.VAR_CREATION_ID, SearchVarExpansion.VAR_CREATION_DATE,
            SearchVarExpansion.VAR_SOURCE_TEXT, SearchVarExpansion.VAR_TARGET_TEXT, SearchVarExpansion.VAR_NOTE, "@{revisor}",
            SearchVarExpansion.VAR_FILE_NAME, SearchVarExpansion.VAR_FILE_NAME_ONLY, SearchVarExpansion.VAR_FILE_PATH, SearchVarExpansion.VAR_FILE_SHORT_PATH
        };
    }
    
    protected String getFormatOptionName() {
        return Preferences.SEARCHWINDOW_TEMPLATE_PROJECT;
    }
    protected String getFormatOptionDefaultValue() {
        return SearchVarExpansion.DEFAULT_TEMPLATE_PROJECT;
    }
    
    public static final Set<String> L_AUTHORS = new java.util.TreeSet<String>();
    public static final Set<String> L_TRANSLATORS = new java.util.TreeSet<String>();
    
    @Override
    protected JComponent advancedPanel() {
        Box advBox = (Box) super.advancedPanel();

        m_dateFormat = new SimpleDateFormat(SAVED_DATE_FORMAT);
        
        // box AuthorBox
        m_authorField = new MemoryTextField ("SW_AUTHOR", true, L_AUTHORS);
        m_translatorField = new MemoryTextField ("SW_TRANSLATOR", true, L_TRANSLATORS);
        
        Box bDB = Box.createHorizontalBox();
        m_dateFrom = new DateField("SW_CHANGED_AFTER");
        bDB.add(m_dateFrom);
        bDB.add(Box.createHorizontalStrut(H_MARGIN));
        bDB.add(Box.createHorizontalGlue());

        m_dateTo = new DateField("SW_CHANGED_BEFORE");
        bDB.add(m_dateTo);
        
        // author search
        //gridbag.setConstraints(bAB, c);
        Box bAuthors = Box.createHorizontalBox(); advBox.add(bAuthors);
        bAuthors.add(m_authorField); if (this instanceof FullProjectSearchWindow) bAuthors.add(m_translatorField);

        // date search
        //gridbag.setConstraints(bDB, c);
        advBox.add(bDB);
        
        return advBox;
    }
    
    abstract class EnableField<Display extends JComponent, Value> extends Box {
    
        protected JCheckBox check, not = null;
        protected JComboBox fieldList = null;
        protected Display component;
        
        public EnableField(String checkLabel, Display theComponent, boolean hasNot) {
            super (javax.swing.BoxLayout.X_AXIS);
            this.check = new JCheckBox();
            if (checkLabel.contains("|")) {
                fieldList = new JComboBox();
                fieldList.setRenderer(new org.omegat.util.gui.DelegatingComboBoxRenderer<String>() {
                    @Override
                    protected Object getDisplayText(String key) {
                        return OStrings.getString(key).trim();
                    }
                });
                for (String label: checkLabel.split("\\|")) fieldList.addItem(label); 
                fieldList.setMaximumSize(new java.awt.Dimension(100, fieldList.getPreferredSize().height));
            } 
            else Mnemonics.setLocalizedText(check, OStrings.getString(checkLabel));
            this.component = theComponent;
            
            add (check);
            if (fieldList != null) add(fieldList);
            if (hasNot) add(this.not = new JCheckBox(OStrings.getString("SW_SEARCH_NOT")));
            add (component);
            
            check.addActionListener (e -> {					
                    boolean editable = check.isSelected();
                    enableComponent(editable);

                    if (editable) {
                        // move focus to content field
                        component.requestFocus();
                    } else {
                        // move focus to search edit field
                        getMainSearchTextField().requestFocus();
                    }
                });
        }
        
        public boolean isNot() {
            if (not == null) return false;
            return not.isSelected();
        }
        
        public  boolean isUsed () {
            return isOpen();
        }
        
        public final boolean isOpen () {
            return check.isSelected();
        }
        
        public void setOpen (boolean value) {
            check.setSelected (value);
            enableComponent (value);
        }
        
        public Value getNullableValue() {
            if (isUsed()) return getValue(); else return null;
        }
        
        protected void enableComponent (boolean value) {
            if (not != null) not.setEnabled(value);		
        }
        
        public final String getSelectedField() {
            if (fieldList == null) return null; else return fieldList.getSelectedItem().toString();
        }
        
        public abstract Value getValue ();
        
        public abstract void setValue (Value v);
    }
    
    /* class EnableTextField<Display extends JTextField> extends EnableField<Display, String> {

        public EnableTextField(String checkLabel, Display theComponent) {
            super (checkLabel, theComponent, true);
        }
    
        protected void enableComponent (boolean value) {
            this.component.setEditable (value);
        }
        
        public boolean isUsed () {
            return super.isUsed() && getValue().trim().length() > 0;
        }
        
        public String getValue () {
            return this.component.getText();
        }

        public void setValue (String text) {
            this.component.setText (text);
        }
        
        JTextField getTextField() {
            return this.component;
        }
        
    } */

    class MemoryTextField extends EnableField<JComboBox, String> {

        private SetRef valuesSet;
    
        public MemoryTextField(final String checkLabel, final boolean canAll, final Set<String> values_set) {
            super (checkLabel, new JComboBox(), canAll); this.valuesSet = new SetRef(values_set);
            for (String item: valuesSet.theSet) this.component.addItem(SearchModeBox.MemorizedExpression.forString(item));
            this.component.setEditable(true);
            this.component.getEditor().getEditorComponent().addKeyListener(new KeyAdapter() {
                public void keyReleased (KeyEvent e) {
                    if (e.getKeyChar() == KeyEvent.VK_ENTER)
                        if (m_viewer != null) doSearch(false);
                }
            });
			this.component.addActionListener (ev -> {
				try {
					SearchModeBox.MemorizedExpression item = (SearchModeBox.MemorizedExpression) component.getSelectedItem();
					item.applyTo(ProjectSearchWindow.this.m_modePanel);
				} catch (Exception cce) {	// ClassCast | NullPointer
					
				}
			});			
            this.add(ProjectSearchWindow.this.createMemorizeButton(
                ProjectSearchWindow.this.getY(), canAll, MemoryTextField.this.component, null, 
                checkLabel, MemoryTextField.this.valuesSet
            ));
        }
    
        protected void enableComponent (boolean value) {
            this.component.setEnabled (value);
            super.enableComponent(value);
        }
        
        public boolean isUsed () {
            return super.isUsed() && getValue().trim().length() > 0;
        }
        
        public String getValue () {
            Object item = this.component.getSelectedItem();
            if (item == null) return "";
            return item.toString();
        }

        public void setValue (String text) {
            this.component.setSelectedItem (text);
        }
        
        JComboBox getTextField() {
            return this.component;
        }
        
        public void changeValuesSet (Set<String> newValuesSet, boolean removeContents) {
            this.valuesSet.theSet = newValuesSet;
            synchronized (this.component) {
                Object selectedItem = removeContents ? "" : this.component.getSelectedItem();
                this.component.removeAllItems();
                this.component.addItem(selectedItem);
                for (String item: newValuesSet) this.component.addItem(SearchModeBox.MemorizedExpression.forString(item));
                this.component.setSelectedItem(selectedItem);
            }
        }
        
    }
        
    class DateField extends EnableField<Box, Date> {
    
        private JButton m_nowButton;
        private JSpinner m_spinner;
        private SpinnerDateModel m_dateModel;
        
        public DateField(String checkLabel) {
            super (checkLabel, Box.createHorizontalBox(), false);
            
            // box DateBox
            Calendar calendar = Calendar.getInstance();
            Date initDate = calendar.getTime();
            calendar.add(Calendar.YEAR, -100);
            Date earliestDate = calendar.getTime();
            Date latestDate = initDate;
            
            m_dateModel = new SpinnerDateModel(initDate, earliestDate, latestDate, Calendar.YEAR);
            component.add(m_spinner = new JSpinner(m_dateModel));
            component.add(m_nowButton = new JButton());		

            Mnemonics.setLocalizedText(m_nowButton, OStrings.getString("SW_NOW"));
            m_nowButton.addActionListener(e -> {
                    Date now = Calendar.getInstance().getTime();
                    m_dateModel.setEnd(now);
                    m_dateModel.setValue(now);
                });
        }

        protected void enableComponent (boolean value) {
            super.enableComponent(value);
            this.m_spinner.setEnabled (value);
            this.m_nowButton.setEnabled (value);
        }
        
        public Date getValue () {
            return this.m_dateModel.getDate();
        }

        public void setValue (Date d) {
            this.m_dateModel.setValue (d);
        }
        
    }
    
    @Override
    protected void loadAdvancedOptionPreferences() {
        super.loadAdvancedOptionPreferences();
        
        // author options
        String searchAuthor = Preferences.getPreference(Preferences.SEARCHWINDOW_SEARCH_AUTHOR);
        if (StringUtil.isEmpty(searchAuthor))
            searchAuthor = "false";
        m_authorField.setOpen(Boolean.valueOf(searchAuthor).booleanValue());
        String authorName = Preferences.getPreference(Preferences.SEARCHWINDOW_AUTHOR_NAME);
        if (!StringUtil.isEmpty(authorName))
            m_authorField.setValue(authorName);
        searchAuthor = Preferences.getPreference(Preferences.SEARCHWINDOW_SEARCH_TRANSLATOR);
        if (StringUtil.isEmpty(searchAuthor))
            searchAuthor = "false";
        m_translatorField.setOpen(Boolean.valueOf(searchAuthor).booleanValue());
        authorName = Preferences.getPreference(Preferences.SEARCHWINDOW_TRANSLATOR_NAME);
        if (!StringUtil.isEmpty(authorName))
            m_translatorField.setValue(authorName);
        

        // date options
        try {
            // from date
            String dateFrom = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_FROM);
            if (StringUtil.isEmpty(dateFrom))
                dateFrom = "false";
            m_dateFrom.setOpen(Boolean.valueOf(dateFrom).booleanValue());
            String dateFromValue = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_FROM_VALUE);
            if (!StringUtil.isEmpty(dateFromValue))
                m_dateFrom.setValue(m_dateFormat.parse(dateFromValue));

            // to date
            String dateTo = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_TO);
            if (StringUtil.isEmpty(dateTo))
                dateTo = "false";
            m_dateTo.setOpen(Boolean.valueOf(dateTo).booleanValue());
            String dateToValue = Preferences.getPreference(Preferences.SEARCHWINDOW_DATE_TO_VALUE);
            if (!StringUtil.isEmpty(dateToValue))
                m_dateTo.setValue(m_dateFormat.parse(dateToValue));
        } catch (ParseException e) {
            // use safe settings in case of parsing error
            m_dateFrom.setOpen(false);
            m_dateTo.setOpen(false);
        }

        // if advanced options are enabled (e.g. author/date search),
        // let the user see them anyway. This is important because
        // search results will be affected by these settings
        if (m_authorField.isOpen() || m_translatorField.isOpen() || m_dateFrom.isOpen() || m_dateTo.isOpen()) {
            m_advancedVisible = true;
        }
    }

    /**
     * Saves the size and position of the search window and the button selection
     * state
     */
    protected void savePreferences() {
        super.savePreferences();
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_AUTHOR, Boolean.toString(m_authorField.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_AUTHOR_NAME, m_authorField.getValue());
        Preferences.setPreference(Preferences.SEARCHWINDOW_SEARCH_TRANSLATOR, Boolean.toString(m_translatorField.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_TRANSLATOR_NAME, m_translatorField.getValue());
        Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_FROM, Boolean.toString(m_dateFrom.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_FROM_VALUE, m_dateFormat.format(m_dateFrom.m_dateModel.getDate()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_TO, Boolean.toString(m_dateTo.isOpen()));
        Preferences.setPreference(Preferences.SEARCHWINDOW_DATE_TO_VALUE, m_dateFormat.format(m_dateTo.m_dateModel.getDate()));
        
        // need to explicitly save preferences because project might not be open
        Preferences.save();
    }
    
    protected org.omegat.core.data.ProjectOptions getOptions() { return Core.getProject().getProjectProperties(); }
    
    private JCheckBox auto, returnInitial;
    protected MemoryTextField m_authorField, m_translatorField;

    protected DateField m_dateFrom;
    protected DateField m_dateTo;
    
    private SimpleDateFormat m_dateFormat;

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