/*	
Dynamic controls 
*/

import java.awt.*;
import java.applet.*;
import java.util.*;
import java.lang.Math;
import java.net.*;
import java.io.*;



public class WordSearch extends java.applet.Applet implements Runnable
{
	int	m_Width;
	int m_Height;

	WordPuzzle m_WordPuzzle;
	SearchList m_SearchList;

    AudioClip gong;
    AudioClip ding;
    AudioClip spaceMusic;

    public void init() 
    {
		m_Width = 15;
		m_Height = 15;

		initialize();
	}

	private void initialize()
	{
		URL WordSource = null;
		URL LetterSource = null;

	    gong = getAudioClip(getCodeBase(), "gong.au");
	    ding = getAudioClip(getCodeBase(), "ding.au");
	    spaceMusic = getAudioClip(getCodeBase(), "spacemusic.au");

		try
		{
		    String WordFile = getParameter("WORDSSOURCE");	
		    WordSource = (WordFile == null) ? getDocumentBase() : new URL(getDocumentBase(), WordFile);

		    String LetterFile = getParameter("LETTERSSOURCE");	
		    LetterSource = (LetterFile == null) ? getDocumentBase() : new URL(getDocumentBase(), LetterFile);
		}
		catch (MalformedURLException e) 
		{
		    Debug("Error");
		} 

		setFont(new Font("TimesRoman", Font.PLAIN, 12));
		setBackground(Color.white);
		setForeground(Color.black);
		setLayout(new BorderLayout());

		m_WordPuzzle = new WordPuzzle(m_Width, m_Height, LetterSource, this);
		m_WordPuzzle.layout();
		add("West",m_WordPuzzle);

		m_SearchList = new SearchList(WordSource, this);
		m_SearchList.layout();
		add("East",m_SearchList);

		layout();
	}

	public void Debug(String s)
	{
		System.out.println(s);
	}

	public Image GetPicture(String s)
	{
		return getImage(getDocumentBase(), s);	  
	}

	public int CheckWord(String s)
	{
		return m_SearchList.CheckWord(s);
	}

	public void BadWord()
	{
		if(ding != null)
			ding.play();
	}

	public void GoodWord()
	{
		if(gong != null)
			gong.play();
	}

	public void PlayWinMusic()
	{
		if(spaceMusic != null)
			spaceMusic.play();
	}

    public void start() 
    {
    }

    public void stop() 
    {
	}

    public void run() 
    {
		repaint();
		try { Thread.sleep(100); } catch (InterruptedException e) {}
	}
}


 
class WordPuzzle extends Panel
{
	char[][] 		m_Letters;
	boolean[][]		m_Used;
	boolean[][]		m_TempUsed;

	int 		m_Width, m_Height;
	int 		m_LetterWidth, m_LetterHeight;
	int 		m_PanelWidth = 250; 
	int			m_PanelHeight = 250;

	boolean		m_MouseDown = false;
	WordSearch	m_Parent;

	String		m_WordSelected = "";

	int			m_StartX = -1;
	int 		m_StartY = -1;
	int			m_EndX = -1;
	int			m_EndY = -1;

	Image		m_offimg;
	Graphics	m_offg;
	

	WordPuzzle(int Width, int Height, URL LetterSource, WordSearch Parent)
	{
		m_Width = Width;
		m_Height = Height;
		m_Parent = Parent;

		m_LetterWidth = m_PanelWidth / m_Width;
		m_LetterHeight = m_PanelHeight / m_Height;

		m_Letters = new char[m_Width][m_Height];
		m_Used = new boolean[m_Width][m_Height];
		m_TempUsed = new boolean[m_Width][m_Height];

		readLetterFile(LetterSource);
	}

    public Dimension minimumSize() 
    {
		return new Dimension(m_PanelWidth, m_PanelHeight);
    }

    public Dimension preferredSize() 
    {
		return minimumSize();
    }

	private void readLetterFile(URL LetterFile)
	{	
		URLConnection uc = null;
		InputStream is = null;

		try
		{
			uc = LetterFile.openConnection();
			is = uc.getInputStream();
			DataInputStream sourceFile = new DataInputStream(is);


			String line;
			int curRow = 0;
			int curCol = 0;
			while((line = sourceFile.readLine()) != null)
			{
				// parse line
				for(int ii=0; ii<line.length(); ii++)
				{
					String s = line.substring(ii,ii+1);
					char temp[] = s.toCharArray();
					m_Letters[curRow][curCol] = temp[0];
					m_Used[curRow][curCol] = false;
					curCol++;
					if(curCol >= m_Width)
					{
						curCol = 0;
						curRow++;
					}
				}
			}
		}
		catch(IOException e)
		{
			Debug("I/O Error");
			System.exit(1);
		}
	}

	public boolean mouseDown(Event e, int x, int y)
	{
		m_StartX = x / m_LetterWidth;
		m_StartY = y / m_LetterHeight;
		m_EndX = m_StartX;
		m_EndY = m_StartY;
		m_MouseDown = true;
		repaint();
		return true;		
	}

	public boolean mouseUp(Event e, int x, int y)
	{
		SelectLetters();
		if(m_Parent.m_SearchList.CheckWord(m_WordSelected) != 0)
		{
			// mark letters as used
			for(int ii=0; ii<m_Width; ii++)
				for(int jj=0; jj<m_Height; jj++)
					if(m_TempUsed[jj][ii])
						m_Used[jj][ii] = true;
		}

		m_MouseDown = false;
		m_StartX = -1;
		m_StartY = -1;
		m_EndX = -1;
		m_EndY = -1;
		repaint();
		return true;
	}

	public boolean mouseDrag(Event e, int x, int y)
	{
		if(m_MouseDown)
		{
			if(x < m_LetterWidth * m_Width && y < m_LetterHeight * m_Height)
			{
				m_EndX = x / m_LetterWidth;
				m_EndY = y / m_LetterHeight;
				repaint();
			}
		}
		return true;
	}

	public void update(Graphics g)
	{
		paint(g);
	}

    public void paint(Graphics g) 
    {
		if(m_offimg == null)
		{
			m_offimg = m_Parent.createImage(m_PanelWidth, m_PanelHeight);
			m_offg = m_offimg.getGraphics();
		}

		m_offg.setColor(Color.white);															
		m_offg.fillRect(0,0,m_PanelWidth,m_PanelHeight);

		m_offg.setFont(new Font("Courier", Font.BOLD, 12));

		for(int ii=0; ii<m_Width; ii++)
		{
			for(int jj=0; jj<m_Height; jj++)
			{
				//if(m_Selected)
				//	g.setColor(Color.lightGray);
				if(m_Used[jj][ii])
					m_offg.setColor(Color.cyan);
				else
					m_offg.setColor(Color.white);

				//g.drawRect(0, 0, w - 1, h - 1);
				m_offg.fill3DRect(ii*m_LetterWidth, jj*m_LetterHeight, m_LetterWidth, m_LetterHeight, true);


				m_offg.setColor(Color.black);

				char c = m_Letters[jj][ii];
				char ca[] = new char[1];
				ca[0] = c;
				m_offg.drawChars(ca, 0, 1, (ii*m_LetterWidth) + 4, (jj*m_LetterHeight) + 11);
			}
	    }

		if(m_StartX != -1)
			DrawSelectLines(m_offg);

		g.drawImage(m_offimg, 0, 0, null);
	}
		
	public void SelectLetters()
	{
		for(int ii=0; ii<m_Width; ii++)
			for(int jj=0; jj<m_Height; jj++)
				m_TempUsed[jj][ii] = false;

		m_WordSelected = "";

	 	// select proper letters
		// get coords of start letter
		int VarX = m_StartX - m_EndX;
		int VarY = m_StartY - m_EndY;

		// check for vertical
		if(m_StartX == m_EndX)
		{
			if(m_StartY <= m_EndY)
			{
				for(int ii=m_StartY; ii<=m_EndY; ii++)
				{
					m_WordSelected += m_Letters[ii][m_StartX];
					m_TempUsed[ii][m_StartX] = true;
				}					
			}
			else
			{
				for(int ii=m_StartY; ii>=m_EndY; ii--)
				{
					m_WordSelected += m_Letters[ii][m_StartX];
					m_TempUsed[ii][m_StartX] = true;
				}					
			}
		}
		// check for horizontal
		else if(m_StartY == m_EndY)
		{
			if(m_StartX <= m_EndX)
			{
				for(int ii=m_StartX; ii<=m_EndX; ii++)
				{
					m_WordSelected += m_Letters[m_StartY][ii];
					m_TempUsed[m_StartY][ii] = true;
				}					
			}
			else
			{
				for(int ii=m_StartX; ii>=m_EndX; ii--)
				{
					m_WordSelected += m_Letters[m_StartY][ii];
					m_TempUsed[m_StartY][ii] = true;
				}					
			}
		}
		// check for diagonal
		else if(abs(VarX) == abs(VarY))
		{				
			int jj;

			if(m_StartX <= m_EndX)
			{
				if(m_StartY <= m_EndY)
				{
					jj=m_StartY;
					for(int ii=m_StartX; ii<=m_EndX; ii++)
					{
						m_WordSelected += m_Letters[jj][ii];
						m_TempUsed[jj][ii] = true;
						jj++;
					}
				}
				else
				{
					jj=m_StartY;
					for(int ii=m_StartX; ii<=m_EndX; ii++)
					{
						m_WordSelected += m_Letters[jj][ii];
						m_TempUsed[jj][ii] = true;
						jj--;
					}
				}
			}
			else
			{
				if(m_StartY <= m_EndY)
				{
					jj=m_StartY;
					for(int ii=m_StartX; ii>=m_EndX; ii--)
					{
						m_WordSelected += m_Letters[jj][ii];
						m_TempUsed[jj][ii] = true;
						jj++;
					}
				}
				else
				{
					jj=m_StartY;
					for(int ii=m_StartX; ii>=m_EndX; ii--)
					{
						m_WordSelected += m_Letters[jj][ii];
						m_TempUsed[jj][ii] = true;
						jj--;
					}
				}
			}
		}
		// just highlight starting letter
		else
		{
			m_WordSelected += m_Letters[m_StartY][m_StartX];
		}
	}


	public void DrawSelectLines(Graphics g)
	{
	 	// select proper letters
		// get coords of start letter
		int VarX = m_StartX - m_EndX;
		int VarY = m_StartY - m_EndY;


		int w2 = m_LetterWidth / 2;
		int h2 = m_LetterHeight / 2;

		// check for vertical
		if(m_StartX == m_EndX)
		{
			if(m_StartY <= m_EndY)
			{
				// down
				g.drawLine(m_StartX*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, m_StartX*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
				g.drawLine((m_StartX+1)*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, (m_StartX+1)*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
				g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, 180);
				g.drawArc(m_StartX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, -180);
				return;
			}
			else
			{
				// up
				g.drawLine(m_StartX*m_LetterWidth, (m_EndY*m_LetterHeight)+h2, m_StartX*m_LetterWidth, (m_StartY*m_LetterHeight)+h2);
				g.drawLine((m_StartX+1)*m_LetterWidth, (m_EndY*m_LetterHeight)+h2, (m_StartX+1)*m_LetterWidth, (m_StartY*m_LetterHeight)+h2);
				g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, -180);
				g.drawArc(m_StartX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, 180);
				return;
			}
		}
		// check for horizontal
		else if(m_StartY == m_EndY)
		{
			if(m_StartX <= m_EndX)
			{
				// right
				g.drawLine(m_StartX*m_LetterWidth+w2, m_StartY*m_LetterHeight, m_EndX*m_LetterWidth+w2, m_StartY*m_LetterHeight);
				g.drawLine(m_StartX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight, m_EndX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight);
				g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -90, -180);
				g.drawArc(m_EndX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -270, -180);
				return;
			}
			else
			{
				// left
				g.drawLine(m_EndX*m_LetterWidth+w2, m_StartY*m_LetterHeight, m_StartX*m_LetterWidth+w2, m_StartY*m_LetterHeight);
				g.drawLine(m_EndX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight, m_StartX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight);
				g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -270, -180);
				g.drawArc(m_EndX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -90, -180);
				return;
			}
		}
		// check for diagonal
		else if(abs(VarX) == abs(VarY))
		{				
			int jj;

			if(m_StartX <= m_EndX)
			{
				if(m_StartY <= m_EndY)
				{
					// down and right
					g.drawLine(m_StartX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight, m_EndX*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
					g.drawLine((m_StartX+1)*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, (m_EndX*m_LetterWidth)+w2, m_EndY*m_LetterHeight);
					g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, 270);
					g.drawArc(m_EndX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 90, -270);
				}
				else
				{
					// up and right
					g.drawLine(m_StartX*m_LetterWidth+w2, m_StartY*m_LetterHeight, m_EndX*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
					g.drawLine((m_StartX+1)*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, m_EndX*m_LetterWidth+w2, (m_EndY+1)*m_LetterHeight);
					g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, -270);
					g.drawArc(m_EndX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -90, 270);
				}
			}
			else
			{
				if(m_StartY <= m_EndY)
				{
					// down and left
					g.drawLine(m_StartX*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, m_EndX*m_LetterWidth+w2, m_EndY*m_LetterHeight);
					g.drawLine(m_StartX*m_LetterWidth+w2, (m_StartY+1)*m_LetterHeight, (m_EndX+1)*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
					g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, -90, 270);
					g.drawArc(m_EndX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, -270);
				}
				else
				{
					// up and left
					g.drawLine(m_StartX*m_LetterWidth, (m_StartY*m_LetterHeight)+h2, m_EndX*m_LetterWidth+w2, (m_EndY+1)*m_LetterHeight);
					g.drawLine(m_StartX*m_LetterWidth+w2, m_StartY*m_LetterHeight, (m_EndX+1)*m_LetterWidth, (m_EndY*m_LetterHeight)+h2);
					g.drawArc(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 90, -270);
					g.drawArc(m_EndX*m_LetterWidth, m_EndY*m_LetterHeight, m_LetterWidth, m_LetterHeight, 0, 270);
				}
			}
		}
		// just highlight starting letter
		else
		{
			g.setColor(Color.black);
			g.drawOval(m_StartX*m_LetterWidth, m_StartY*m_LetterHeight, m_LetterWidth, m_LetterHeight);
		}
	}

	public int abs(int i)
	{
		if(i >= 0)
			return i;
		else
			return i*-1;
	}

	public void Debug(String s)
	{
		System.out.println(s);
	}

}

class SearchList extends Panel
{
	WordList	m_WordList;
	WordSearch	m_Parent;

	SearchList(URL WordSource, WordSearch Parent)
	{	
		m_Parent = Parent;
		m_WordList = new WordList(WordSource, this);
		setLayout(new GridLayout(0,1));
		add(m_WordList);
	}
		
	public int CheckWord(String s)
	{
		System.out.println("Checking "+s);
		int ret = m_WordList.CheckWord(s);
		if(ret == 2)
			m_Parent.PlayWinMusic();
		else if(ret == 1)
			m_Parent.GoodWord();
		else
			m_Parent.BadWord();

		return ret;
	}


	public static void Error(String errStr)
	{
		System.out.println(errStr+"\n");
	}
	

}


class WordList extends Canvas
{
	Color c1 = Color.black;
	Color c2 = Color.lightGray;
	Font m_Font; 

	SearchList	m_Parent;
	Vector		m_Words;	
	int			m_NumFound;

	public WordList(URL WordSource, SearchList Parent)
	{
		m_Parent = Parent;
		m_Words = new Vector();
		m_NumFound= 0;
		m_Font = new Font("Arial", Font.PLAIN, 10);

		readWordFile(WordSource);
	}

    public Dimension minimumSize() 
    {
		return new Dimension(150,250);
    }

    public Dimension preferredSize() 
    {
		return minimumSize();
    }
	
    public void paint(Graphics g) 
    {
		g.setFont(m_Font);

		int w = size().width;
		int h = size().height;

		g.setColor(Color.black);
		g.drawRect(0, 0, w - 1, h - 1);
		g.setColor(Color.cyan);
		g.fillRect(1, 1, w - 2, h - 2);


		// display words
		int IncY = g.getFontMetrics().getHeight();
		
		int NumRows = h / IncY;
		int NumCols = (m_Words.size() / NumRows)+1;

		int ElementNum = 0;

		for(int ii=0; ii<NumCols; ii++)
		{
			for(int jj=0; jj<NumRows; jj++)
			{
				// jump out at last element
				if(ElementNum >= m_Words.size())
					return;

				SearchWord sWord = (SearchWord)m_Words.elementAt(ElementNum);
				if(sWord.IsFound())
					g.setColor(c2);
				else
					g.setColor(c1);


				g.drawString(sWord.GetWord(), ((w/NumCols)*ii)+2, (jj+1)*IncY);

				ElementNum++;
			}
		}			
	}

	private void readWordFile(URL WordFile)
	{	
		URLConnection uc = null;
		InputStream is = null;

		try
		{
			uc = WordFile.openConnection();
		}
		catch(IOException e)
		{
			Debug("Connection");
			return;
		}

		try
		{
			is = uc.getInputStream();
		}
		catch(IOException e)
		{
			Debug("getInputStream");
			return;
		}

		DataInputStream sourceFile = new DataInputStream(is);


		String line;
		try
		{
			while((line = sourceFile.readLine()) != null)
			{
				SearchWord sWord = new SearchWord(line);

				m_Words.addElement((Object)sWord);
			}
		}
		catch(IOException e)
		{
			Debug("I/O Error");
			System.exit(1);
		}
	}

	public int CheckWord(String s)
	{
		//Debug("CheckWord: "+s);
		for(int ii=0; ii<m_Words.size(); ii++)
		{
			SearchWord sWord = (SearchWord)m_Words.elementAt(ii);
			if(!sWord.IsFound())
			{		
				//Debug("Word #"+ii+": "+sWord.GetWord().toUpperCase());

				if(s.equals(sWord.GetWord().toUpperCase()))
				{
					Debug("Found: "+s);
					m_NumFound++;

					if(m_NumFound == m_Words.size())
						return 2;

					sWord.Found();
					repaint();
					return 1;
				}
			}
		}
		return 0;
	}

	public void Debug(String s)
	{
		System.out.println(s);
	}

}

class SearchWord
{
	String 	m_WordStr;
	boolean	m_Found;

	SearchWord(String s)
	{
		m_WordStr = s;
		m_Found = false;
	}

	public boolean IsFound()
	{
		return m_Found;
	}

	public void Found()
	{
		m_Found = true;
	}

	public String GetWord()
	{
		return m_WordStr;
	}
}
