import java.awt.*;
import java.applet.*;

public class Monster {

   int state;           /*what is it doing?*/
   int index;           /*index to animation frame*/
   Picture[] cels;              /*pointer to array of animation frames*/
   int x, y;            /*location*/
   int target_x;                /*target x position*/
   int target_y;                /*target y position*/
   int target_c;                /*target computer*/
   int cargo;           /*which OS carried*/
   int x_offset;                /*accounts for width differences*/
   int y_offset;                /*'bounce' factor for OS carried*/
   int sx, sy;          /*used for drawing extra OS during switch*/

   static final int SLOW = 0;   /* speeds of moving bills */
   static final int FAST = 1;

   static final int OFF = 0;    /* Bill's states */
   static final int IN = 1;
   static final int AT = 2;
   static final int OUT = 3;
   static final int DYING = 4;
   static final int STRAY = 5;

   static final int GRAVITY = 3;        /*speed at which os drops*/

   static final int XOFFSET = 20;       /*offset from right of computer*/
   static final int YOFFSET = 3;        /*offset from top of computer*/

   JBill2 game;
   public Monster(JBill2 a) {
      game=a;
   }

   void get_border() {
      int i=game.RAND(0,3);
      if (i%2==0)
        target_x=game.RAND(0, game.scrwidth-game.bill.width);
      else
        target_y=game.RAND(0, game.scrheight-game.bill.height);
      switch (i) {
       case 0: target_y=-game.bill.height-16; break;
       case 1: target_x=game.scrwidth+1; break;
       case 2: target_y=game.scrheight+1; break;
       case 3: target_x=-game.bill.width-2; break;
      }
   }

   /*  Adds a bill to the in state*/
   void enter(){
      state = IN;
      get_border();
      x = target_x;
      y=target_y;
      index = 0;
      cels = game.bill.lcels;
      cargo = game.OS.WINGDOWS;
      x_offset = -2;
      y_offset = -15;
      target_c = game.RAND(0, game.net.units-1);
      target_x = game.net.computers[target_c].x+game.net.width-XOFFSET;
      target_y = game.net.computers[target_c].y+YOFFSET;
      game.bill.on_screen++;
      game.bill.off_screen--;
   }

   /*  Moves bill toward his target - returns whether or not he moved */
   int move (int mode) {
      int xdist = target_x - x;
      int ydist = target_y - y;
      int step = step_size(game.level);
      int dx, dy;
      int signx = xdist >= 0 ? 1 : -1;
      int signy = ydist >= 0 ? 1 : -1;
      xdist = Math.abs(xdist);
      ydist = Math.abs(ydist);
      if (xdist==0 && ydist==0)
        return 0;
      else if (xdist<step && ydist<step) {
         x = target_x;
         y = target_y;
      }
      else {
         dx = (xdist*step*signx)/(xdist+ydist);
         dy = (ydist*step*signy)/(xdist+ydist);
         switch(mode) {
          case SLOW: break;
          case FAST: dx = 5*dx/4; dy = 5*dy/4; break;
         }
         x+=dx;
         y+=dy;
         if (dx<0)
           cels = game.bill.lcels;
         else if (dx>0)
           cels = game.bill.rcels;
      }
      return 1;
   }

   void draw() {
      switch (state) {
       case IN:
       case OUT:
       case DYING: draw_std(); break;
       case AT: draw_at(); break;
       case STRAY: draw_stray(); break;
       default: break;
      }
   }

   /*  Update Bill's position */
   void update_in() {
      int moved = move(SLOW);
      if (moved==0 && (game.net.computers[target_c].os != game.OS.WINGDOWS)
          && !(game.net.computers[target_c].busy))
        {
           game.net.computers[target_c].busy=true;
           cels = game.bill.acels;
           index=0;
           state = AT;
           return;
        }
      else if (moved==0) {
         int i;
         do {
            i=game.RAND(0, game.net.units-1);
         } while (i == target_c);
         target_c = i;
         target_x = game.net.computers[target_c].x + game.net.width-XOFFSET;
         target_y = game.net.computers[target_c].y + YOFFSET;
      }
      index++;
      index%=game.bill.WCELS;
      y_offset+=(8*(index%2)-4);
   }

   /*  Update Bill standing at a computer  */
   void update_at() {
      int sys;
      if (index==0 && game.net.computers[target_c].os == game.OS.OFF) {
         index=6;
         sys = game.net.computers[target_c].find_stray();
         if (sys<0) cargo = -1;
         else {
            cargo = game.bill.list[sys].cargo;
            game.bill.list[sys].state = OFF;
         }
      }
      else index++;
      if (index == 13) {
         y_offset = -15;
         x_offset = -2;
         get_border();
         index = 0;
         cels = game.bill.lcels;
         state = OUT;
         game.net.computers[target_c].busy=false;
         return;
      }
      y_offset = game.bill.height - game.OS.height;
      switch (index) {
       case 1 :
       case 2 : x -= 8; x_offset +=8; break;
       case 3 : x -= 10; x_offset +=10; break;
       case 4 : x += 3; x_offset -=3; break;
       case 5 : x += 2; x_offset -=2; break;
       case 6 :
         if (game.net.computers[target_c].os != game.OS.OFF) {
            game.net.base--; game.net.off++;
            cargo = game.net.computers[target_c].os;
         }
         else {
            x-=21; x_offset+=21;
         }
         game.net.computers[target_c].os = game.OS.OFF;
         y_offset = -15;
         x += 20;
         x_offset -=20;
         break;
       case 7 : sy = y_offset; sx = -2; break;
       case 8 : sy = -15; sx = -2; break;
       case 9 : sy = -7; sx = -7; x -= 8; x_offset +=8; break;
       case 10 : sy = 0; sx = -7; x -= 15; x_offset +=15; break;
       case 11 : sy = 0; sx = -7;
         game.net.computers[target_c].os = game.OS.WINGDOWS;
         game.net.off--; game.net.win++;
         break;
       case 12 : x += 11; x_offset -=11;
      }
   }

   /* Updates Bill fleeing with his ill gotten gain */
   void update_out() {
      if (game.INTERSECT(x, y, game.bill.width, game.bill.height, 0, 0, game.scrwidth,
                         game.scrheight))
        {
           move(FAST);
           index++;
           index%=game.bill.WCELS;
           y_offset+=(8*(index%2)-4);
        }
      else {
         state = OFF;
         game.bill.on_screen--; game.bill.off_screen++;
      }
   }


   /* Updates Bill who is dying */
   void update_dying() {
      if (index < game.bill.DCELS-1){
         y_offset += (index*GRAVITY);
         index++;
      }
      else {
         y+=y_offset;
         if (cargo<0 || cargo == game.OS.WINGDOWS) state = OFF;
         else state = STRAY;
         game.bill.on_screen--;
      }
   }

   void update() {
      switch (state) {
       case IN: update_in(); break;
       case AT: update_at(); break;
       case OUT: update_out(); break;
       case DYING: update_dying(); break;
       default: break;
      }
   }

   boolean clicked(int locx, int locy) {
      return (locx>x && locx<x+game.bill.width && locy>y && locy<y+game.bill.height);
   }

   boolean clickedstray(int locx, int locy) {
      return (locx>x && locx<x+game.OS.width && locy>y && locy<y+game.OS.height);
   }

   int step_size(int lev) {
      return game.MIN(14+lev, 18);
   }

   void draw_std() {
      if (cargo>=0)
        game.ui.draw(game.OS.os[cargo], x + x_offset, y + y_offset);
      try {
        game.ui.draw(cels[index], x, y);
      }
      catch (Exception e) {
        System.out.println("Caught Exception in Draw_Std with index: "+index+" Exception:"+e);
      }
   }

   void draw_at() {
      if (index>6 && index<12)
        game.ui.draw(game.OS.os[0], x + sx, y + sy);
      if (cargo>=0)
        game.ui.draw(game.OS.os[cargo], x + x_offset, y + y_offset);
      game.ui.draw(cels[index], game.net.computers[target_c].x,
              game.net.computers[target_c].y);
   }

   void draw_stray() {
      if ((game.grabbed < 0 || x != game.bill.list[game.grabbed].x))
        game.ui.draw(game.OS.os[cargo], x, y);
   }
}
