import java.awt.*;
import java.util.*;

public class Tamara2 extends BufferedApplet
{
   public boolean mouseMove(Event e, int x, int y) {
      idle = 0;                  // NOT IDLE ANYMORE
      wakeUp = 1;                // GOTTA WAKE UP NOW
      mx = x;                    // REMEMBER MOUSE POSITION
      my = y;
      return true;
   }
   public boolean mouseUp(Event e, int x, int y) {
      click = 1;                 // DO CLICK RESPONSE
      effectType = rnd(0, 2);    // OF ONE TYPE OR ANOTHER
      return mouseMove(e, x, y);
   }

   int floor(int x) { return 20; }
   int ifade(int a, int b, double dur) { return (int)(a + (b-a) / dur); }
   int ilerp(double t, double a, double b) { return (int)(a + t*(b-a)); }
   int rnd(int lo, int hi) { return lo + (Math.abs(random.nextInt()) % (hi+1 - lo)); }

   Random random = new Random();
   int mx = 0, my = 0, effectType = 0, w=0, h=0, X=50, Y, W=50, H=50, R, U, V, rR, rG, rB;
   double click = 0, idle = 1, wakeUp = 0, awake = 1, time = 0, s, c, B, T, Z;

   int hR, hG, hB, HR, HG, HB;

   double clock;

/////////////////////// MAIN RENDER LOOP //////////////////////

   public void render(Graphics g)
   {

// INITIALIZE THE FIRST TIME

      if (w == 0) {
         w = bounds().width;             // SCREEN RESOLUTION
         h = bounds().height;
         X = w/2;                        // MY INITIAL POSITION
         Y = 20;
         mx = w/2;                       // DEFAULT MOUSE POSITION
         my = h;
	 wanderX = w/2;
      }

// MOVE EVERYTHING

      X = ilerp(.05, X, mx);      	 // MY DEFAULT X,Y POSITION
      Y = floor(X);
      W = 50;                            // MY DEFAULT WIDTH AND HEIGHT
      H = 50;
      s = .5 + .5 * Math.sin(time);      // TIME-VARYING "BEAT"
      c = .5 + .5 * Math.sin(time+.9);

      B = awake * Math.min(1, 3*(1-Math.abs(2*click-1)));     // A CLICK MAKES THE HEART BEAT
      T = awake * (1-B) * my / h;                             // HOW MUCH AM I HOPPING FAST?
      Z = awake * (1-B) - T;                                  // HOW MUCH AM I HOPPING SLOWLY?

      wander(awake * awake * idle);                           // WANDER AROUND WHILE WIDE AWAKE AND IDLE

      double elapsed = .030*(new Date()).getTime() - clock;   // TIME ADVANCES, WEIGHTED BY
      clock += elapsed;                                       // WEIGHTED SUM OF ALL MOVEMENTS
      time  += elapsed * (B*beat(B) + T*hop(T) + Z*slowHop(Z) + (1-awake)*sleep(1-awake));

      awake  = Math.max(0, Math.min(1,awake-.002+.1*wakeUp)); // HOW AWAKE AM I?
      click  = Math.max(0, click - .01);                      // EFFECT OF CLICKING DECAYS OVER TIME
      idle   = Math.min(1, idle + .01);                       // KEEP GETTING MORE IDLE UNTIL MOUSE MOVES
      wakeUp = Math.max(0, wakeUp - .1);                      // THE WAKEUP ALARM DECAYS QUICKLY

      if (B > 0) {	                                      // IF BEATING IN RESPONSE TO A CLICK,
         R = (int)(6*w*(1-click)*(1-click));                  // COMPUTE PARAMETERS FOR EFFECTS ANIMATION
         U = X-R/2;
	 V = h-Y-3*H/5-R/2;
      }

      if (awake < .1)                                         // IF SLEEPING, ANIMATE THE 'Z'S
         for (int i = 0 ; i < 10 ; i++)
            moveZ(i);

// DRAW EVERYTHING

      drawBackground(g);

      hR = 255; hG =   0; hB =   0; // HEART COLOR IS RED, BEFORE EFFECTS ARE APPLIED.
      HR = 255; HG = 255; HB = 255; // HEART HILITE IS WHITE, BEFORE EFFECTS ARE APPLIED.

      if (B > 0) {
         if (click > .9) {
            rG = rnd(0, 255);
            rB = rnd(0, 255);
            rR = Math.max(0,Math.min(255,255-rG-rB));
         }
         switch (effectType) {
         case 0: drawColorburst(g, R, U, V); break;
         case 1: drawBubble    (g, R, U, V); break;
         case 2: drawPinwheel  (g, R, U, V); break;
         }
      }

      if (awake < .1)
         for (int i = 0 ; i < 10 ; i++)
            if (zy[i] > Y+H)
               drawZ(g, zx[i], h - zy[i]);

      drawHeart(g, X-W/2, h-Y-H, W, H);
      drawFloor(g);
   }

/////////////////////// BEHAVIOR ROUTINES ////////////////////

   int[] zx = new int[10], zy = new int[10];
   void moveZ(int i) {
      zx[i] = awake > 0 ? X : zy[i] > 0 ? zx[i] + rnd(-1,1) : zx[i];
      zy[i] = awake > 0 ? -2*rnd(0,h) : zy[i] + 1;
      if (zy[i] > h) {
         zx[i] = X;
         zy[i] = Y+H;
      }
   }
   double wanderX;
   void wander(double t) {
      if (t > 0) {
         if ((int)time % 30 == 0)
            wanderX = Math.max(0,Math.min(w, wanderX + awake*rnd(-w/4,w/4)));
         X = ilerp(t, X, ilerp(.05, X, wanderX));
      }
   }
   double beat(double t) {
      if (t > 0) {
         W = (int)(W + t * 30 * s);
         H = (int)(H + t * 30 * s);
         Y = ilerp(t, Y, h/2);
      }
      return .4;
   }
   double sleep(double t) {
      if (t > 0) {
         H = (int)(H - t * 10 * (c - .2));
         W = (int)(W + t * 10 * (c - .5));
      }
      return .1;
   }
   double hop(double t) {
      if (t > 0) {
         Y = (int)(Y + t * 20 * (1 - c * c));
         H = (int)(H - t * 10 * (s - .5));
         W = (int)(W + t *  3 * (s - .5));
      }
      return .5;
   }
   double slowHop(double t) {
      if (t > 0) {
         Y = (int)(Y + t * (h-H) * (1 - c * c));
         H = (int)(H - t * 25 * (s - .5));
         W = (int)(W + t * 10 * (s - .5));
      }
      return .2;
   }

/////////////////////// BASIC DRAWING ROUTINES ////////////////////

   void drawBackground(Graphics g) {
      g.setColor(Color.black);
      g.fillRect(0,0,w,h);
   }

   void drawFloor(Graphics g) {
      g.setColor(Color.blue);
      g.fillRect(0, h-floor(X), w, floor(X));
   }

   void drawHeart(Graphics g, int x, int y, int w, int h) {
      int a = 3*w/5, b = 3*h/5, c = w/20, d = h/2-h/32;

      Color color  = new Color(hR, hG, hB);
      Color hilite = new Color(HR, HG, HB);

      g.setColor(color);
      g.fillOval(x, y, a, b);
      g.fillOval(x+w-a, y, a, b);
      int[] X = {x+c+1,x+w-1-c,x+w/2}, Y = {y+d,y+d,y+h};
      g.fillPolygon(X,Y,3);

      g.setColor(hilite);
      g.fillOval(x+a/4, y+b/4, a/2, b/2);
      g.setColor(color);
      g.fillOval(x+5*a/16, y+5*b/16, a, b);
   }

   void drawZ(Graphics g, int x, int y) {
      g.setColor(Color.gray);
      g.drawLine(x+4,y+4,x-4,y+4);
      g.drawLine(x-4,y+4,x+4,y-4);
      g.drawLine(x+4,y-4,x-4,y-4);
   }

/////////////////////// EFFECTS DRAWING ROUTINES ////////////////////

   void drawColorburst(Graphics g, int R, int U, int V) {
      g.setColor(new Color(rR/4,rG/4,rB/4));
      g.fillOval(U-10, V-10, R+20, R+20);
      g.setColor(new Color(2*rR/3,2*rG/3,2*rB/3));
      g.fillOval(U-5, V-5, R+10, R+10);
      g.setColor(new Color((int)(rR*B),(int)(rG*B),(int)(rB*B)));
      g.fillOval(U, V, R, R);
   }   

   void drawBubble(Graphics g, int R, int U, int V) {
      int X = U+R/2;
      int Y = V+R/2;
      int RR = (int)(B*B*rR), GG = (int)(B*B*rG), BB = (int)(B*B*rB);
      for (int i = 10 ; i >= 1 ; i--) {
	 int S = R/2 * (20+i) / 30;
         g.setColor(new Color(RR,GG,BB));
         g.fillOval(X-S,Y-S,S+S,S+S);
	 RR = RR * 9 / 10;
	 GG = GG * 9 / 10;
	 BB = BB * 9 / 10;
      }
      double b = .3*B*B;
      hR = ilerp(b, hR, rR);
      hG = ilerp(b, hG, rG);
      hB = ilerp(b, hB, rB);
      HR = ilerp(b, HR, rR);
      HG = ilerp(b, HG, rG);
      HB = ilerp(b, HB, rB);
   }

   int direction;
   void drawPinwheel(Graphics g, int R, int U, int V) {
      if (click > .9)
	 direction = 2*rnd(0,1)-1;
      int n = 20;
      g.setColor(new Color((int)(rR*B*B),(int)(rG*B*B),(int)(rB*B*B)));
      R = 3*w;
      U = X-R/2;
      V = h-Y-3*H/5-R/2;
      int x[] = new int[n], y[] = new int[n];
      for (int i = 0 ; i < n ; i++) {
         double s = .5 + .5 * Math.sin((2.*i/n + 3*direction*click) * Math.PI);
         double c = .5 + .5 * Math.cos((2.*i/n + 3*direction*click) * Math.PI);
	 x[i] = ilerp(s, U, U+R);
	 y[i] = ilerp(c, V, V+R);
      }
      for (int i = 0 ; i < n ; i++) {
	 int X[] = {U+R/2,x[i],(x[i]+x[(i+1)%n])/2};
	 int Y[] = {V+R/2,y[i],(y[i]+y[(i+1)%n])/2};
	 g.fillPolygon(X,Y,3);
      }
   }
}