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);
}
}
}