/*
 * HyperSearchResult.java - HyperSearch result
 * :tabSize=4:indentSize=4:noTabs=false:
 * :folding=explicit:collapseFolds=1:
 *
 * Copyright (C) 1998, 2003 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.search;

//{{{ Imports
import javax.swing.text.Position;

import org.gjt.sp.jedit.msg.BufferUpdate;
import org.gjt.sp.jedit.textarea.*;
import org.gjt.sp.jedit.*;
import org.gjt.sp.jedit.EditBus.EBHandler;
import org.gjt.sp.util.ThreadUtilities;
//}}}

/**
 * A set of occurrences of the search string on a given line in a buffer.
 * @author Slava Pestov
 */
public class HyperSearchResult implements HyperSearchNode
{
	public String path;
	public Buffer buffer;
	public int line;
	public String str; // cached for speed
	public Occur occur;
	public int occurCount;

	//{{{ getBuffer() method
	public Buffer getBuffer(View view)
	{
		if(buffer == null)
			buffer = jEdit.openFile(view,path);
		return buffer;
	} //}}}

	//{{{ getSelection() method
	/**
	 * Returns an array of selection objects pointing to the occurrences
	 * of the search term on the current line. The buffer must be opened
	 * first.
	 * @since jEdit 4.2pre5
	 */
	public Selection[] getSelection()
	{
		if(buffer == null)
			return null;

		Selection[] returnValue = new Selection[occurCount];
		Occur o = occur;
		int i = 0;
		while(o != null)
		{
			int start = o.startPos.getOffset();
			int end = o.endPos.getOffset();
			Selection.Range s = new Selection.Range(
				start,
				end
			);
			returnValue[i++] = s;
			o = o.next;
		}
		return returnValue;
	} //}}}

	//{{{ goTo() method
	public void goTo(final EditPane editPane)
	{
		new GotoDelayed(editPane);
	} //}}}

	//{{{ toString() method
	public String toString()
	{
		return str;
	} //}}}

	//{{{ Package-private members

	//{{{ HyperSearchResult constructor
	HyperSearchResult(Buffer buffer, int line)
	{
		path = buffer.getPath();

		if(!buffer.isTemporary())
			bufferOpened(buffer);

		this.line = line;

		str = (line + 1) + ": " + buffer.getLineText(line)
			.replace('\t',' ').trim();
	} //}}}

	//{{{ bufferOpened() method
	void bufferOpened(Buffer buffer)
	{
		this.buffer = buffer;
		Occur o = occur;
		while(o != null)
		{
			o.bufferOpened();
			o = o.next;
		}
	} //}}}

	//{{{ bufferClosed() method
	void bufferClosed()
	{
		buffer = null;
		Occur o = occur;
		while(o != null)
		{
			o.bufferClosed();
			o = o.next;
		}
	} //}}}

	//{{{ addOccur() method
	void addOccur(int start, int end)
	{
		Occur o = new Occur(start,end);
		o.next = occur;
		occur = o;
		occurCount++;
	} //}}}

	//{{{ pathEquals() method
	/**
	 * @param path A canonical path
	 */
	boolean pathEquals(String path)
	{
		return path.equals(MiscUtilities.resolveSymlinks(this.path));
	} //}}}
	
	//{{{ equals() method
	public boolean equals(Object compareObj)
	{
		if (!(compareObj instanceof HyperSearchResult))
			return false;
		HyperSearchResult otherResult = (HyperSearchResult)compareObj;
		return pathEquals(otherResult.path) && line == otherResult.line
			&& buffer.equals(otherResult.buffer);		
	}//}}}

	//}}}

	//{{{ Occur class
	public class Occur
	{
		public int start, end;
		public Position startPos, endPos;
		public Occur next;

		//{{{ Occur constructor
		Occur(int start, int end)
		{
			this.start = start;
			this.end = end;

			if(buffer != null && !buffer.isTemporary())
				bufferOpened();
		} //}}}

		//{{{ bufferOpened() method
		void bufferOpened()
		{
			startPos = buffer.createPosition(Math.min(
				buffer.getLength(),start));
			endPos = buffer.createPosition(Math.min(
				buffer.getLength(),end));
		} //}}}

		//{{{ bufferClosed() method
		void bufferClosed()
		{
			start = startPos.getOffset();
			end = endPos.getOffset();
			startPos = endPos = null;
		} //}}}
	} //}}}

	public class GotoDelayed implements Runnable
	{
		private final EditPane editPane;
		private volatile boolean loadedEventReceived = false;

		private GotoDelayed(EditPane editPane)
		{
			this.editPane = editPane;
			EditBus.addToBus(this);
			buffer = getBuffer(editPane.getView());
			if(buffer == null)
			{
				EditBus.removeFromBus(this);
				return;
			}
			editPane.setBuffer(buffer);
			synchronized (this)
			{
				if (!loadedEventReceived && buffer.isLoaded())
				{
					bufferLoaded();
				}
			}

		}

		public void run()
		{
			Selection[] s = getSelection();
			if(s == null)
				return;

			JEditTextArea textArea = editPane.getTextArea();
			if(textArea.isMultipleSelectionEnabled())
				textArea.addToSelection(s);
			else
				textArea.setSelection(s);

			textArea.moveCaretPosition(occur.endPos.getOffset());
		}

		private void bufferLoaded()
		{
			synchronized (this)
			{
				if (!loadedEventReceived)
				{
					EditBus.removeFromBus(this);
					loadedEventReceived = true;
					ThreadUtilities.runInDispatchThread(this);
				}
			}
		}

		@EBHandler
		public void handleBufferUpdate(BufferUpdate msg)
		{
			if (msg.getWhat() == BufferUpdate.LOADED &&
				msg.getBuffer() == buffer)
			{
				bufferLoaded();
			}
		}
	}
}
