/**
 * File:   Swarm.java
 * Author: Bill Tschumy -- Otherwise
 *
 * This Turtle creates a swarm of MouseChasers to chase the mouse.
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.otherwise.jurtle.*;
import java.util.Random;

public class Swarm extends Turtle
{

    public void runTurtle()
    {
        hideTurtle();

        Console.print( "How many Turtle objects would you like to create? " );
        int numTurtles = Console.readInt();

        Console.println( "Click mouse to double width, Shift-Click to halve width, Control-Click to toggle Pen/Erase Mode." );

        for ( int i = 0; i < numTurtles; i++ )
        {
            MouseChaser chaser = new MouseChaser();
            chaser.start();
        }
        waitForStop();
    }



    /**
     * Original Author: Bill Tschumy -- Otherwise
     * Enhancements: John Kirkilis -- Austin Waldorf School
     *    11/01/03: Added color cycling and penWidth changing depending on mouse clicks
     *    11/02/03: Control-Click toggles between paint and erase modes
     *
     * This Turtle will chase the mouse when run.
     */    
     private class MouseChaser extends Turtle
    {
        private final static int HOVER_DIAMETER = 1;
        private final static int HOVER_CNT_INIT = 10;
        private final static double MIN_FORWARD_FRAC = 0.1;
        private final static double MAX_FORWARD_FRAC = 0.5;

        private final static int PEN_PAINT_MODE = 1;
        private final static int PEN_ERASE_MODE = 0;

        private final static boolean debug = false;

        private Point currentMouse;
        private Random rnd = new Random();
        int hoverCnt;
        int offsetX;
        int offsetY;
        double forwardFrac;

        private boolean MousePressOccured = false; // used for tracking mouse clicks
        private int penWidth = 1;             // to increase  or decrease penWidth
        private boolean shiftKeyDown, controlKeyDown = false;
        private int penMode = PEN_PAINT_MODE;

        public void runTurtle()
        {
            Dimension size = getDisplaySize();

            int x = rnd.nextInt( size.width );
            int y = rnd.nextInt( size.height );
            int hue = 0;

            penUp();
            setPosition( x, y );
            penDown();
            setPenColor( Color.blue );

            JPanel display = getDisplay();
            MouseMotionAdapter mma = new MouseMotionAdapter()
                                     {
                                         public void mouseMoved( MouseEvent evt )
                                         {
                                             currentMouse = evt.getPoint();
                                         }
                                     };
            display.addMouseMotionListener( mma );

            MouseAdapter ma = new MouseAdapter()            // added mouse click adapter
                              {
                                  public void mousePressed( MouseEvent evt )
                                  {
                                      MousePressOccured = true;
                                      shiftKeyDown = evt.isShiftDown();
                                      controlKeyDown = evt.isControlDown();
                                  }
                              };
            display.addMouseListener( ma );

            while ( true )
            {
                pause( 50 );
                chaseMouse();
                if ( penMode == PEN_PAINT_MODE )
                    setPenColor( Color.getHSBColor( ( hue++ % 50 ) / 50.0f, 1.0f, 1.0f ) ); // cycle penColor
                else
                    setPenColor ( getDisplayColor() );
            }

        }


        public void chaseMouse()
        {
            if ( currentMouse != null )
            {
                if ( MousePressOccured )
                {
                    if ( !shiftKeyDown && !controlKeyDown )     // normal mouse press doubles penWidth
                        processNoKeys();
                    else if ( shiftKeyDown && !controlKeyDown )     // shift mouse press
                    {
                        processShiftKey();
                        shiftKeyDown = false;
                    }

                    else if ( controlKeyDown && !shiftKeyDown )
                    {
                        processControlKey();
                        controlKeyDown = false;
                    }
                    else if ( controlKeyDown && shiftKeyDown )
                    {
                        processShiftKey();
                        shiftKeyDown = false;
                        processControlKey();
                        controlKeyDown = false;
                    }

                    setPenWidth( penWidth );
                    MousePressOccured = false;
                }

                Point currentTurtle = getPosition();

                // Every so often we adjust the hover offsets and the fraction of the
                // distance to the mouse that we move forward.

                if ( hoverCnt-- <= 0 )
                {
                    offsetX = rnd.nextInt( HOVER_DIAMETER ) - HOVER_DIAMETER / 2;
                    offsetY = rnd.nextInt( HOVER_DIAMETER ) - HOVER_DIAMETER / 2;
                    forwardFrac = MIN_FORWARD_FRAC +
                                  ( rnd.nextFloat() * ( MAX_FORWARD_FRAC - MIN_FORWARD_FRAC ) );
                    hoverCnt = HOVER_CNT_INIT;
                }

                double deltaX = currentMouse.x - currentTurtle.x + offsetX;
                double deltaY = currentMouse.y - currentTurtle.y + offsetY;

                if ( deltaX != 0.0 || deltaY != 0.0 )
                {
                    double heading;
                    if ( deltaX == 0 )
                        heading = deltaY < 0 ? 0 : Math.PI;
                    else
                        heading = Math.atan( deltaY / deltaX ) + ( Math.PI / 2 );
                    if ( deltaX < 0 )
                        heading += Math.PI;

                    setHeading( radiansToDegrees( heading ) );

                    double forwardAmt = forwardFrac * Math.sqrt( deltaX * deltaX + deltaY * deltaY );
                    if ( forwardAmt > 0.5 )
                        forward( forwardAmt );
                }
            }
        }


        /**
         *  Converts radians to degrees in the displays coordinate system.
         */
        private double radiansToDegrees( double radians )
        {
            return radians * 360 / ( 2 * Math.PI );
        }

        private boolean isPenPaint()
        {
            return ( penMode == PEN_PAINT_MODE );
        }

        private void updateStatus ()
        {
            String penModeString;
            if ( penMode == PEN_PAINT_MODE )
                penModeString = "Paint";
            else
                penModeString = "Erase";

            if ( debug )
            {
                Console.println( "Pen width set to " + penWidth + " pixels in " + penModeString + " mode." );
                Console.println( "Click mouse to double width, Shift-Click to halve width, Control-Click to toggle Pen/Erase Mode." );
                Console.println();
            }
        }

        private void togglePaintMode ()
        {
            if ( isPenPaint() )
            {
                //penErase();
                //setPenColor ( getDisplayColor() );
                penMode = PEN_ERASE_MODE;
                updateStatus();
            }
            else
            {
                //penPaint();
                penMode = PEN_PAINT_MODE;
                updateStatus();
            }
        }

        private void processNoKeys()
        {
            if ( penWidth == 512 )
                Console.println( "Pen width cannot exceed 512 pixels. Use shift-click to reduce width." );
            else
            {
                penWidth *= 2;
                updateStatus();
            }
        }

        private void processControlKey()
        {
            togglePaintMode();
        }

        private void processShiftKey()
        {
            if ( penWidth == 1 )
                Console.println( "Pen width cannot be made less than 1 pixel. Click mouse to double pen width." );
            else
            {
                penWidth /= 2;
                updateStatus();
            }
            shiftKeyDown = false;
        }

    }


}
