package control;

import control.*;
import java.awt.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;

/**
 * The Turtle class provides LOGO graphics functionality to the Screen.
 */
public class Turtle extends Sprite {
    
    /*
     * logo commands:
     * forward back left right setpc penup pendown hideturtle showturtle clearscreen repeat to
     */
    
    class State {
        double angle;
        double x;
        double y;
    }
    
    Stack stateStack;
    
    boolean penDown= true;
    int cycleDelay;
    
    Color color;
    Stroke stroke;
    Font font;
   
    /**
     * creates a new Turtle on the Graph.
     * @param graph graph object where the Turtle will live
     */
    public Turtle( Screen graph ) {
        super( "/chess/turtle.png", graph, 200, 200, 0, 14 );
        this.stateStack= new Stack();        
        color= Color.blue.darker().darker();
        stroke= new BasicStroke(1.f);
        font= new Font( "SansSerif", Font.PLAIN, 12 );
        home();
        fast();
    }
    
    /**
     * returns the turtle to its home position.
     */
    public void home() {
        setPosition( 200, 200 );
        setOrientation(-90);
        graph.repaint();
    }
    
    /**
     * Moves the turtle forward i pixels, drawing a line if the pen is down
     * @param i distance forward in pixels.
     */
    
    public void forward( double i ) {
        double x0= x;
        double y0= y;
        double x2= x + Math.cos(angle) * i;
        double y2= y + Math.sin(angle) * i;
        x= x2;
        y= y2;              
        if ( penDown ) {
            graph.line((int)x0,(int)y0,(int)x,(int)y, color, stroke );
        }
        setPosition( x2, y2 );
        graph.repaint();
        pause();        
    }
    
    /**
     * Moves the turtle backward i pixels, drawing a line if the pen is down.
     * @param i distance in pixels
     */
    public void back( double i ) {
        forward(-1*i);
    }
    
    /**
     * Turn the turtle right i degrees.
     * @param i angle in degrees to turn right.
     */
    public void right(double i) {
        angle += i * Math.PI / 180;
        resetTransform();
        graph.repaint();
        pause();
    }
    
    /**
     * Turn the turtle left i degrees.
     * @param i angle in degrees to turn left.
     */
    public void left(double i) {
        angle -= i * Math.PI / 180;
        resetTransform();
        graph.repaint();        
        pause();
    }
    
    private AffineTransform getAffineTransform() {
        AffineTransform result= AffineTransform.getTranslateInstance(x, y);
        result.rotate(angle);
        return result;
    }
    
    /**
     * move the turtle forward i pixels, drawing a circle with diameter equal to 
     * i pixels
     * @param i diameter of the circle.
     */
    public void circle(int i) {
        double x2= x + Math.cos(angle) * i;
        double y2= y + Math.sin(angle) * i;
        if ( penDown ) {
            graph.circle( (int)x2, (int)y2, i, color, stroke );
        }
        x2= x2 + Math.cos(angle) * i;
        y2= y2 + Math.sin(angle) * i;
        setPosition( x2, y2 );
        graph.repaint();
        pause();
    }
    
    /**
     * draw text as the turtle advances
     * @param text text to draw.
     */ 
    public void text( String text ) {
        Graphics2D g= graph.getGraphics();
        g.setColor(color);
        g.setStroke(stroke);
        g.setFont( font );
        
        g.setTransform(getAffineTransform());        
        g.drawString( text, 0, 0 );        
        int w= g.getFontMetrics().stringWidth(text);
        double x2= x + Math.cos(angle) * w;
        double y2= y + Math.sin(angle) * w;        
        setPosition( x2, y2 );
        graph.repaint();
        pause();
    }
    
    /**
     * draw an image as the turtle advances
     * @param filename image filename, a .gif or .png file.
     */ 
    public void image( String filename ) {
        Graphics2D g= graph.getGraphics();
        g.setColor(color);
        g.setStroke(stroke);
        g.setTransform(getAffineTransform());
        Image i= graph.getImage(filename);
        g.drawImage( i, 0, 0, null );
        int w= i.getWidth(null);
        double x2= x + Math.cos(angle) * w;
        double y2= y + Math.sin(angle) * w;
        setPosition( x2, y2 );
        graph.repaint();
        pause();
    }
    
    /**
     * draw an image as the turtle advances, scaling it so that the turtle moves
     * length pixels forward.
     * @param filename image filename, a .gif or .png file.
     * @param length the new length in pixels.
     */     
    public void image( String filename, int length ) {
        Graphics2D g= graph.getGraphics();
        g.setColor(color);
        g.setStroke(stroke);
        g.setTransform(getAffineTransform());        
        Image i= graph.getImage(filename);
        int h= i.getHeight(null);
        int w= i.getWidth(null);        
        g.drawImage( i, 0, 0, w*length/h, length, null );
        double x2= x + Math.cos(angle) * length;
        double y2= y + Math.sin(angle) * length;
        setPosition( x2, y2 );
        graph.repaint();
        pause();
    }

    /**
     * pick up the pen so that the turtle doesn't leave a line as it moves forward
     */
    public void penUp() {
        penDown= false;
        graph.repaint();
        pause();
    }
    
    /**
     * put down the pen so that the turtle leaves a line as it moves forward
     */
    public void penDown() {
        penDown= true;
        graph.repaint();
        pause();
    }
    
    /**
     * put the turtle in slow-moving mode.  This pauses shortly after each movement so
     * that its movements can be carefully tracked.
     */
    public void slow() {
        cycleDelay= 50;
    }
    
    /**
     * put the turtle in fast-moving mode.
     */
    public void fast() {
        cycleDelay= 5;
    }
    
    /**
     * put the turtle in ultra-fast-moving move.
     */
    public void ultraFast() {
        cycleDelay= 0;
    }
    
    /**
     * remember the current turtle position by pushing in onto the stack.
     */
    public void push() {
        State state= new State();
        state.x= x;
        state.y= y;
        state.angle= angle;
        stateStack.push(state);
        pause();
    }
    
    /**
     * recall the last pushed position.
     */
    public void pop() {
        State state= (State)stateStack.pop();
        this.x= state.x;
        this.y= state.y;
        this.angle= state.angle;
        resetTransform();
        graph.repaint();
        pause();
    }
    
    private void pause() {
        graph.repaint();
        if ( cycleDelay>0 ) {
            try {
                Thread.sleep( cycleDelay );
            } catch ( InterruptedException e ) {
            }
        }
    }   
    
    /*
     * sets the pen color by specifying the Hue (rainbow color), 
     * Saturation (0=black and white, 1=color) and Value ( 0=dark, 1=bright)
     */
    private static Color getHSVColor( double h, double s, double v ) {
        
        double r=-99999, g=-99999, b=-99999;
        
        double hTemp;
        
        if (s==0.0 ) {
            r=v;
            g=v;
            b=v;
        } else {
            hTemp= h % 360;
            
            hTemp= hTemp/60.0f; //  -- h is now in [0,6)
            int i= (int)Math.floor(hTemp);  // -- largest integer <= h
            double f= hTemp-i;          // -- fractional part of h
            
            double p=v*(1.0f-s);
            double q=v*(1.0f-(s*f));
            double t=v*(1.0f-(s*(1.0f-f)));
            
            switch (i) {
                case 0:
                    r = v;
                    g = t;
                    b = p;
                    break;
                case 1:
                    r = q;
                    g = v;
                    b = p;
                    break;
                case 2:
                    r = p;
                    g = v;
                    b = t;
                    break;
                case 3:
                    r = p;
                    g = q;
                    b = v;
                    break;
                case 4:
                    r = t;
                    g = p;
                    b = v;
                    break;
                case 5:
                    r = v;
                    g = p;
                    b = q;
                    break;
            }
        }
        
        return new Color( (float)r, (float)g, (float)b, 1.0f );
    }
    
    /**
     * Set the pen color
     * @param c new java.awt.Color, such as Color.red or Color.black
     */
    public void setColor( Color c ) {
        color= c;
    }
    
    /**
     * Set the pen color by specifying the hue, 0-360 degrees on the color wheel
     * @param hue set the new color by specifying the hue only.  The hue is an angle on the 
     * color wheel between 0 and 360 degress.
     */
    public void setColor( int hue ) {
        // java gets mad when the hue is <0 even though it is modulo 360.
        hue= hue % 360; 
        if ( hue < 0 ) hue+= 360;
        setHSVColor( hue, 1.0, 1.0 );
    }
    
    /**
     * Set the pen color by specifying the hue, saturation, and value.
     * @param hue 0-360 degrees on the color wheel. 
     * @param sat 0 = black and white, 1.0 = color
     * @param value is like brightness, 0.= completely off, 1.= completely on.
     */
    public void setHSVColor( double hue, double sat, double value ) {
        color= getHSVColor( (float)hue,  (float)sat, (float)value );
    }
    
    /**
     * Set the pen color by specifying the amount of red, green and blue.
     * each from 0-255.  So (0,0,0) is black, (255,255,255) is white,
     * (255,0,0) is red, etc.
     * @param red red intensity 0-255.
     * @param green green intensity 0-255.
     * @param blue blue intensity 0-255.
     */
    public void setRGBColor( double red, double green, double blue ) {
        color= new Color( (float)red, (float)green, (float)blue, 1.0f );
    }
    
    /**
     * set the font for text()
     * @param font a Font object to specify the font to use for text(), such as 
     * <pre>new Font( "SansSerif", Font.PLAIN, 12 )</pre> or 
     * <pre>Font.decodeFont( "SanSerif-12" )</pre>
     */
    public void setFont( Font font ) {
        this.font= font;
    }
    
    /**
     * set the size of the current font
     * @param pointSize int pointSize of the font, which is roughly the height 
     * of the letter T in pixels.
     */
    public void setFontSize( double pointSize ) {
        this.font= font.deriveFont( (float)pointSize );
    }
    
    /**
     * set the width of the line drawn by the turtle.
     * @param width width in pixels.
     */
    public void setLineWidth( double width ) {
        stroke= new BasicStroke( (float)width );
    }
    
    
}

