import java.io.RandomAccessFile;
import java.io.File;
import java.io.EOFException;
import java.io.IOException;
import java.util.Random;

/**
	Conversion from fortune.cpp.  No longer supports fortune cookie file header.  The format of cookies
	that this class supports is a series of quotes seperated by a % (on a line by itself)

	There should be no % on the first line of the file, the first cookie should be here.  Neither should
	there be a % at the end, the last cookie should be here.  Will no longer work without the index.

	Fortune Cookie
	Copyright (C)2000 Jason Pell.

	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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
		
	Email: 	jasonpell@hotmail.com
	Url:	http://www.geocities.com/SiliconValley/Haven/9778
*/

public class Fortune
{
	public static void main(String args[])
	{
		try
		{
			if (args.length>=1)
			{
				Fortune fortuneObj = new Fortune(args[0]);
				if(args.length>=2 && args[1].equalsIgnoreCase("-i"))
					fortuneObj.generateIndex();
				else if(args.length>=2)
					System.out.println(fortuneObj.getEntry(Integer.parseInt(args[1])));
				else
					System.out.println(fortuneObj.getEntry());
			}
			else
			{
				System.out.println("Syntax: Fortune <fortune.txt file> [EntryNo | -i]");
				System.out.println("Use the option -i to generate a new index for the quote file.");
				System.out.println("  Using -i will generate an index for the Fortune.txt file as normal.");
				System.out.println("  The index will be written to: Fortune.idx.\n");
				System.out.println("  But if the file already exists the program will abort with an error.");
			}
		}
		catch(IOException ioe)
		{
			// display any error message.
			System.out.println(ioe.getMessage());
		}
	}
	
	// Until I found out how to dynamically work this out, we shall define it here.
	private static final long INT_BYTE_LENGTH = 4;
	private static final String LINE_ENDING = System.getProperties().getProperty("line.separator");

	byte[] byteFind = (LINE_ENDING + "%" + LINE_ENDING).getBytes();
	private String strFortuneFile = null;
	private RandomAccessFile fileFortune = null;
	private String strIndexFile = null;

	/**
	 * @param strFortune The name of the <fortune>.txt file
	 */
	private Fortune(String strFortune) throws IOException
	{
		this.strFortuneFile = strFortune;
		this.strIndexFile = getFileMinusExtension(strFortuneFile) + ".idx";
		this.fileFortune = new RandomAccessFile(strFortuneFile, "r");
	}

	private String getEntry()
	{
		return getEntry(-1);	
	}

	/**
	*/
	private String getEntry(int entry)
	{
		RandomAccessFile idxFile = null;
		Random random = null;
		try
		{
			try
			{
				idxFile = new RandomAccessFile(strIndexFile, "r");
			}
			catch (Exception e)
			{
				throw new Exception("Could not open index file for reading: " + e.getMessage());
			}

			// Read first long as total.
		    int total = readInt(idxFile);
			
			if (entry==-1)
			{
				random = new Random();
				entry = (random.nextInt()*-1);
				if (entry<0)
					entry = entry*-1;
				entry = (entry%total);
			}
			else if (entry>total)
				throw new Exception ("Fortune Cookie Number specified is too large.");

			//System.out.println("total: " + total);
			//System.out.println("Entry: " + entry);
			idxFile.seek(idxFile.getFilePointer() + (entry*INT_BYTE_LENGTH));
			int startEntry = readInt(idxFile);
			int endEntry=-1;
			if (entry<total)
				endEntry = readInt(idxFile);
			idxFile.close();

			return getEntry(startEntry, endEntry);
		}
		catch (Exception e)
		{
			return "No Entry: " + e.getMessage();
		}
	}
	
	private String getEntry(int start, int end) throws IOException
	{
		String strReturn = "";

		fileFortune.seek(start);
		if (end==-1)
			end=(int)fileFortune.length();
		else
			end = end-byteFind.length;

		for (int i=0; i<(end-start); i++)
		{
			strReturn = strReturn.concat((char)fileFortune.read() + "");
		}
		return strReturn;
	}

	private int readInt(RandomAccessFile file)
	{
		try
		{
			return file.readInt();
		}
		catch (Exception e)
		{
			return -1;
		}
	}

	private boolean generateIndex()
	{
		try
		{
			RandomAccessFile idxFile = null;
			
			try
			{
				idxFile = new RandomAccessFile(strIndexFile, "rw");
			}
			catch (Exception e)
			{
				throw new Exception("Could not open index file for writing: " + e.getMessage());
			}

			byte read;
			int count=0;
			int counter=0;
			try
			{   
				// Write first long as a dummy so we can come back later and overwrite it with the total quotes found.
				idxFile.writeInt(666);
				// Now write record for first fortune cookie.  This fortune cookie should begin at the beginning of the fortune cookie file.
				idxFile.writeInt(0);

				while ((read = fileFortune.readByte())!=-1)
				{
					// Find a first character.
					if (count<byteFind.length && read == byteFind[count])
					{
						count++;
						if (count==byteFind.length)
						{
							counter++;
							if ((counter%100)==0)
								System.err.print(".");

							idxFile.writeInt((int) fileFortune.getFilePointer());
							count=0;
						}
					}
				}
			}
			catch (EOFException e) {}

			// Seek to the beginning of the file and overwrite the dummy long with the count of entries processed.
			idxFile.seek(0);
			System.err.println("\nProcessed " + counter + " fortune Cookies.");
			idxFile.writeInt(counter);
			idxFile.close();

			return true;
		}
		catch (Exception e)
		{   
			System.err.println("Error: " + e.getMessage());
			return false;
		}
	}

	private String getFileMinusExtension(String strFile)
	{
		if (strFile==null || strFile.indexOf(".")==-1)
			return strFile;

		return strFile.substring(0, strFile.indexOf("."));
	}
}

