/*
 * Author: Danilov N.Y. <NikitaDanilov@yahoo.com> http://nikita.w3.to
 * Maintainer: Danilov N.Y.
 * Keywords: Pilots Brothers, puzzle
 *
 * Copyright (C) 1999 Danilov N.Y.
 *
 * This file is part of Pilots Brothers Puzzle.
 *
 * Pilots Brothers Puzzle 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, or (at your option)
 * any later version.
 *
 * This software 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 Library General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this software; see the file COPYING.  If not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 *
 */

/*
  You can get COPYING from http://WWW.Geocities.COM/NikitaDanilov/code/COPYING.gz
 */
/*
  If Pilots Brothers happened to be someone registered trademark,
  it is reserved by its respective owner(s).
 */

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.awt.geom.*;
import java.util.*;
import java.io.*;

public class PilotsPuzzleGeneric extends java.applet.Applet
{
	public PilotsPuzzleGeneric()
	{
		super();

	};

	public void init()
	{
		ActionListener listener;

		classic = ( getIntParameter( "classic", 10, 0 ) == 1 );
		rows = getIntParameter( "rows", 10, 4 );
		cols = getIntParameter( "cols", 10, 4 );

		if( classic )
			{
				modulus = 2;
			}
		else
			{
				modulus = getIntParameter( "modulus", 10, 10 );
			}
		setBackground( new Color( getIntParameter( "background", 0x10, 0xffffff ) ) );
		setForeground( new Color( getIntParameter( "foreground", 0x10, 0x000000 ) ) );

		matrix = new Canvas()
			{
				public void paint( Graphics g )
					{
						if( buffer != null )
							{
								g.drawImage( buffer, 0, 0, this );
							}
					};
			};

		rowCounter = new TextField( String.valueOf( rows ) );
		colCounter = new TextField( String.valueOf( cols ) );
		modCounter = new TextField( String.valueOf( modulus ) );
		classicSwitch = new Checkbox( "Classic", classic );

		head = new Panel( new FlowLayout() );

		head.add( new Label( "Rows: " ) );
		head.add( rowCounter );
		head.add( new Label( "Columns: " ) );
		head.add( colCounter );
 		head.add( modLabel = new Label( "Modulo: " ) );
		head.add( modCounter );

		head.add( classicSwitch );
		
		listener = new ActionListener()
			  {
				  public void actionPerformed( ActionEvent event )
					  {
						  takeSizes();
						  rebuild();
					  };
			  };

		rowCounter.addActionListener
			( listener );

		colCounter.addActionListener
			( listener );

		modCounter.addActionListener
			( listener );

		classicSwitch.addItemListener
			( new ItemListener()
			  {
				  public void itemStateChanged( ItemEvent click )
					  {
						  classic = classicSwitch.getState();
						  takeSizes();
						  startUp();
						  start();
						  rebuild();
						  head.repaint();
					  };
			  } );
									  
		setLayout( new BorderLayout() );

		add( head, "North" );
		add( matrix, "Center" );

		matrix.addMouseListener
			( new MouseAdapter()
			  {
				  public void mouseClicked( MouseEvent event )
					  {
						  int col;
						  int row;

						  col = event.getX() / cellWidth();
						  row = event.getY() / cellHeight();
						  if( event.isControlDown() )
							  {
								  doPopup( row, col );
							  }
						  else if( event.isShiftDown() )
							  {
								  doHint( row, col );
							  }
						  else
							  {
								  touch( matrix.getGraphics(), row, col );
								  matrix.repaint();
							  }
					  };
			  } );
		matrix.addComponentListener
			( new ComponentAdapter()
			  {
				  public void componentResized( ComponentEvent event )
					  {
						  initImage();
					  };
			  } );
		popup = new Dialog( new Frame(), "Change Element Settings", true );
		popup.setResizable( true );
		modEdit = new TextField();
		posEdit = new TextField();
	};

	public void start()
	{
		setVisible( true );
		doLayout();

		initImage();
		startUp();
	};

	public void stop()
	{
	};

	public static void main( String[] args )
	{
	};

	public String getAppletInfo()
	{
		return "Pilots Brothers Puzzle 0.5.1 (C) 1999 Danilov N.Y. <NikitaDanilov@yahoo.com> http://nikita.w3.to";
	};

	public String[][] getParameterInfo()
	{
		String[][] info = 
		{
			{ "classic", "0/1", "if 1 then the puzzle will imitate original puzzle from the 1C's game (default: 0)" },
			{ "rows", "1 .. 20", "number of rows of the puzzle (default: 4)" },
			{ "cols", "1 .. 20", "number of columnss of the puzzle (default: 4)" },
			{ "modulus", "1 .. infinity", "initial number of positions of each switch (deafult: 10)" },
			{ "background", "rrggbb", "hex representation of background colour (default: ffffff)" },
			{ "foreground", "rrggbb", "hex representation of background colour (default: 000000)" },
		};

		return info;
	};

	protected void rebuild()
	{
		counters = new int[ rows ][ cols ];
		moduli = new int[ rows ][ cols ];
			
		buffer.getGraphics().clearRect
			( 0, 0, 
			  buffer.getWidth( this ), 
			  buffer.getHeight( this ) );

		defect = 0;
		for( int i = 0 ; i < rows ; ++i )
			{
				for( int j = 0 ; j < cols ; ++j )
					{
						moduli[ i ][ j ] = classic ? 2 : modulus;
						counters[ i ][ j ] = ( int ) ( Math.random() * moduli[ i ][ j ] );
						defect += counters[ i ][ j ] % moduli[ i ][ j ];
						drawSwitch( buffer.getGraphics(), i, j );
					}
			}
		matrix.repaint();
	};

	protected void announceDefect()
	{
		showStatus( "Defectness: " + defect );
		if( defect == 0 )
			{
				Panel south;
				Button ok;

				won = new Dialog( new Frame(), "Congratulations!", true );
				won.setResizable( false );
				won.setSize( getSize() );

				won.add( new Label( "You won!", Label.CENTER ), "Center" );

				south = new Panel( new GridLayout( 1, 3 ) );
				south.add( new Label( "" ) );
				south.add( ok = new Button( "OK" ) );
				south.add( new Label( "" ) );

				ok.addActionListener
					( new ActionListener()
					  {
						  public void actionPerformed( ActionEvent event )
							  {
								  won.setVisible( false );
							  };
					  } );

				won.add( south, "South" );
				won.doLayout();
				won.setVisible( true );
			}
	};

	protected void initImage()
	{
		if( ( matrix.getSize().width > 0 ) &&
			( matrix.getSize().height > 0 ) )
			{
				buffer = createImage( matrix.getSize().width, matrix.getSize().height );
				rebuild();
			}
	};

	protected void takeSizes()
	{
		try
			{
				cols = Integer.valueOf( colCounter.getText() ).intValue();
				if( ( cols <= 0 ) || ( cols > 20 ) )
					{
						cols = 4;
					}
			}
		catch( NumberFormatException wrongValue )
			{}
		colCounter.setText( String.valueOf( cols ) );
		try
			{
				rows = Integer.valueOf( rowCounter.getText() ).intValue();
				if( ( rows <= 0 ) || ( rows > 20 ) )
					{
						rows = 4;
					}
			}
		catch( NumberFormatException wrongValue )
			{}
		rowCounter.setText( String.valueOf( rows ) );
		try
			{
				modulus = Integer.valueOf( modCounter.getText() ).intValue();
				if( ( modulus <= 0 ) )
					{
						modulus = 10;
					}
			}
		catch( NumberFormatException wrongValue )
			{}
		modCounter.setText( String.valueOf( modulus ) );
	};

	protected void drawSwitch( Graphics g, int row, int col )
	{
		Rectangle r;
		
		r = getBound( counters[ row ][ col ], moduli[ row ][ col ],
					  cellWidth() - 2 * xGap(),
					  cellHeight() - 2 * yGap() );
		g.clearRect( col * cellWidth(), row * cellHeight(), cellWidth(), cellHeight()  );
		if( !classic )
			{
				g.drawLine( r.x + col * cellWidth() + xGap(), 
							r.y + row * cellHeight() + yGap(), 
							r.x + col * cellWidth() + xGap() + r.width, 
							r.y + row * cellHeight() + yGap() + r.height );
				g.drawOval( r.x + col * cellWidth() + xGap() + r.width - barWidth()/2 ,
							r.y + row * cellHeight() + yGap() + r.height - barHeight()/2,
							barWidth(), barHeight() );
			}
		else
			{
				g.drawRect( r.x + col * cellWidth() + xGap(), 
							r.y + row * cellHeight() + yGap(), 
							r.width, r.height );
			}
	};

	protected Rectangle getBound( int position, int modulus, int width, int height )
	{
		int x0;
		int y0;
		int x1;
		int y1;

		if( !classic )
			{
				double alpha;

				alpha = 2 * Math.PI / modulus * ( position % modulus );

		
				x0 = ( int ) ( ( width - barWidth() ) * ( 1 - Math.sin( alpha ) ) / 2 );
				y0 = ( int ) ( ( height - barHeight() ) * ( 1 + Math.cos( alpha ) ) / 2 );
				
				x1 = ( int ) ( ( width - barWidth() ) * ( 1 + Math.sin( alpha ) ) / 2 );
				y1 = ( int ) ( ( height - barHeight() ) * ( 1 - Math.cos( alpha ) ) / 2 );
			}
		else
			{
				if( position % 2 == 0 ) /* hor */
					{
						x0 = 0;
						y0 = ( height - barHeight() )/ 2;
						
						x1 = width;
						y1 = y0 + barHeight() / 2;
					}
				else /* ver */
					{
						x0 = ( width - barWidth() )/ 2;
						y0 = 0;

 				x1 = x0 + barWidth() / 2;
 				y1 = height;
					}
			}
		return new Rectangle( x0, y0, x1 - x0, y1 - y0 );
	};

	protected void touch( Graphics g, int row, int col )
	{
		for( int i = 0 ; i < rows ; ++i )
			{
				defect -= counters[ i ][ col ] % moduli[ i ][ col ];
				++counters[ i ][ col ];
				defect += counters[ i ][ col ] % moduli[ i ][ col ];
				drawSwitch( buffer.getGraphics(), i, col );
			}
		for( int j = 0 ; j < cols ; ++j )
			{
				if( j != col )
					{
						defect -= counters[ row ][ j ] % moduli[ row ][ j ];
						++counters[ row ][ j ];
						defect += counters[ row ][ j ] % moduli[ row ][ j ];
						drawSwitch( buffer.getGraphics(), row, j );
					}
			}
		announceDefect();
	};

	protected int cellWidth()
	{
		return matrix.getSize().width / cols;
	};

	protected int cellHeight()
	{
		return matrix.getSize().height / rows;
	};

	protected int xGap()
	{
		return cellWidth() / 5;
	};

	protected int yGap()
	{
		return cellHeight() / 5;
	};
	
	protected int barHeight()
	{
		return cellWidth() / 8;
	};

	protected int barWidth()
	{
		return cellWidth() / 8;
	};

	protected int getIntParameter( String param, int radix, int def )
	{
		String value;

		value = getParameter( param );
		if( value == null )
			{
				return def;
			}
		else
			{
				try
					{
						return Integer.parseInt( value, radix );
					}
				catch( NumberFormatException wrongValue )
					{
						return def;
					}
			}
	};
	
	protected void startUp()
	{
		modCounter.setEnabled( !classic );
		/*		modLabel.setVisible( !classic ); */
	};

	protected void doPopup( final int row, final int col )
	{
		if( !classic )
			{
				Panel north;
				Panel center;

				TextField mod;
				TextField pos;
				Button ok;

				popup.removeAll();

				north = new Panel( new GridLayout( 1, 2 ) );
				center = new Panel( new GridLayout( 2, 2 ) );

				popup.add( north, "North" );
				popup.add( center, "Center" );

				modEdit.setText( String.valueOf( moduli[ row ][ col ] ) );
				posEdit.setText( String.valueOf( counters[ row ][ col ] ) );

				ok = new Button( "OK" );

				north.add( new Label( "Row: " + row ) );
				north.add( new Label( "Col: " + col ) );

				center.add( new Label( "Modulus: " ) );
				center.add( modEdit );
				center.add( new Label( "Position: " ) );
				center.add( posEdit );

				popup.add( ok, "South" );
				ok.addActionListener
					( new ActionListener()
					  {
						  public void actionPerformed( ActionEvent event )
							  {
								  try
									  {
										  moduli[ row ][ col ] = 
											  Integer.valueOf( modEdit.getText() ).intValue();
										  if( moduli[ row ][ col ] < 0 )
											  {
												  moduli[ row ][ col ] = 2;
											  }
										  counters[ row ][ col ] =
											  Integer.valueOf( posEdit.getText() ).intValue();
									  }
								  catch( NumberFormatException wrongValue )
									  {}
								  popup.setVisible( false );
							  };
					  } );

				popup.setSize( head.getSize().width, head.getSize().height * 4 );
				popup.doLayout();
				popup.setVisible( true );
			}
	};

	protected void doHint( int row, int col )
	{
		if( !classic )
			{
				showStatus( "Think yourself, MAN!" );
			}
		else
			{
				int turns;

				turns = 0;
				for( int i = 0 ; i < rows ; ++i )
					{
						turns += counters[ i ][ col ] % moduli[ i ][ col ];
					}
				for( int j = 0 ; j < cols ; ++j )
					{
						turns += counters[ row ][ j ] % moduli[ row ][ j ];
					}
				turns += counters[ row ][ col ] % moduli[ row ][ col ];
				turns = turns % moduli[ row ][ col ];
				showStatus( "Turn it " + turns + " times." );
			}
	};

	protected int rows = 4;
	protected int cols = 4;

	protected Panel  head;
	protected Canvas matrix;

	protected TextField rowCounter;
	protected TextField colCounter;
	protected TextField modCounter;
	protected Checkbox  classicSwitch;

	protected TextField modEdit;
	protected TextField posEdit;

	protected Label modLabel;

	protected int[][] counters;
	protected int[][] moduli;

	protected Image buffer;
	protected int modulus;

	protected boolean classic;

	protected Dialog popup;
	protected Dialog won;

	protected int defect;
};


