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

 Copyright (C) 2012-2019 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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Color;

import javax.swing.Box;
import javax.swing.JRadioButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;

import org.omegat.util.OStrings;
import org.omegat.util.StringUtil;
import org.omegat.util.Preferences;
import org.omegat.tokenizer.ITokenizer;
import org.omegat.core.search.TextExpression;
import org.omegat.core.Core;

import org.openide.awt.Mnemonics;

/**
 * Contains two collections of search modes, used by both search and replace windows.
 * 
 * @author Thomas Cordonnier
 */
public class SearchModeBox extends Box {

    public SearchModeBox (int axis, ITokenizer.StemmingMode mode, final SearchWindow win) {
        super (axis);
        this.mode = mode;
        
        // ---------------- Expression mode ---------------
        m_exactSearchRB = new JRadioButton();
        m_keywordSearchRB = new JRadioButton();
        m_regexpSearchRB = new JRadioButton();

        Mnemonics.setLocalizedText(m_exactSearchRB, OStrings.getString("SW_EXACT_SEARCH"));
        Mnemonics.setLocalizedText(m_keywordSearchRB, OStrings.getString("SW_WORD_SEARCH"));
        Mnemonics.setLocalizedText(m_regexpSearchRB, OStrings.getString("SW_REGEXP_SEARCH"));

        m_caseCB = new JCheckBox();
        Mnemonics.setLocalizedText(m_caseCB, OStrings.getString("SW_CASE_SENSITIVE"));
        
        ButtonGroup bgExpr = new ButtonGroup();
        bgExpr.add(m_exactSearchRB);
        bgExpr.add(m_keywordSearchRB);
        bgExpr.add(m_regexpSearchRB);

        Box bexpr_RB = Box.createHorizontalBox();
        bexpr_RB.add(m_exactSearchRB);
        bexpr_RB.add(Box.createHorizontalStrut(H_MARGIN));
        if (! (win instanceof ReplaceDialog)) // No keyword replace
            bexpr_RB.add(m_keywordSearchRB);
        else
            m_keywordSearchRB.setEnabled(false);
        bexpr_RB.add(Box.createHorizontalStrut(H_MARGIN));
        if (mode != ITokenizer.StemmingMode.GLOSSARY)
            bexpr_RB.add(m_regexpSearchRB);
        bexpr_RB.add(Box.createHorizontalGlue());
        bexpr_RB.setBorder (BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), OStrings.getString("SW_ZONE_EXPRESSION_MODE")));
        bexpr_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bexpr_RB.add(m_caseCB);
        this.add (bexpr_RB);		
        
        // ---------------- Words mode ---------------
        m_expressionWordsRB  = new JRadioButton();
        m_expressionStringsRB = new JRadioButton();
        m_expressionTokensRB = new JRadioButton();

        Mnemonics.setLocalizedText(m_expressionWordsRB, OStrings.getString("SW_WORD_MODE_WHOLE"));
        Mnemonics.setLocalizedText(m_expressionStringsRB, OStrings.getString("SW_WORD_MODE_STRINGS"));
        Mnemonics.setLocalizedText(m_expressionTokensRB, OStrings.getString("SW_WORD_MODE_LEMMAS"));
        
        ButtonGroup bgWords = new ButtonGroup();
        bgWords.add(m_expressionWordsRB);
        bgWords.add(m_expressionStringsRB);
        bgWords.add(m_expressionTokensRB);

        bWord_RB = Box.createHorizontalBox();
        bWord_RB.add(m_expressionStringsRB);
        bWord_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bWord_RB.add(m_expressionWordsRB);
        bWord_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bWord_RB.add(m_expressionTokensRB);
        bWord_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bWord_RB.setBorder (BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), OStrings.getString("SW_ZONE_WORD_MODE")));
        this.add (bWord_RB); bWord_RB.setVisible(true);
        
        // ---------------- Regex mode ---------------
        ButtonGroup bgRegex = new ButtonGroup();
        bgRegex.add(m_regexPartial = new JRadioButton (OStrings.getString("SW_ZONE_REGEX_MODE_PARTIAL")));
        bgRegex.add(m_regexWhole = new JRadioButton (OStrings.getString("SW_ZONE_REGEX_MODE_WHOLE")));
        bgRegex.add(m_regexFull = new JRadioButton (OStrings.getString("SW_ZONE_REGEX_MODE_FULL")));

        bRegex_RB = Box.createHorizontalBox();
        bRegex_RB.add(m_regexPartial);
        bRegex_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bRegex_RB.add(m_regexWhole);
        bRegex_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bRegex_RB.add(m_regexFull);
        bRegex_RB.add(Box.createHorizontalStrut(H_MARGIN));
        bRegex_RB.setBorder (BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black), OStrings.getString("SW_ZONE_REGEX_MODE")));
        this.add (bRegex_RB); bRegex_RB.setVisible(false);
                
        final ActionListener  regexListener = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean regexSelected = m_regexpSearchRB.isSelected();
                bWord_RB.setVisible (! regexSelected);
                bRegex_RB.setVisible (regexSelected);
                if (win != null) win.refreshLists(regexSelected);
                //if (regexSelected) m_caseCB.setEnabled (true); else m_caseCB.setEnabled (! m_expressionTokensRB.isSelected());
            }
        };
        m_exactSearchRB.addActionListener (regexListener);
        m_keywordSearchRB.addActionListener (regexListener);
        m_regexpSearchRB.addActionListener (regexListener);
        
        /*final ActionListener wordsListener = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                //m_regexpSearchRB.setEnabled (m_expressionStringsRB.isSelected());
                m_caseCB.setEnabled (! m_expressionTokensRB.isSelected());
            }
        };
        m_expressionWordsRB.addActionListener (wordsListener);
        m_expressionStringsRB.addActionListener (wordsListener);
        m_expressionTokensRB.addActionListener (wordsListener);*/
    }
    
    public void loadPreferences(String prefix) {	
        // Word search type
        String searchWord = Preferences.getPreferenceDefault(prefix + Preferences.SEARCHWINDOW_SEARCH_WORD, 
            Preferences.getPreferenceDefault (Preferences.SEARCHWINDOW_SEARCH_WORD,SEARCH_WORD_STRINGS));
        m_expressionWordsRB.setSelected(searchWord.equals(SEARCH_WORD_WHOLE));
        m_expressionStringsRB.setSelected(searchWord.equals(SEARCH_WORD_STRINGS));
        m_expressionTokensRB.setSelected(searchWord.equals(SEARCH_WORD_TOKENS));
        
        // Regex search type
        String searchRegex = Preferences.getPreferenceDefault(prefix + Preferences.SEARCHWINDOW_SEARCH_REGEX, 
            Preferences.getPreferenceDefault (Preferences.SEARCHWINDOW_SEARCH_REGEX,
                prefix.startsWith("R") || prefix.startsWith("P") ? SEARCH_REGEX_FULL : SEARCH_REGEX_PARTIAL));
        m_regexPartial.setSelected(searchRegex.equals(SEARCH_REGEX_PARTIAL));
        m_regexFull.setSelected(searchRegex.equals(SEARCH_REGEX_FULL));
        m_regexWhole.setSelected(searchRegex.equals(SEARCH_REGEX_WHOLE));		
        
        // Expression search type
        String searchType = Preferences.getPreferenceDefault(prefix + Preferences.SEARCHWINDOW_SEARCH_TYPE,
            Preferences.getPreferenceDefault(Preferences.SEARCHWINDOW_SEARCH_TYPE, SEARCH_TYPE_EXACT));
        m_exactSearchRB.setSelected(searchType.equals(SEARCH_TYPE_EXACT));
        m_keywordSearchRB.setSelected(searchType.equals(SEARCH_TYPE_KEYWORD));
        m_regexpSearchRB.setSelected(searchType.equals(SEARCH_TYPE_REGEXP));	

        // case sensitivity
        String caseSens = Preferences.getPreferenceDefault(prefix + Preferences.SEARCHWINDOW_CASE_SENSITIVE,
            Preferences.getPreferenceDefault(Preferences.SEARCHWINDOW_CASE_SENSITIVE, "false"));
        m_caseCB.setSelected(Boolean.valueOf(caseSens).booleanValue());		
        
        if (m_regexpSearchRB.isSelected()) {
            bWord_RB.setVisible (false); bRegex_RB.setVisible (true);
        } else {
            bWord_RB.setVisible (true); bRegex_RB.setVisible (false);
            //m_caseCB.setEnabled (! m_expressionTokensRB.isSelected());
        }
    }
    
    public void savePreferences(String prefix) {
        Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_CASE_SENSITIVE, Boolean.toString(m_caseCB.isSelected()));
                
        // Word search type
        if (m_expressionWordsRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_WORD, SEARCH_WORD_WHOLE);
        else if (m_expressionStringsRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_WORD, SEARCH_WORD_STRINGS);
        else if (m_expressionTokensRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_WORD, SEARCH_WORD_TOKENS);
        
        // Regex search mode
        if (m_regexPartial.isSelected()) 
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_REGEX, SEARCH_REGEX_PARTIAL);
        else if (m_regexFull.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_REGEX, SEARCH_REGEX_FULL);
        else if (m_regexWhole.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_REGEX, SEARCH_REGEX_WHOLE);
        
        // Expression search type
        if (m_exactSearchRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_TYPE, SEARCH_TYPE_EXACT);
        else if (m_keywordSearchRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_TYPE, SEARCH_TYPE_KEYWORD);
        else if (m_regexpSearchRB.isSelected())
            Preferences.setPreference(prefix + Preferences.SEARCHWINDOW_SEARCH_TYPE, SEARCH_TYPE_REGEXP);
    }
    
    public String searchTypeString() {
        if (m_exactSearchRB.isSelected()) return SEARCH_TYPE_EXACT;
        if (m_keywordSearchRB.isSelected()) return SEARCH_TYPE_KEYWORD;
        if (m_regexpSearchRB.isSelected()) return SEARCH_TYPE_REGEXP;
        return "";
    }
    
    public String wordModeString() {
        if (m_expressionStringsRB.isSelected()) return "S" + (m_caseCB.isSelected() ? "C" : "c");
        if (m_expressionWordsRB.isSelected()) return "W" + (m_caseCB.isSelected() ? "C" : "c");
        if (m_expressionTokensRB.isSelected()) return "T" + (m_caseCB.isSelected() ? "C" : "c");
        return "";		
    }
    
	/** Contains an expression with word mode and case **/
	public static class MemorizedExpression {
		private String mode;
		private String expression;
		
		private MemorizedExpression(String txt) {
			if (txt.startsWith("{")) { expression = txt.substring(txt.indexOf('}') + 1); mode = txt.substring(1, txt.indexOf('}')); }
			else { expression = txt; mode = ""; }
		}
		
		public static Object forString (String txt) {
			if (txt.startsWith("{")) return new MemorizedExpression(txt); else return txt;
		}
		
		public void applyTo (SearchModeBox box) {
			if (mode.contains("C")) box.m_caseCB.setSelected(true);
			else if (mode.contains("c")) box.m_caseCB.setSelected(false);
			// else: keep as is
			
			if (mode.toUpperCase().contains("S")) box.m_expressionStringsRB.setSelected(true);
			if (mode.toUpperCase().contains("W")) box.m_expressionWordsRB.setSelected(true);
			if (mode.toUpperCase().contains("T")) box.m_expressionTokensRB.setSelected(true);
			if (mode.toUpperCase().contains("L")) box.m_expressionTokensRB.setSelected(true); // lemmas <=> tokens
		}

		public String toString() { return expression; }
	}
	
    public TextExpression buildExpression (String text, boolean isNot, boolean isSource) {
        if (isNot)
            return new TextExpression.NotExpression(this.buildExpression(text, false, isSource));
         if (m_regexpSearchRB.isSelected()) {
            if (m_regexFull.isSelected())
                return new TextExpression.RegexTextExpression ("^" + text + "$", m_caseCB.isSelected());
            else if (m_regexWhole.isSelected())
                return new TextExpression.RegexTextExpression ("\\b" + text + "\\b", m_caseCB.isSelected());
            else
                return new TextExpression.RegexTextExpression (text, m_caseCB.isSelected());
        } else if (m_expressionTokensRB.isSelected()) {
            ITokenizer tokenizer = 
                isSource ? Core.getProject().getSourceTokenizer() : Core.getProject().getTargetTokenizer();
            if (m_exactSearchRB.isSelected())
                return new TextExpression.ExactTokenExpression (tokenizer, mode, text, m_caseCB.isSelected());
            if (m_keywordSearchRB.isSelected())
                return new TextExpression.WordsTokenExpression (tokenizer, mode, text, m_caseCB.isSelected());		
        } else {
            if (m_exactSearchRB.isSelected())
                if (m_expressionWordsRB.isSelected())
                    return TextExpression.RegexTextExpression.exactWholeWordsExpression (text, m_caseCB.isSelected());
                else
                    return TextExpression.RegexTextExpression.exactStringExpression (text, m_caseCB.isSelected());
            if (m_keywordSearchRB.isSelected())
                return new TextExpression.WordsTextExpression (text, m_caseCB.isSelected(), m_expressionWordsRB.isSelected());
        }
        return null;
    }
    
    public TextExpression buildReplaceExpression (String text, boolean isSource, String replaceText, boolean useVars) {
         if (m_expressionTokensRB.isSelected() && (! m_regexpSearchRB.isSelected())) {
            ITokenizer tokenizer = 
                isSource ? Core.getProject().getSourceTokenizer() : Core.getProject().getTargetTokenizer();
            if (m_exactSearchRB.isSelected())
                if (useVars)
                    return new TextExpression.ExactReplaceTokenExpression (tokenizer, mode, text, replaceText, m_caseCB.isSelected());
                else
                    return new TextExpression.ExactTokenExpression (tokenizer, mode, text, m_caseCB.isSelected());	
            if (m_keywordSearchRB.isSelected())
                return new TextExpression.WordsTokenExpression (tokenizer, mode, text, m_caseCB.isSelected());		
        } else {
            if (m_exactSearchRB.isSelected())
                if (m_expressionWordsRB.isSelected())
                    if (useVars)
                        return TextExpression.RegexReplaceExpression.exactWholeWordsExpression (text, m_caseCB.isSelected(), replaceText);
                    else
                        return TextExpression.RegexTextExpression.exactWholeWordsExpression (text, m_caseCB.isSelected());					
                else
                    if (useVars)
                        return TextExpression.RegexReplaceExpression.exactStringExpression (text, m_caseCB.isSelected(), replaceText);
                    else
                        return TextExpression.RegexTextExpression.exactStringExpression (text, m_caseCB.isSelected());
            if (m_keywordSearchRB.isSelected())
                return new TextExpression.WordsTextExpression (text, m_caseCB.isSelected(), m_expressionWordsRB.isSelected());
            if (m_regexpSearchRB.isSelected()) {
				if (m_regexFull.isSelected()) text = "^" + text + "$";
				if (m_regexWhole.isSelected()) text = "\\b" + text + "\\b";
                if (useVars)
                    return new TextExpression.RegexReplaceExpression (text, m_caseCB.isSelected(), replaceText);
                else
                    return new TextExpression.RegexTextExpression (text, m_caseCB.isSelected());					
			}
        }
        return null;
    }

    public javax.swing.AbstractButton[] getOptionsComponents() {
        return new javax.swing.AbstractButton[] {
            m_exactSearchRB, m_keywordSearchRB, m_regexpSearchRB,
            m_expressionWordsRB, m_expressionStringsRB, m_expressionTokensRB,
            m_regexPartial, m_regexFull,
            m_caseCB
        };
    }
    
    public boolean englobeWords() {
        return ! (m_expressionWordsRB.isSelected());
    }
	
    public void addModeChangeListener (ActionListener l) {
        m_exactSearchRB.addActionListener (l);
        m_keywordSearchRB.addActionListener (l);
        m_regexpSearchRB.addActionListener (l);
    }
    
    private JCheckBox m_caseCB;
    private JRadioButton m_exactSearchRB, m_keywordSearchRB, m_regexpSearchRB;
    private JRadioButton m_expressionWordsRB, m_expressionStringsRB, m_expressionTokensRB;
    private JRadioButton m_regexPartial, m_regexWhole, m_regexFull;
    private final Box bWord_RB, bRegex_RB;
    
    private ITokenizer.StemmingMode mode;
    
    private final static String SEARCH_WORD_WHOLE = "WHOLE";
    private final static String SEARCH_WORD_STRINGS = "STRINGS";
    private final static String SEARCH_WORD_TOKENS = "TOKENS";
    
    private final static String SEARCH_TYPE_EXACT = "EXACT";
    private final static String SEARCH_TYPE_KEYWORD = "KEYWORD";
    private final static String SEARCH_TYPE_REGEXP = "REGEXP";

    private final static String SEARCH_REGEX_PARTIAL = "PARTIAL";
    private final static String SEARCH_REGEX_WHOLE = "WHOLE";
    private final static String SEARCH_REGEX_FULL = "FULL";
    
    public final static int H_MARGIN = 10;
    
}
