/*
 * HistoryText.java - Common code for text components with a history
 * :tabSize=4:indentSize=4:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 2004 Slava Pestov
 *
 * 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 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

package org.gjt.sp.jedit.gui;

//{{{ Imports
import javax.swing.*;
import javax.swing.text.*;
import java.awt.event.*;
import org.gjt.sp.jedit.*;
import org.gjt.sp.util.GenericGUIUtilities;
//}}}

/**
 * Controller (manager of models) for HistoryTextArea.
 * @author Slava Pestov
 * @version $Id: HistoryText.java 25461 2021-03-29 21:09:10Z kpouer $
 */
public class HistoryText
{
	//{{{ HistoryText constructor
	public HistoryText(JTextComponent text, String name)
	{
		this.text = text;
		setModel(name);
		index = -1;
	} //}}}

	//{{{ fireActionPerformed() method
	public void fireActionPerformed()
	{
	} //}}}

	//{{{ getIndex() mehtod
	public int getIndex()
	{
		return index;
	} //}}}

	//{{{ setIndex() mehtod
	public void setIndex(int index)
	{
		this.index = index;
	} //}}}

	//{{{ getModel() method
	/**
	 * Returns the underlying history controller.
	 * @since jEdit 4.3pre1
	 */
	public HistoryModel getModel()
	{
		return historyModel;
	} //}}}

	//{{{ setModel() method
	/**
	 * Sets the history list controller.
	 * @param name The model name
	 * @since jEdit 4.3pre1
	 */
	public void setModel(String name)
	{
		if(name == null)
			historyModel = null;
		else
			historyModel = HistoryModel.getModel(name);
		index = -1;
	} //}}}

	//{{{ setInstantPopups() method
	/**
	 * Sets if selecting a value from the popup should immediately fire
	 * an ActionEvent.
	 */
	public void setInstantPopups(boolean instantPopups)
	{
		this.instantPopups = instantPopups;
	} //}}}

	//{{{ getInstantPopups() method
	/**
	 * Returns if selecting a value from the popup should immediately fire
	 * an ActionEvent.
	 */
	public boolean getInstantPopups()
	{
		return instantPopups;
	} //}}}

	//{{{ addCurrentToHistory() method
	/**
	 * Adds the currently entered item to the history.
	 */
	public void addCurrentToHistory()
	{
		if(historyModel != null)
			historyModel.addItem(getText());
		index = 0;
	} //}}}

	//{{{ doBackwardSearch() method
	public void doBackwardSearch()
	{
		if(historyModel == null)
			return;

		if(text.getSelectionEnd() != getDocument().getLength())
		{
			text.setCaretPosition(getDocument().getLength());
		}

		int start = getInputStart();
		String t = getText().substring(0,
			text.getSelectionStart() - start);

		for(int i = index + 1; i < historyModel.getSize(); i++)
		{
			String item = historyModel.getItem(i);
			if(item.startsWith(t))
			{
				text.replaceSelection(item.substring(t.length()));
				text.select(getInputStart() + t.length(),
					getDocument().getLength());
				index = i;
				return;
			}
		}

		javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null); 
	} //}}}

	//{{{ doForwardSearch() method
	public void doForwardSearch()
	{
		if(historyModel == null)
			return;

		if(text.getSelectionEnd() != getDocument().getLength())
		{
			text.setCaretPosition(getDocument().getLength());
		}

		int start = getInputStart();
		String t = getText().substring(0,
			text.getSelectionStart() - start);

		for(int i = index - 1; i >= 0; i--)
		{
			String item = historyModel.getItem(i);
			if(item.startsWith(t))
			{
				text.replaceSelection(item.substring(t.length()));
				text.select(getInputStart() + t.length(),
					getDocument().getLength());
				index = i;
				return;
			}
		}

		javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null); 
	} //}}}

	//{{{ historyPrevious() method
	public void historyPrevious()
	{
		if(historyModel == null)
			return;

		if(index == historyModel.getSize() - 1)
			javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null); 
		else if(index == -1)
		{
			current = getText();
			setText(historyModel.getItem(0));
			index = 0;
		}
		else
		{
			// have to do this because setText() sets index to -1
			int newIndex = index + 1;
			setText(historyModel.getItem(newIndex));
			index = newIndex;
		}
	} //}}}

	//{{{ historyNext() method
	public void historyNext()
	{
		if(historyModel == null)
			return;

		if(index == -1)
			javax.swing.UIManager.getLookAndFeel().provideErrorFeedback(null); 
		else if(index == 0)
			setText(current);
		else
		{
			// have to do this because setText() sets index to -1
			int newIndex = index - 1;
			setText(historyModel.getItem(newIndex));
			index = newIndex;
		}
	} //}}}
	
	//{{{ getDocument() method
	public Document getDocument()
	{
		return text.getDocument();
	} //}}}
	
	//{{{ getText() method
	/**
	 * Subclasses can override this to provide funky history behavior,
	 * for JTextPanes and such.
	 */
	public String getText()
	{
		return text.getText();
	} //}}}
	
	//{{{ setText() method
	/**
	 * Subclasses can override this to provide funky history behavior,
	 * for JTextPanes and such.
	 */
	public void setText(String text)
	{
		this.index = -1;
		this.text.setText(text);
	} //}}}

	//{{{ getInputStart() method
	/**
	 * Subclasses can override this to provide funky history behavior,
	 * for JTextPanes and such.
	 */
	public int getInputStart()
	{
		return 0;
	} //}}}

	//{{{ showPopupMenu() method
	public void showPopupMenu(String t, int x, int y)
	{
		if(historyModel == null)
			return;

		text.requestFocus();

		if(popup != null && popup.isVisible())
		{
			popup.setVisible(false);
			popup = null;
			return;
		}

		popup = new JPopupMenu()
		{
			@Override
			public void setVisible(boolean b)
			{
				if (!b)
				{
					popup = null;
				}
				super.setVisible(b);
			}
		};
		JMenuItem caption = new JMenuItem(jEdit.getProperty(
			"history.caption"));
		caption.addActionListener(e -> new ListModelEditor().open(historyModel));
 		popup.add(caption);
 		popup.addSeparator();

		for(int i = 0; i < historyModel.getSize(); i++)
		{
			String item = historyModel.getItem(i);
			if(item.startsWith(t))
			{
				JMenuItem menuItem = new JMenuItem(item);
				menuItem.setActionCommand(String.valueOf(i));
				menuItem.addActionListener(
					new ActionHandler());
				popup.add(menuItem);
			}
		}

		GenericGUIUtilities.showPopupMenu(popup,text,x,y,false);
	} //}}}

	//{{{ showPopupMenu() method
	public void showPopupMenu(boolean search)
	{
		if(search)
			showPopupMenu(getText().substring(getInputStart(),
				text.getSelectionStart()),0,text.getHeight());
		else
			showPopupMenu("",0,text.getHeight());
	} //}}}

	//{{{ Private members
	private final JTextComponent text;
	private HistoryModel historyModel;
	private int index;
	private String current;
	private JPopupMenu popup;
	private boolean instantPopups;
	//}}}

	//{{{ ActionHandler class
	class ActionHandler implements ActionListener
	{
		@Override
		public void actionPerformed(ActionEvent evt)
		{
			int ind = Integer.parseInt(evt.getActionCommand());
			if(ind == -1)
			{
				if(index != -1)
					setText(current);
			}
			else
			{
				setText(historyModel.getItem(ind));
				index = ind;
			}
			if(instantPopups)
			{
				addCurrentToHistory();
				fireActionPerformed();
			}
		}
	} //}}}
}
