/**
*************************************************************************************************************
*    ____               ____     ____
* U | __")u    ___   U |  _"\ u |  _"\
*  \|  _ \/   |_"_|   \| |_) |//| | | |
*   | |_) |    | |     |  _ <  U| |_| |\
*   |____/   U/| |\U   |_| \_\  |____/ u
*  _|| \\_.-,_|___|_,-.//   \\_  |||_
* (__) (__)\_)-' '-(_/(__)  (__)(__)_)
*
* Bastli Interactive RGB Dancefloor - PC framework
*
**************************************************************************************************************
* Copyright (C) 2010 authors (see below)
*
* 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 3 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, see <http://www.gnu.org/licenses/>
**************************************************************************************************************
* authors:
*  Lukas Schrittwieser		- LS
*  Christian Reiter			- CR
**************************************************************************************************************
* versions and change log:
*  1.0 - LS
*     First realese
*  2.0 - CR
* 	  Competition entry
**************************************************************************************************************
*/

package BIRD;

import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Vector;
import javax.xml.bind.annotation.XmlElementDecl.GLOBAL;

class position
{
    position(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public int x;
    public int y;
}

class color
{
    public color ()
    {}
    
    public color (int r, int g, int b)
    {
        this.r = r;
        this.g = g;
        this.b = b;
    }
	
	public color ( Color c )
	{
		this.r = c.getRed();
		this.g = c.getGreen();
		this.b = c.getBlue();
	}

    public Color toColor ()
    {
        return new Color (r,g,b);
    }

    public int r;
    public int g;
    public int b;
}

class globals
{
	public static float update_speed = 0.04f;
	public static int width = 16;
	public static int height = 10;
}

interface state
{
    public void run ( Status s, Status last_state);
}

class renderer
{
    protected Random random;	
    private Color BackgroundColor;
    
    public renderer ()
    {
        random = new Random ();
		BackgroundColor = new Color (0,0,0);
    }
	
    public void setBackgroundColor ( Color c )
    {
        BackgroundColor = c;
    }

    public Color getBackgroundColor ()
    {
        return BackgroundColor;
    }

    /*
     * override this to get special color for each draw call
     */
    public Color drawPointColor (Color c, Color last_color)
    {
        return c;
    }

    /*
     * draws a point at a given location
     */
    public void drawPoint ( Status s, Status ls, int x, int y, Color c)
    {
        if ( x >= 0 && x < globals.width && y >= 0 && y < globals.height )
        {
            Color col = drawPointColor(c, ls.tiles[x][y]);
            s.tiles[x][y] = new Color (col.getRed(), col.getGreen(), col.getBlue());
        }
    }

    /*
     * draws a circle with bresenhams algorithm
     */
    public void drawCircle (Status s, Status ls, int x0, int y0, int radius, Color c)
    {
        int f = 1 - radius;
        int ddF_x = 0;
        int ddF_y = -2 * radius;
        int x = 0;
        int y = radius;

        drawPoint(s,ls, x0, y0 + radius, c);
        drawPoint(s,ls, x0, y0 - radius, c);
        drawPoint(s,ls, x0 + radius, y0, c);
        drawPoint(s,ls, x0 - radius, y0, c);

        while(x < y)
        {
          if(f >= 0)
          {
            y--;
            ddF_y += 2;
            f += ddF_y;
          }
          x++;
          ddF_x += 2;
          f += ddF_x + 1;

          drawPoint(s,ls, x0 + x, y0 + y, c);
          drawPoint(s,ls, x0 - x, y0 + y, c);
          drawPoint(s,ls, x0 + x, y0 - y, c);
          drawPoint(s,ls, x0 - x, y0 - y, c);
          drawPoint(s,ls, x0 + y, y0 + x, c);
          drawPoint(s,ls, x0 - y, y0 + x, c);
          drawPoint(s,ls, x0 + y, y0 - x, c);
          drawPoint(s,ls, x0 - y, y0 - x, c);
        }
    }

    /* [[Signum (Mathematik)|signum function]] */
    private int sgn(int x){
      return (x > 0) ? 1 : (x < 0) ? -1 : 0;
    }

    void drawLine(Status s, Status ls, int xstart,int ystart,int xend,int yend, Color c)
    /*--------------------------------------------------------------
     * Bresenham-Algorithmus: Linien auf Rastergeräten zeichnen
     *
     * Eingabeparameter:
     *    int xstart, ystart        = Koordinaten des Startpunkts
     *    int xend, yend            = Koordinaten des Endpunkts
     *
     * Ausgabe:
     *    void SetPixel(int x, int y) setze ein Pixel in der Grafik
     *         (wird in dieser oder aehnlicher Form vorausgesetzt)
     *---------------------------------------------------------------
     */
    {
        int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;

    /* Entfernung in beiden Dimensionen berechnen */
       dx = xend - xstart;
       dy = yend - ystart;

    /* Vorzeichen des Inkrements bestimmen */
       incx = sgn(dx);
       incy = sgn(dy);
       if(dx<0) dx = -dx;
       if(dy<0) dy = -dy;

    /* feststellen, welche Entfernung größer ist */
       if (dx>dy)
       {
          /* x ist schnelle Richtung */
          pdx=incx; pdy=0;    /* pd. ist Parallelschritt */
          ddx=incx; ddy=incy; /* dd. ist Diagonalschritt */
          es =dy;   el =dx;   /* Fehlerschritte schnell, langsam */
       } else
       {
          /* y ist schnelle Richtung */
          pdx=0;    pdy=incy; /* pd. ist Parallelschritt */
          ddx=incx; ddy=incy; /* dd. ist Diagonalschritt */
          es =dx;   el =dy;   /* Fehlerschritte schnell, langsam */
       }

    /* Initialisierungen vor Schleifenbeginn */
       x = xstart;
       y = ystart;
       err = el/2;
       drawPoint(s, ls, x,y,c);

    /* Pixel berechnen */
       for(t=0; t<el; ++t) /* t zaehlt die Pixel, el ist auch Anzahl */
       {
          /* Aktualisierung Fehlerterm */
          err -= es;
          if(err<0)
          {
              /* Fehlerterm wieder positiv (>=0) machen */
              err += el;
              /* Schritt in langsame Richtung, Diagonalschritt */
              x += ddx;
              y += ddy;
          } else
          {
              /* Schritt in schnelle Richtung, Parallelschritt */
              x += pdx;
              y += pdy;
          }
          drawPoint(s,ls,x,y,c);
       }
    } /* gbham() */

    public void drawRect ( Status s, Status ls,  int u_x, int u_y, int l_x, int l_y, Color c)
    {
        drawLine(s, ls, u_x, u_y, l_x, u_y, c);
        drawLine(s, ls, l_x, u_y, l_x, l_y, c);
        drawLine(s, ls, l_x, l_y, u_x, l_y, c);
        drawLine(s, ls, u_x, l_y, u_x, u_y, c);
    }
	
	public void setBackground ( Status s)
	{
		fillColor ( s, BackgroundColor );
	}

    /*
     * fills the whole filed with a color
     */
    public void fillColor ( Status s, Color c)
    {
        for (int i=0; i<s.sens.length; i++)
        {
                for (int j=0; j<s.sens[0].length; j++)
                {
                    s.tiles[i][j] = new Color (c.getRed(), c.getGreen(), c.getBlue());
                }
        }
    }

    public void fillColorMeta (meta_information[][] c, Color col)
    {
        for (int i=0; i<c.length; i++)
        {
                for (int j=0; j<c[0].length; j++)
                {
                    c[i][j] = new meta_information (new color(col.getRed(), col.getGreen(), col.getBlue()));
                }
        }
    }

    public color cutColor ( int red, int green, int blue )
    {
        color c = new color ();
        if ( red > 255 || red < 0 ) red = 0;
        if ( green > 255 || green < 0 ) green = 0;
        if ( blue > 255 || blue < 0 ) blue = 0;
        
        c.r = red;
        c.g = green;
        c.b = blue;

        return c;
    }

    public Color getRandomColor ()
    {
        return new Color (random.nextInt(256),random.nextInt(256),random.nextInt(256));
    }

    public color getRandomcolor ()
    {
        return new color (random.nextInt(256),random.nextInt(256),random.nextInt(256));
    }

    public Color getFadedColor (Color start, int fade_r, int fade_g, int fade_b)
    {
        color temp = cutColor(start.getRed()-fade_r, start.getGreen()-fade_b,start.getBlue()-fade_b);
        return new Color (temp.r,temp.g,temp.b);
    }
}

/*
 * manages the fading of a color value from a source to a target
 */
class colorFader
{
    public colorFader ( Color from, Color to, int steps)
    {
        from_ = new color ();
        to_ = new color ();
        
        from_.r = from.getRed ();
        from_.g = from.getGreen ();
        from_.b = from.getBlue ();

        to_.r = to.getRed ();
        to_.g = to.getGreen ();
        to_.b = to.getBlue ();

        steps_ = steps;
        step_ = 0;

        diff_ = new color ((to_.r-from_.r)/steps_,(to_.g-from_.g)/steps_,(to_.b-from_.b)/steps_);
    }

    public Color getNextColor ()
    {
        Color ret = new Color (from_.r+diff_.r*step_, from_.g+diff_.g*step_, from_.b+diff_.b*step_);
        if ( step_ < steps_ )
            ++step_;
        
        return ret;
    }

    private color from_;
    private color to_;
    private color diff_;
    private int steps_;
    private int step_;
}

/*
 * implements a loading effect
 */
class loading extends renderer implements state
{
    public loading ()
    {
        reset ();
    }

    public void reset ()
    {
        x_position = 0.0f;
        x_speed = 0.1f;
    }

    public boolean isfull () { boolean ret = full; full = false; return ret; }

    public void run ( Status s, Status last_state)
    {
        colorFader fade = new colorFader (new Color(0x636C69),new Color(0xB6C8C2),globals.height);
        
        for (int i = 0; i < globals.height; ++i)
        {
            Color c = fade.getNextColor();
            for (int j = 0; j < globals.width; ++j)
            {
                s.tiles[j][i] = c;
            }
        }

        if ( x_position >= (float)globals.width )
        {
            reset ();
            full = true;
        }

        // after some time increase loading speed
        if ( x_position >= 0.5f*globals.width) x_speed = 0.5f;

        for (int i = 0; i < (int)x_position; i+=2)
            drawPoint(s,last_state, i, globals.height/2-1, new Color (255,255,255));

        x_position += x_speed;
    }

    private float x_speed;
    private float x_position;
    private boolean full = false;
}

/*
 * Helper class to store infrmation in a 16x10 array
 */
class meta_information
{
    public meta_information (color c)
    {
        this.color = c;
        this.position = 0.0f;
    }
    
    public color color;
    public float position;
}

/*
 * implements the marker effect
 */
class marker extends renderer implements state
{
    public marker ()
    {
        random = new Random ();

        meta = new meta_information[globals.width][globals.height];
        fillColorMeta ( meta, new Color (0,0,0));
    }

    public void redraw_fade(int i, int j)
    {
        if ( i >= 0 && i < globals.width && j >= 0 && j < globals.height )
        {
            Color last_color = last_state.tiles[i][j];
            
            color new_color = cutColor(last_color.getRed()-dec_per_step,
                    last_color.getGreen()-dec_per_step,
                    last_color.getBlue()-dec_per_step);

            current_state.tiles[i][j] = new Color (new_color.r, new_color.g, new_color.b);
        }
    }
    
    public void run (Status s, Status last_state)
    {        
        this.current_state = s;
        this.last_state = last_state;
        
        for (int i = 0; i < s.sens.length; ++i)
            for (int j = 0; j < s.sens[0].length; ++j)
            {                
                if ( s.sens[i][j] )
                {
                    color col = meta[i][j].color;
                    if ( col.r == 0 && col.g == 0 && col.b == 0 ) // black means void
                        meta[i][j].color = new color (100+random.nextInt(156),
                                100+random.nextInt(156),
                                100+random.nextInt(156));

                    redraw_fade(i-1, j-1);
                    redraw_fade(i-1, j);
                    redraw_fade(i-1, j+1);
                    redraw_fade(i, j+1);
                    redraw_fade(i+1, j+1);
                    redraw_fade(i+1, j);
                    redraw_fade(i+1, j-1);
                    redraw_fade(i, j-1);
                    
                    Color temp = new Color(col.r,col.g,col.b);
                    switch ((int)meta[i][j].position)
                    {
                        case 0: drawPoint(s, last_state, i-1, j-1, temp); break;
                        case 1: drawPoint(s, last_state, i-1, j, temp); break;
                        case 2: drawPoint(s, last_state, i-1, j+1, temp); break;
                        case 3: drawPoint(s, last_state, i, j+1, temp); break;
                        case 4: drawPoint(s, last_state, i+1, j+1, temp); break;
                        case 5: drawPoint(s, last_state, i+1, j, temp); break;
                        case 6: drawPoint(s, last_state, i+1, j-1, temp); break;
                        case 7: drawPoint(s, last_state, i, j-1, temp); break;
                    }

                    meta[i][j].position += update_speed;
                    if ( meta[i][j].position >= 8.0f ) meta[i][j].position = 0.0f;

                }
                else
                {
                    meta[i][j].color = new color(0,0,0);
                }
            }
    }

    private Random random;
    private Status current_state;
    private Status last_state;
    private meta_information[][] meta; // stores meta information
    private float update_speed = 0.5f;
    private int dec_per_step = 15;
}

class connections extends renderer implements state
{
    public connections ()
    {
		active = new ArrayList<position> ();
    }

    public void fade(int i, int j)
    {
        Color last_color = last_state.tiles[i][j];

        color new_color = cutColor(last_color.getRed()-dec_per_step,
                last_color.getGreen()-dec_per_step,
                last_color.getBlue()-dec_per_step);

        current_state.tiles[i][j] = new Color (new_color.r, new_color.g, new_color.b);
    }

    public void run (Status s, Status last_state)
    {
        this.current_state = s;
        this.last_state = last_state;

        for (int i = 0; i < s.sens.length; ++i)
            for (int j = 0; j < s.sens[0].length; ++j)
            {
                fade ( i, j );
                
                if ( s.sens[i][j] )
                {
                    if ( last_state.sens[i][j] == false || first ) // new node
					{
                        active.add(new position(i, j));
					}
                }
                else
                {
                    if ( last_state.sens[i][j] ) // remove just necessary if it was active in last step
                    {
                        position to_delete = null;
                        // remove position out of actve list
                        for (position m: active){if ( m.x == i && m.y == j ) { to_delete = m; break;}}

                        active.remove(to_delete);
                    }
                }
            }

        if ( time >= max_time )
        {
            // connect each node with each other
            for (int k = 0; k < active.size(); ++k)
            {
                position m = active.get(k);

                for (int l = k; l < active.size (); ++l)
                {
                    position n = active.get(l);

                    if ( m == n ) continue;

                    int fx = m.x;
                    int fy = m.y;
                    int tx = n.x;
                    int ty = n.y;

                    drawLine(current_state,last_state, fx, fy, tx, ty, getRandomColor());
                }
            }

            time = 0.0f;
        }

        time += globals.update_speed;
		first = false;
    }

    private Status current_state;
    private Status last_state;

    private List<position> active;

    float max_time = 0.8f;
    float time;
    int dec_per_step = 10;
	boolean first = true;
}

class texting extends renderer implements state
{
    // no Q!
    public texting ()
    {
        characters = new HashMap<Character, int[]>();

        int[] A = {
            1,1,1,
            1,0,1,
            1,1,1,
            1,0,1,
            1,0,1
        };
        characters.put('A', A);

        int[] B = {
            1,1,1,
            1,0,1,
            1,1,1,
            1,0,1,
            1,1,1
        };
        characters.put('B',B);

        int[] C = {
            1,1,1,
            1,0,0,
            1,0,0,
            1,0,0,
            1,1,1
        };
        characters.put('C',C);

        int[] D = {
          1,1,0,
          1,0,1,
          1,0,1,
          1,0,1,
          1,1,0
        };
        characters.put('D',D);

        int[] E = {
            1,1,1,
            1,0,0,
            1,1,1,
            1,0,0,
            1,1,1
        };
        characters.put('E',E);

        int[] F = {
            1,1,1,
            1,0,0,
            1,1,1,
            1,0,0,
            1,0,0
        };
        characters.put('F',F);
        
        int[] G = {
            1,1,1,
            1,0,0,
            1,1,1,
            1,0,1,
            1,1,1
        };
        characters.put('G',G);

        int[] H = {
            1,0,1,
            1,0,1,
            1,1,1,
            1,0,1,
            1,0,1
        };
        characters.put('H',H);

        int[] I = {
            0,1,0,
            0,1,0,
            0,1,0,
            0,1,0,
            0,1,0
        };
        characters.put('I',I);

        int[] J = {
            0,0,1,
            0,0,1,
            0,0,1,
            1,0,1,
            0,1,1
        };
        characters.put('J',J);

        int[] K = {
            1,0,1,
            1,1,0,
            1,1,0,
            1,0,1,
            1,0,1
        };
        characters.put('K',K);

        int[] L = {
            1,0,0,
            1,0,0,
            1,0,0,
            1,0,0,
            1,1,1
        };
        characters.put('L',L);

        int[] M = {
            1,0,0,0,1,
            1,1,0,1,1,
            1,0,1,0,1,
            1,0,0,0,1,
            1,0,0,0,1
        };
        characters.put('M',M);

        int[] N = {
            1,0,0,1,
            1,1,0,1,
            1,0,1,1,
            1,0,0,1,
            1,0,0,1
        };
        characters.put('N',N);

        int[] O = {
            1,1,1,
            1,0,1,
            1,0,1,
            1,0,1,
            1,1,1
        };
        characters.put('O',O);

        int[] P = {
            1,1,1,
            1,0,1,
            1,1,1,
            1,0,0,
            1,0,0
        };
        characters.put('P',P);

        int[] R = {
            1,1,1,
            1,0,1,
            1,1,0,
            1,0,1,
            1,0,1
        };
        characters.put('R',R);

        int[] S = {
            1,1,1,
            1,0,0,
            1,1,1,
            0,0,1,
            1,1,1
        };
        characters.put('S',S);
        
        int[] T = {
          1,1,1,
          0,1,0,
          0,1,0,
          0,1,0,
          0,1,0
        };
        characters.put('T',T);

        int[] U = {
            1,0,1,
            1,0,1,
            1,0,1,
            1,0,1,
            1,1,1
        };
        characters.put('U',U);

        int[] V = {
            1,0,1,
            1,0,1,
            1,0,1,
            1,0,1,
            0,1,0
        };
        characters.put('V',V);

        int[] W = {
            1,0,0,0,1,
            1,0,0,0,1,
            1,0,0,0,1,
            1,0,1,0,1,
            0,1,0,1,0
        };
        characters.put('W',W);

        int[] X = {
            1,0,1,
            0,1,0,
            0,1,0,
            0,1,0,
            1,0,1
        };
        characters.put('X',X);

        int[] Y = {
            1,0,1,
            1,0,1,
            0,1,0,
            0,1,0,
            0,1,0
        };
        characters.put('Y',Y);

        int[] Z = {
            1,1,1,
            0,0,1,
            0,1,0,
            1,0,0,
            1,1,1
        };
        characters.put('Z',Z);

        int[] NR0 = {
            1,1,1,
            1,0,1,
            1,0,1,
            1,0,1,
            1,1,1
        };
        characters.put('0',NR0);

        int[] NR1 = {
            0,0,1,
            0,1,1,
            0,0,1,
            0,0,1,
            0,0,1
        };
        characters.put('1',NR1);

        int[] NR2 = {
            1,1,1,
            0,0,1,
            0,1,1,
            1,0,0,
            1,1,1
        };
        characters.put('2',NR2);

        int[] NR3 = {
            1,1,1,
            0,0,1,
            0,1,1,
            0,0,1,
            1,1,1
        };
        characters.put('3',NR3);

        int[] NR4 = {
            1,0,1,
            1,0,1,
            1,1,1,
            0,0,1,
            0,0,1
        };
        characters.put('4',NR4);

        int[] NR5 = {
            1,1,1,
            1,0,0,
            1,1,1,
            0,0,1,
            1,1,1
        };
        characters.put('5',NR5);

        int[] NR6 = {
            1,1,1,
            1,0,0,
            1,1,1,
            1,0,1,
            1,1,1
        };
        characters.put('6',NR6);

        int[] NR7 = {
            1,1,1,
            0,0,1,
            0,1,1,
            0,0,1,
            0,0,1
        };
        characters.put('7',NR7);

        int[] NR8 = {
            1,1,1,
            1,0,1,
            1,1,1,
            1,0,1,
            1,1,1
        };
        characters.put('8',NR8);

        int[] NR9 = {
            1,1,1,
            1,0,1,
            1,1,1,
            0,0,1,
            1,1,1
        };
        characters.put('9',NR9);

        int[] SPACE = {
            0,0,0,
            0,0,0,
            0,0,0,
            0,0,0,
            0,0,0
        };
        characters.put(' ',SPACE);

        int[] EX = {
            0,1,0,
            0,1,0,
            0,1,0,
            0,0,0,
            0,1,0
        };
        characters.put('!',EX);

        int[] QEST = {
            0,1,0,
            1,0,1,
            0,1,0,
            0,0,0,
            0,1,0
        };
        characters.put('?',QEST);
        
        int[] POINT = {
            0,0,0,
            0,0,0,
            0,0,0,
            0,0,0,
            0,1,0
        };
        characters.put('.',POINT);


        font_color = getRandomColor();
    }

    public void drawCharacter (Status s,int[] c, Color color)
    {
		drawCharacter (s,c,color,character_width, character_height);
    }
	
	public void drawCharacter (Status s,int[] c, Color color, int width, int height)
	{
        for (int i = 0; i < height; ++i)
            for (int j = 0; j < width; ++j)
            {
                if ( c[i*width+j] == 1 )
                    drawPoint(current_state, last_state,(int)offset_x + write_pointer_x+j,(int)offset_y+i, color);
            }	
	}

    public void drawString ( String s, Color color)
    {
        s = s.toUpperCase();
        for(int i = 0; i < s.length(); ++i)
        {
            Character c = s.charAt(i);
            
			int width = character_width;
			
			if ( c == 'M' || c == 'W' )
				width += 2;
			else if ( c == 'N' )
				width += 1;
			
			drawCharacter(current_state, characters.get(c),color, width,character_height);
			
            write_pointer_x += width + 1;
        }
    }

    public void floatText ( boolean left, boolean down, boolean float_x, boolean float_y, String text, int start_x, int start_y)
    {
        if ( !float_x ) offset_x = start_x;
        if ( !float_y ) offset_y = start_y;
        
        drawString(text, font_color);

        if ( left )
        {
            offset_x -= update_speed;
            if ( offset_x <= (float)-write_pointer_x )
            {
                offset_x = (float)globals.width;
                finish = true;
            }
        }
        else
        {
            offset_x += update_speed;
            if ( offset_x >= (float)globals.width)
            {
                offset_x = (float)-write_pointer_x ;
                finish = true;
            }
        }

        if ( !down )
        {
            offset_y -= update_speed;
            if ( offset_y <= (float)-character_height )
            {
                offset_y = (float)globals.height;
                finish = true;
            }
        }
        else
        {
            offset_y += update_speed;
            if ( offset_y >= (float)globals.height)
            {
                offset_y = (float)-character_height;
                finish = true;
            }
        }

        write_pointer_x = 0;
    }
	
    public void setFontColor ( Color c )
    {
            font_color = c;
    }

    public Color getFontColor ()
    {
            return font_color;
    }
    
    public void run (Status s, Status ls)
    {
        current_state = s;
        last_state = ls;
        setBackground ( s );
	
        time += globals.update_speed;
    }
	
    public void set_x_offset (int value) { this.offset_x = value;}
    public void set_y_offset (int value) { this.offset_y = value;}

    public boolean finished () { return finish; }
    public void resetFinished () { finish = false;}

    private Status current_state;
    private Status last_state;

    private final int character_height = 5;
    private final int character_width = 3;

    private Color font_color;

    private float offset_x;
    private float offset_y;
    private int write_pointer_x;

    private float update_speed = 0.3f;

    private Map<Character, int[]> characters;

    private int state = 0;
    private float time;
    private float state_time = 3.0f;
    private final int max_state = 4;

    final float color_time = 1.0f;
    boolean finish = false;
}

class equalizer extends renderer implements state
{
    public equalizer ()
    {
        heights = new Vector<Integer> (globals.width);
        for (int i = 0; i < globals.width; ++i)
        {
            heights.add(random.nextInt(10));
        }
        
        // red to green
        from = new color(145,224,0);
        to = new color(255,76,0);
    }

    public void run (Status s, Status ls )
    {
        current_state = s;
        last_state = ls;

        update_time -= globals.update_speed;
        
        color diff = new color ((to.r-from.r)/globals.height,(to.g-from.g)/globals.height,(to.b-from.b)/globals.height);

        for (int i = 0; i < heights.size(); ++i)
        {
            // set all colors in a fading manner
            for (int j = 0; j < Math.abs(heights.elementAt(i)); ++j)
            {		
                s.tiles[i][globals.height-1-j] = new Color (from.r+diff.r*j, from.g+diff.g*j, from.b+diff.b*j);
            }
        }

        if ( update_time <= 0.0f )
        {
            for (int i = 0; i < heights.size (); ++i)
            {
                Integer n = heights.get(i);
                ++n;
                if ( n == globals.height || n == 0 || random.nextInt(10)==0 ) n = -n;

                heights.set(i,n);
            }
	
            update_time = max_update_time;
        }
    }

    private Status current_state;
    private Status last_state;

    private final float max_update_time = 0.15f;
    private float update_time = max_update_time;
    private Vector<Integer> heights;
    
    private color from;
    private color to;
}

class  stream extends renderer implements state
{
    /*
    * types:
    *	0: water
    *   1: fire
    */
    public stream ( int type )
    {
            init ( type, 0.05f );
    }
	
    public stream ( int type, float update_time )
    {
		init ( type, update_time );
    }

    private void init ( int type, float update_time )
    {
            this.type = type;
            this.max_update_time = update_time;
    }

    @Override
    public Color getRandomColor ()
    {
        Color[] colors;
	
		if ( type == 0 ) // water
		{
			colors = new Color[7];
			colors[0] = new Color (16,73,169);
			colors[1] = new Color (19,57,172);
			colors[2] = new Color (22,43,175);
			colors[3] = new Color (10,97,164);
			colors[4] = new Color (2,142,155);
			colors[5] = new Color (0,0,0);
			colors[6] = new Color (0,0,0);
		}
		else // fire
		{
			colors = new Color[2];
			colors[0] = new Color (0xFFAB40);
			colors[1] = new Color (0xFFB400);			
		}
        
        return colors[random.nextInt(colors.length)];
    }

    public void run (Status s, Status ls )
    {
        current_state = s;
        last_state = ls;

        update_time -= globals.update_speed;

        // update screen
        if ( update_time <= 0.0f )
        {
            if ( last_shown_state != null ) last_state = last_shown_state;
            
            // generate first row
            for (int j = 0; j < globals.width; ++j)
            {
                if ( type == 0 ) // water
                    current_state.tiles[j][0] = getRandomColor();
                else // fire
                    current_state.tiles[j][globals.height-1] = getRandomColor();
            }

            if ( type == 0) // water
            {
                // propagate the previous line
                for (int i = globals.height-1; i > 0; --i)
                {
                    for (int j = 0; j < globals.width; ++j)
                    {
                        // if above (and left/right) stands a person than no propagation
                        if ( !current_state.sens[j][i-1] )
                        {
                            if ( j > 0 && j < globals.width-1)
                            {
                                if ( !current_state.sens[j-1][i-1] && !current_state.sens[j+1][i-1])
                                    current_state.tiles[j][i] = last_state.tiles[j][i-1];
                            }
                            else
                                current_state.tiles[j][i] = last_state.tiles[j][i-1];
                        }
                    }
                }
            }
            else // fire
            {
                int[] more = new int[globals.width];

                // find out position (column) of person
                for (int i = 1; i < globals.width-1; ++i)
                    for (int j = 0; j < globals.height; ++j)
                    {
                        if (current_state.sens[i][j] ) more[i] = 2;
                        if (current_state.sens[i-1][j]) more[i] += 1;
                        if (current_state.sens[i+1][j]) more[i] += 1;
                    }

                // propagate the next upwards line
                for (int i = 0; i < globals.height-1; ++i)
                {
                    for (int j = 0; j < globals.width; ++j)
                    {
                        int extra = 6*more[j]; // more magic numbers! yeah!

                        int r_fade_speed = random.nextInt (10)+30-extra;
                        int g_fade_speed = random.nextInt (10)+30-extra;
                        int b_fade_speed = random.nextInt (10)+30-extra;

                        Color new_color = getFadedColor ( last_state.tiles[j][i+1], r_fade_speed, g_fade_speed, b_fade_speed );
                        current_state.tiles[j][i] = new_color;
                    }
                }
            }
            
            update_time = max_update_time;
            last_shown_state = current_state;
        }
        else if ( last_shown_state != null)
        {
            // else copy old values
            for (int i = 0; i < globals.width; ++i)
                for (int j = 0; j < globals.height; ++j)
                    current_state.tiles[i][j] = last_shown_state.tiles[i][j];
        }
    }

    private Status current_state;
    private Status last_state;
    private Status last_shown_state;

    private float max_update_time;
    private float update_time = max_update_time;
    private int type;
}

class marker2 extends renderer implements state
{
    public marker2 ()
    {
		reset();
    }
	
	public void reset ()
	{
        meta = new meta_information[globals.width][globals.height];
        fillColorMeta(meta, new Color (0,0,0));
	}

    public void run (Status s, Status ls )
    {
        current_state = s;
        last_state = ls;

        for (int i = 0; i < s.sens.length; ++i)
            for (int j = 0; j < s.sens[0].length; ++j)
            {
                if ( s.sens[i][j] )
                {
                    color col = meta[i][j].color;
                    if ( col.r == 0 && col.g == 0 && col.b == 0 ) // black means void
                        meta[i][j].color = new color (100+random.nextInt(156),
                                100+random.nextInt(156),
                                100+random.nextInt(156));

                    int radius = (int)meta[i][j].position;

                    Color new_color = new Color (col.r,col.g,col.b);
                    new_color = getFadedColor(new_color, 5, 6, 8);

                    col.r = new_color.getRed();
                    col.g = new_color.getGreen();
                    col.b = new_color.getBlue();
                    
                    drawRect(s, ls, i-radius, j-radius, i+radius, j+radius, new_color );

                    meta[i][j].position -= update_speed;
                    if ( meta[i][j].position <= 0.0f ) meta[i][j].position = max_radius;
                }
                else
                {
                    meta[i][j].color = new color(0,0,0);
                }
            }
    }
    
    private Status current_state;
    private Status last_state;

    private float update_speed = 0.1f;
    private final float max_radius = 4.0f;
    private meta_information[][] meta; // stores meta information
}

class strobo extends renderer implements state
{
    public strobo ()
    {
        reset ();
    }

    public void reset ()
    {
        update_speed = 0.4f;
        counter = update_speed;
        update_step = 0.03f;
    }

    public void run (Status s, Status ls)
    {
        current_state = s;
        last_state = ls;
        
        if ( counter <= 0.0f )
        {
            fillColor(s, Color.white);
            if ( update_speed >= 0.2)
                update_speed -= update_step;
            counter = update_speed;
        }

        counter -= globals.update_speed;
    }

    private Status current_state;
    private Status last_state;
    
    private float counter;
    private float update_speed;
    private float update_step;
}

class invader extends renderer
{
    public invader (int x, int y)
    {
        pos = new position (x,y);
        col = getRandomColor();
    }

    public void moveLeft () {pos.x--;}
    public void moveRight () {pos.x++;}
    public void moveDown () {pos.y++;}
    public void moveUp () {pos.y--;}

    public int getX () { return pos.x; }
    public int getY () { return pos.y; }

    public boolean isDirectionRight () { return directionRight; }
    public void setDirectionRight (boolean b){ directionRight = b; }

    public void draw ( Status s, Status ls)
    {
        int[] fig = {
            0,1,0,
            1,1,1,
            1,0,1
        };
        figureWidth = 3;
        figureHeight = 4;

        for (int i = 0; i < fig.length/figureWidth; ++i)
        {
            for (int j = 0; j < figureWidth; ++j)
            {
                if ( fig[i*figureWidth+j] == 1)
                    drawPoint(s, ls, j+pos.x, i+pos.y, col);
            }
        }
    }

    public boolean collidePoint( position p)
    {        
        return p.x >= pos.x && p.x <= pos.x+figureWidth && // x-dimension
             p.y >= pos.y && p.y <= pos.y+figureHeight; // y-dimension
    }

    public void setAlive(boolean alive){ isAlive = alive; }
    public boolean isAlive() {return isAlive; }
    
    private position pos;
    private Color col;
    private boolean directionRight = false;
    private int figureWidth;
    private int figureHeight;
    private boolean isAlive = true;
}

class shot extends position
{
    public shot (int x, int y)
    {
        super(x, y);
    }

    public void setAlive ( boolean alive ){ isAlive = alive;}
    public boolean isAlive () { return isAlive; }

    private boolean isAlive = true;
}

class invaders extends renderer implements state
{
    public invaders ()
    {
        shots = new Vector<shot> ();

        enemys = new Vector<invader> ();
        enemys.add(new invader (2,0));
        enemys.add(new invader (6,0));
        enemys.add(new invader (10,0));
    }

    public void run ( Status s, Status ls)
    {
        int figureWidth = 3;
        int figureHeight = 3;

        // draw
        for (invader e: enemys) e.draw(s, ls);

        // update
        if ( updateCounter <= 0.0f )
        {
            List<Object> toRemove = new ArrayList<Object>();
            
            for (invader e: enemys)
            {
                if (e.isDirectionRight())
                    e.moveRight();
                else
                    e.moveLeft();

                if ( e.getX() == 0 )
                {
                    e.setDirectionRight(true);
                    e.moveDown();
                }
                else if  ( e.getX() == globals.width-figureWidth+1)
                {
                    e.setDirectionRight(false);
                    e.moveDown();
                }

                if ( e.getY() == globals.height-figureHeight )
                    dead = true;

                if ( !e.isAlive())
                    toRemove.add(e);
            }

            enemys.removeAll(toRemove);
            if ( enemys.isEmpty() ) win = true;

            updateCounter = updateSpeed;
        }

        // shoot
        if ( shotUpdateCounter <= 0.0f )
        {
            // do shoot
            for (int i = 0; i < globals.width; ++i)
                for (int j = 0; j < globals.height; ++j)
                {
                    if ( s.sens[i][j])
                        shots.add(new shot(i,globals.height-1));
                }

            shotUpdateCounter = shotUpdateSpeed;
        }

        // collision detection and notify
        for (shot p: shots )
            for (invader inv: enemys)
            {
                if ( inv.collidePoint(p) )
                {
                    p.setAlive(false);
                    inv.setAlive(false);
                }
            }

        if ( shotUpdateDelay <= 0.0f )
        {
            // update remove shots
            List<Object> toRemove = new ArrayList<Object>();
            for(shot p: shots)
            {
                p.y--;
                if ( p.y < 0 || !p.isAlive())
                    toRemove.add(p);
            }
            shots.removeAll(toRemove);
            
            shotUpdateDelay = shotUpdateDelaySpeed;
        }

        // draw shots
        for (position p: shots)
            drawPoint(s, ls, p.x, p.y, shotColor);

        updateCounter -= globals.update_speed;
        shotUpdateCounter -= globals.update_speed;
        shotUpdateDelay -= globals.update_speed;
    }

    public boolean isDead () { return dead; }
    public boolean isWin () { return win; }

    private Status current_state;
    private Status last_state;

    private Vector<invader> enemys;
    private float updateSpeed = 0.10f;
    private float updateCounter = updateSpeed;

    private boolean dead = false;
    private boolean win = false;

    private Vector<shot> shots;
    private Color shotColor = Color.cyan;

    private float shotUpdateSpeed = 1.2f;
    private float shotUpdateCounter = shotUpdateSpeed;

    private float shotUpdateDelaySpeed = 0.1f;
    private float shotUpdateDelay = shotUpdateDelaySpeed;
}

class circles extends renderer implements state
{
    public circles()
    {
        meta = new meta_information[globals.width][globals.width];
        fillColorMeta(meta, Color.black);
    }

    public void run (Status s, Status last_state)
    {
        current_status = s;
        last_status = last_state;

        // no user input
        for (int i=0; i<s.sens.length; i++)
            for (int j=0; j<s.sens[0].length; j++)
                s.sens[i][j] = false;

        // corner send waves
        s.sens[0][0] = true;
        s.sens[globals.width-1][globals.height-1] = true;

        for (int i=0; i<s.sens.length; i++)
        {
            for (int j=0; j<s.sens[0].length; j++)
            {
                if ( s.sens[i][j] )
                {
                    Color new_color = getFadedColor(meta[i][j].color.toColor(), 4, 4, 4);
					meta[i][j].color = new color(new_color);
                    
                    drawCircle(s,last_status, i, j, (int)meta[i][j].position, new_color);
                    meta[i][j].position += circle_speed;
					
					if ( meta[i][j].position >= 4 )
						drawCircle(s,last_status, i, j, (int)meta[i][j].position-4, getFadedColor(new_color,6,6,6));
					
                    if ( meta[i][j].position > circle_max_radius )
                    {
                        meta[i][j].position = 0.0f;
                        meta[i][j].color = getRandomcolor();
                    }
                }
            }
        }
    }

    Status current_status;
    Status last_status;

    private meta_information[][] meta; // stores meta information

    float circle_speed = 0.4f;
    float circle_max_radius = 10.0f;

    float radius = 0.0f;
}

/*
*	 not implementet correctly: maybe remove
*/
class fadingLine extends renderer
{
    public fadingLine (position from, position to, float interval, Color fromColor, Color toColor)
    {
            this.fromColor = fromColor;
            this.toColor = toColor;
            this.from = from;
            this.to = to;
            this.generatingInterval = interval;
            this.generatingCounter = this.generatingInterval;
            this.index = 0;
    }

    public void run (Status s, Status ls )
    {
		if ( generatingCounter <= 0.0f )
        {
			generatingCounter = generatingInterval;
        }

        generatingCounter -= globals.update_speed;
    }
	
    private Color fromColor;
    private Color toColor;
    private position from;
    private position to;
    private int index;
    private float generatingInterval;
    private float generatingCounter;
}

class rocket extends renderer
{
    public rocket (position startPosition)
    {
        this.startPosition = startPosition;
        this.maxHeight = random.nextInt(globals.height-5)+5;
        this.color = getRandomColor();
		
        this.forms = new HashMap<Integer, int[]>();
		this.formWidth = 7;
		this.formHeight = 7;

        int[] A = {
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,1,1,1,0,0,
            0,0,1,0,1,0,0,
            0,0,1,1,1,0,0,
			0,0,0,0,0,0,0,
			0,0,0,0,0,0,0
        };
        forms.put(0, A);
		
		int[] A1 = {
            0,0,0,0,0,0,0,
            0,1,0,1,0,1,0,
            0,0,1,0,1,0,0,
            0,1,0,0,0,1,0,
            0,0,1,0,1,0,0,
			0,1,0,1,0,1,0,
			0,0,0,0,0,0,0
        };
        forms.put(1, A1);
		
		int[] A2 = {
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
			0,0,0,0,0,0,0,
			0,0,0,0,0,0,0
        };
        forms.put(2, A2);
		
		int[] A3 = {
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            1,0,0,0,0,0,1,
            0,0,0,0,0,0,0,
            1,0,0,0,0,0,1,
			0,0,0,0,0,0,0,
			0,0,1,0,1,0,0
        };
        forms.put(3, A3);
		
		int[] A4 = {
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,
            1,0,0,0,0,0,1,
			1,0,1,0,1,0,1,
			1,0,1,0,1,0,1
        };
        forms.put(4, A4);
         
    }

    public void update ()
    {
        if ( isExploded() )
        {
            liveTime -= globals.update_speed;
            //explosionRadius += explosionSpeed;
        }
        else
            rocketHeight += rocketSpeed;
    }

    public void draw ( Status s, Status ls )
    {
        int x = startPosition.x;
        int y = startPosition.y-(int)rocketHeight;

        if( isAlive() )
        {
            if ( isExploded () )
            {	
				int[] c = forms.get(animationState%forms.size());
				if ( animationCounter <= 0.0f )
				{
					animationState++;
					animationCounter = animationInterval;
				}
				
				for (int i = 0; i < formHeight; ++i)
					for (int j = 0; j < formWidth; ++j)
					{	
						Color tc = getFadedColor(color,
						random.nextInt(50),
						random.nextInt(50),
						random.nextInt(50));
						
						if ( c[i*formWidth+j] == 1 )
							drawPoint(s, ls,x+j-formWidth/2,y+i-formHeight/2, tc);
					}
					
				animationCounter -= globals.update_speed;
            }

			// rocket is flying
            if ( !isExploded() )
                drawPoint(s, ls, x, y, color);
        }
    }

    public boolean isExploded(){ return rocketHeight >= maxHeight;}
    public boolean isAlive () { return liveTime > 0.0f;}
    
    private position startPosition;
    private int maxHeight;
    private float rocketHeight;
    private float rocketSpeed = 0.5f;
    private Color color;
    private float liveTime = 0.6f;
    private float explosionRadius = 8;
    private Map<Integer,int[]> forms;
	private int formWidth;
	private int formHeight;
	
	private int animationState;
	private float animationInterval = 0.1f;
	private float animationCounter = animationInterval;
}

class firework extends renderer implements state
{
    public firework()
    {
        rockets = new Vector<rocket> ();
    }
	
	public Vector<position> getAllSens(Status s)
	{
		Vector<position> ret = new Vector<position> ();
		
		for (int i = 0; i < globals.height; ++i)
			for (int j = 0; j < globals.width; ++j)
				if ( s.sens[j][i] )
					ret.add ( new position (j,i) );					
					
		return ret;
	}

    public void run (Status s, Status ls)
    {
        current_status = s;
        last_status = ls;

        for (int i = 0; i < globals.width; ++i)
            for (int j = 0; j < globals.height; ++j)
            {
                s.tiles[i][j] = getFadedColor(ls.tiles[i][j],15,15,15);
            }
			
		Vector<position> sens = getAllSens (s);

        // launch some rockets
        if ( launchCounter <= 0.0f )
        {
           // rockets.add(new rocket(new position(random.nextInt(globals.width-1),globals.height-1)));
		   position p = sens.get(random.nextInt(sens.size()));
		   p.y = globals.height-1;
		   
		   rockets.add (new rocket (p));
		   
           launchCounter = launchInterval;
        }

        // update & delete rockets if neccessary
        Vector<Object> toDelete = new Vector<Object>();
        for (rocket r: rockets)
        {
            r.update();
            if ( !r.isAlive() )
                toDelete.add(r);
        }
        rockets.removeAll(toDelete);

        // draw rockets
        for (rocket r: rockets)
            r.draw(s,ls);

        launchCounter -= globals.update_speed;
    }
    
    private Status current_status;
    private Status last_status;

    private Vector<rocket> rockets;
    private float launchInterval = 1.0f;
    private float launchCounter = launchInterval;
}

public class cr_effect implements Effect
{
    Status last_state;
    Status current_status;

    state current_state;
    loading loading;
    state circles;
    state marker;
    connections connections;
    texting text;
    state equalizer;
    stream stream;
    marker2 marker2;
    strobo strobo;
    invaders invaders;
    firework firework;

    float running_time = 0.0f;
	
    private final int max_state = 24;
    private int state = 0;
    private boolean on_enter_state = true;
    private float state_counter = 0.0f;
	
	private boolean win_switch = false;
	private float win_switch_time = 0.2f;
	private float win_switch_counter = win_switch_time;

    // general purpose timer for an effect
    private float effect_timer;

    private colorFader color_fader;

    public void start()
    {
        loading = new loading();
        circles = new circles();
        marker = new marker();
        connections = new connections();
        text = new texting();
        equalizer = new equalizer();
        stream = new stream(1);
        marker2 = new marker2();
        strobo = new strobo();
        invaders = new invaders();
        firework = new firework();

        current_state = stream;
        
        System.out.println("demo effect started");
    }
    public void stop() { System.out.println("demo effect stopped"); }

    public int count_people ( Status s)
    {
        int ret = 0;
        for (int i=0; i < s.sens.length; i++)
        {
            for (int j=0; j<s.sens[0].length; j++)
            {
                if ( s.sens[i][j] ) ret++;
            }
        }

        return ret;
    }

    /*
    * handels everything for the next state
    */
    public void next_state ( boolean condition)
    {
        next_state (condition, -1);
    }

    public void next_state ( boolean condition, int next )
    {
        if ( condition )
        {
            if ( next == -1)
                state++;
            else
                state = next;

            if ( state > max_state ) state = 0;
            on_enter_state = true;
        }
    }

    /*
    * call this to check if we enter a state
    */
    public boolean on_enter ()
    {
        if ( on_enter_state )
        {
            on_enter_state = false;
            return true;
        }
        else
            return false;
    }
	
    public void update (Status s)
    {
        running_time += globals.update_speed;
        
        current_status = s;
        if ( last_state == null )
        {
            for (int i=0; i < s.sens.length; i++)
            {
                for (int j=0; j<s.sens[0].length; j++)
                {
                    s.tiles[i][j] = Color.black;
                    s.sens[i][j] = false;
                }
            }
            last_state = s;
        }

        // set all to black
        for (int i=0; i<s.sens.length; i++)
        {
            for (int j=0; j<s.sens[0].length; j++)
            s.tiles[i][j] = Color.black;
        }
        
        int count = count_people (s);
		
		count++; // remove in real!
		
        // do ya rly wanna see my statemachine?
        switch ( state )
        {
            case 0: // loading
            {
                if ( on_enter () ){
                }

                loading.run(s, last_state);

                next_state ( loading.isfull ()
                        );
            }break;

            case 1: // ETH
            {
                if ( on_enter () ){
                    state_counter = 3.0f;
                    
                    text.setBackgroundColor(Color.white);
                    text.setFontColor(new Color(40,230,173));
                }

                text.run ( s, last_state );
                text.setBackgroundColor(text.getFadedColor(text.getBackgroundColor(), 3, 3, 3));
                text.setFontColor (text.getFadedColor (text.getFontColor(),5,5,5));
                
                text.floatText(true,true,false,false, "ETH", 2,2 );

                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 2: // BIRD
            {
                if ( on_enter () ){
                    state_counter = 3.0f;

                    text.setBackgroundColor(Color.white);
                    text.setFontColor(new Color(40,230,173));
                }
                text.run ( s, last_state );
                text.setBackgroundColor(text.getFadedColor(text.getBackgroundColor(), 3, 3, 3));
                text.setFontColor (text.getFadedColor (text.getFontColor(),5,5,5));

                text.floatText(true, true, false, false, "BIRD", 0, 2);

                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 3: // strobo
            {
                if ( on_enter ()){
                    strobo.reset();
                    state_counter = 4.0f;
                }

                strobo.run(s, last_state);

                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 4: // fade
            {
                if ( on_enter ()){
                    state_counter = 2.0f;
                    color_fader = new colorFader(Color.white, Color.black ,35);
                }

                text.fillColor(s, color_fader.getNextColor());
				
                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 5: // on the floor
            {
                if ( on_enter ())
                {
                    state_counter = 2.0f;
                    text.resetFinished();
                    text.setBackgroundColor(Color.BLACK);
                    text.setFontColor(new Color(236,210,64));
                    text.set_x_offset(globals.width);
                }

                text.run(s, last_state);

				// implement fadingLine for that!
				//text.drawLine (s, last_state, 0,0,globals.width-1,0, text.getRandomColor());
				//text.drawLine (s, last_state, 0,globals.height-1,globals.width-1,globals.height-1, text.getRandomColor());
				
                text.floatText(true, true, true, false, "GET ON THE FLOOR!", 0, 2);

                next_state (
                        text.finished(),
                        count >= 2 ? 7:6
                        );
            }break;

            case 6: // waiting
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(text.getRandomColor());
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "1 MORE!", 0, 2);

                next_state (
                        text.finished () && (count >= 2 || count == 0),
                        count==1?6:count == 0?5:7
                        );

            }break;

            case 7: // feel the music
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0x37DB79));
                    text.set_x_offset(globals.width);
                }

                text.run(s, last_state);
                text.floatText(true, true, true, false, "FEEL THE MUSIC!", 2, 2);

                next_state (
                        text.finished()
                        );
            }break;

            case 8: // circles
            {
                if ( on_enter ())
                {
                    state_counter = 20.0f;
                }

                circles.run(s, last_state);

                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 9: // listen
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0xCCF600));
                    text.set_x_offset(globals.width);
                }
				
				text.run(s, last_state);
                text.floatText(true, true, true, false, "LISTEN!", 0, 2);

                next_state (
                        text.finished ()
                        );
            }break;

            case 10: // EQ
            {
                if ( on_enter ())
                {
                    state_counter = 15.0f;
                }
				
                equalizer.run(s, last_state);

                next_state (
                        state_counter <= 0.0f
                        );
            }break;
			
            case 11: // dance
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0x64AAD0));
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "Dance!", 0, 2);

                next_state (
                        text.finished () && (count >= 2)
                        );
            }break;

            case 12: // focus rects
            {
                if (on_enter())
                {
                    state_counter = 1.5f;
					marker2.reset ();
                }

                marker2.run(s, last_state);

                next_state(
                        state_counter <= 0.0f
                        );
            }break;

            case 13: // marker
            {
                if ( on_enter () )
                {
                    state_counter = 10.0f;
                }

                marker.run(s, last_state);

                next_state (
                        state_counter <= 0.0f
                        );
            }break;

            case 14: //connect text
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0xFF9340));
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "Connect!", 0, 2);

                next_state (
                        text.finished () && (count >= 2)
                        );
            }break;

            case 15: //connect
            {
                if ( on_enter () )
                {
                    state_counter = 15.0f;
					connections = new connections ();
                }

                connections.run(s, last_state);

                next_state (
                        state_counter <= 0.0f/*,
                        count == 2?16:7*/         // if < 2 then loop back
                        );
            }break;

            case 16: // water
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0x3E97D1));
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "ENJOY.Water!", 0, 2);

                next_state (
                    text.finished ()
                    );

            }break;

            case 17: // water
            {
                if (on_enter ())
                {
                    state_counter = 20.0f;
                    stream = new stream (0,0.05f);
                }
                
                stream.run(s, last_state);

                next_state(
                        state_counter <= 0.0f
                        );
            }break;

            case 18: //fire
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0xFF4940));
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "ENJOY.Fire!", 0, 2);

                next_state (
                    text.finished ()
                    );
            }break;

            case 19: //fire
            {
                if (on_enter ())
                {
                    state_counter = 20.0f;
                    stream = new stream (1,0.05f);
                }

                stream.run(s, last_state);

                next_state(
                        state_counter <= 0.0f/*,
						(count == 0 || count == 1)?20:7*/
                        );
            }break;

            case 20: // save the world
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(new Color(0xB9F73E));
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "Save the world!", 0, 2);

                next_state (
                    text.finished ()
                    );
            }break;

            case 21: // invaders!
            {
                if ( on_enter () )
                {}

                invaders.run(s, last_state);

                next_state(
                        invaders.isDead() || invaders.isWin(),
                        invaders.isWin()?22:23
                        );
            }break;

            case 22: // win!
            {
                if ( on_enter ())
                {
                    state_counter = 2.0f;
                    text.setFontColor(new Color(0xFFDD40));
                }

                text.run (s,last_state);
				if(win_switch)
				{
					text.floatText(true, true, false, false, "Win!", 1, 2);
				}
				if ( win_switch_counter <= 0.0f )
				{
					win_switch = !win_switch;
					win_switch_counter = win_switch_time;
				}
				win_switch_counter -= globals.update_speed;

                next_state (
                        state_counter <= 0.0f,
                        24                       // goto firework
                    );
            }break;

            case 23: // lost
            {
                if ( on_enter ())
                {
                    text.resetFinished();
                    text.setFontColor(text.getRandomColor());
                    text.set_x_offset(globals.width);
                }

                text.run (s,last_state);
                text.floatText(true, true, true, false, "The world is gone...", 2, 2);

                next_state (
                    text.finished (),
					7
                    );

            }break;

            case 24: // firework
            {
                if ( on_enter () )
                {
					state_counter = 10.0f;
                }

                firework.run(s, last_state);

                next_state (
					state_counter <= 0.0f,
					7
					);

            }break;



            //...
        }

        state_counter -= globals.update_speed;
        effect_timer -= globals.update_speed;
		
        last_state = s;
    }
	
}

