#include <allegro.h>
#include "xlines.h"
#include "globals.h"
#include "water.h"
#include "map.h"
#include "data.h"

#define PADDIST 2000

Ship::Ship()
{
  image=NULL;
  turn_rate=0;
  power_rate=0;
  crossing=0;

  key_left=0;
  key_right=0;
  key_go=0;

  input=0;
  voice=-1;
  color=-1;
  type=-1;
}

void Ship::Init(fixed startx,fixed starty)
{
  x=startx;
  y=starty;
  head_dir=0;
  move_dir=0;
  move_spd=0;

  jumping=0;
  jump_length=0;
  landed=0;
  speeding=0;
  speeding_dir=0;

  waypoint=0;
  lastcol=NULL;

  last_render_x=startx;
  last_render_y=starty;
}

void Ship::Left()
{
  if (jumping) return;
  head_dir-=turn_rate;
  head_dir+=itofix(256);
  head_dir&=(itofix(256)-1);
}

void Ship::Right()
{
  if (jumping) return;
  head_dir+=turn_rate;
  head_dir&=(itofix(256)-1);
}

void Ship::Go()
{
  if (jumping) return;
  ApplyForce(head_dir,power_rate);
}

void Ship::Jump()
{
  if (jumping) return;
  //jump_length=fixtoi(fmul(itofix(16),move_spd));
  jump_length=256;
  if (jump_length==0) jump_length=1;
  jumping=jump_length;

  // Bring in a little sound
  if (!nosound) {
    if (rand()&1)
     play_sample((SAMPLE*)(data[dat_jumppad].dat),255,128,1000,0);
    else
     play_sample((SAMPLE*)(data[dat_jumppad2].dat),255,128,1000,0);
    switch (rand()&3) {
      case 0:
        play_sample((SAMPLE*)(data[dat_yahoo].dat),255,128,1000,0);
        break;
      case 1:
        play_sample((SAMPLE*)(data[dat_scream].dat),255,128,1000,0);
        break;
    }
  }
}

void Ship::Control()
{
  switch (input) {
    case -1:
      poll_mouse();
      if (mouse_x<(SCREEN_W/2)) {
        Left();
        position_mouse(SCREEN_W/2,SCREEN_H/2);
      }
      else if (mouse_x>(SCREEN_W/2)) {
        Right();
        position_mouse(SCREEN_W/2,SCREEN_H/2);
      }
      if (mouse_b&1) Go();
      break;
    case 0:
      poll_keyboard();
      if (key[key_left]) Left();
      if (key[key_right]) Right();
      if (key[key_go]) Go();
      break;
    case 1:
    case 2:
    case 3:
    case 4:
      poll_joystick();
      if (joy[input-1].stick[0].axis[0].d1) Left();
      if (joy[input-1].stick[0].axis[0].d2) Right();
      if (joy[input-1].button[0].b) Go();
      break;
  }
}

void Ship::AI()
{
  fixed wx,wy;
  fixed wx0,wy0;
  fixed wx1,wy1;
  fixed dir;
  fixed dist;
  int delta_dir;
  fixed turns;
  int go=0;

  // Head to the next waypoint
  wx0=map->waypoints[waypoint].x0;
  wy0=map->waypoints[waypoint].y0;
  wx1=map->waypoints[waypoint].x1;
  wy1=map->waypoints[waypoint].y1;
  wx=(wx0+wx1)/2;
  wy=(wy0+wy1)/2;
  dist=fsqrt(
    fmul((wx>>16)-(x>>16),(wx>>16)-(x>>16))+
    fmul((wy>>16)-(y>>16),(wy>>16)-(y>>16))
  )<<16;
  dir=fatan2(wy-y,wx-x)+itofix(64);
  if (ass_level>=1)
   delta_dir=(dir-move_dir)&(itofix(256)-1);
  else
   delta_dir=(dir-head_dir)&(itofix(256)-1);
  if (delta_dir>turn_rate && delta_dir<itofix(128)) Right();
  if (delta_dir<itofix(256)-turn_rate && delta_dir>=itofix(128)) Left();

  // Try to compensate inertia
  delta_dir=(dir-head_dir)&(itofix(256)-1);

  // Calculate a stupid number that proves to be OK via testing
  if (move_spd<power_rate*8) {
    go=1;
  }
  else {
    turns=fdiv(dist,move_spd);
    if (turns>itofix(4)) go=1;
  }
  if (go) Go();
}

void Ship::ApplyForce(fixed dir,fixed spd)
{
  x+=fmul(fsin(dir),spd);
  y-=fmul(fcos(dir),spd);
}

int Ship::Think(int ai)
{
  fixed px,py;
  fixed intx,inty;
  int n;
  int dx,dy;
  fixed prev_head_dir=head_dir;
  int sprw,sprh;
  int sz;

  sz=MIN(image->w,image->h);

  px=x;
  py=y;
  if (ai) AI(); else Control();
  ApplyForce(move_dir,move_spd);
  ApplyForce(map->wind_dir,map->wind_spd);
  if (speeding) {
    ApplyForce(speeding_dir,itofix(1));
    speeding--;
  }

  // Changing direction reduces speed a little
  if (head_dir!=prev_head_dir) move_spd=fmul(move_spd,ftofix(0.99));

  move_dir=fatan2(y-py,x-px)+itofix(64);
  move_spd=fsqrt(fmul(y-py,y-py)+fmul(x-px,x-px));
  if (!jumping) {
    if (speeding)
     move_spd=fmul(move_spd,ftofix(0.998));
    else
     move_spd=fmul(move_spd,ftofix(0.98));
  }

  // Check waypoints
  if (lines_intersect(
    map->waypoints[waypoint].x0>>16,map->waypoints[waypoint].y0>>16,
    map->waypoints[waypoint].x1>>16,map->waypoints[waypoint].y1>>16,
    x>>16,y>>16,px>>16,py>>16,
    (long*)&intx,(long*)&inty
  )==DO_INTERSECT) {
    waypoint++;
    if (!nosound)
     play_sample((SAMPLE*)(data[dat_waypoint].dat),255,128,1000,0);
  }

  // Handle jumps
  if (jumping) {
    jumping--;
    if (jumping==0) {
      if (!nosound)
       play_sample((SAMPLE*)(data[dat_splaaash].dat),255,128,1000,0);
      landed=1;
    }
  }
  else {
    // We're not jumping, so we can do it if there is a jumppad there
    for (n=0;n<map->njumppads;n++) {
      dx=ABS(map->jumppads[n].x-x)>>16;
      dy=ABS(map->jumppads[n].y-y)>>16;
      if ((dx*dx+dy*dy)<PADDIST) {
        Jump();
        break;
      }
    }
    // Or dash
    for (n=0;n<map->nspeedpads;n++) {
      dx=ABS(map->speedpads[n].x-x)>>16;
      dy=ABS(map->speedpads[n].y-y)>>16;
      if ((dx*dx+dy*dy)<PADDIST) {
        if (!nosound) {
          play_sample((SAMPLE*)(data[dat_speedpad].dat),255,128,1000,0);
          play_sample((SAMPLE*)(data[dat_scream].dat),255,128,1000,0);
        }
        speeding=64;
        speeding_dir=move_dir;
        break;
      }
    }
  }

  // Check collisions (should I sort the lists ?)
  if (!jumping) {
    if (!training && !clockmode) {
      for (n=0;n<nships;n++) if (ships[n]!=this) if (lastcol!=ships[n]) {
        sprw=ships[n]->image->w;
        sprh=ships[n]->image->h;
        if (CheckCollision(
          (x>>16)-sz/2,(y>>16)-sz/2,sz,sz,
          (ships[n]->x>>16)-sprw/2,(ships[n]->y>>16)-sprh/2,
          sprw,sprh
        )) {
          if (!nosound)
           play_sample((SAMPLE*)(data[dat_collide].dat),180,128,1000,0);
          if (move_spd>ftofix(0.2))
           move_spd=fmul(move_spd,(ftofix(0.35*crossing))>16);
          lastcol=ships[n];
          break;
        }
      }
    }
    sprw=((BITMAP*)(data[dat_buoy].dat))->w;
    sprh=((BITMAP*)(data[dat_buoy].dat))->h;
    for (n=0;n<map->nbuoys;n++) if (lastcol!=&map->buoys[n]) {
      if (CheckCollision(
        (x>>16)-sz/2,(y>>16)-sz/2,sz,sz,
        (map->buoys[n].x>>16)-sprw/2,(map->buoys[n].y>>16)-sprh/2,
        sprw,sprh
      )) {
        if (!nosound)
         play_sample((SAMPLE*)(data[dat_collide].dat),180,128,1000,0);
        if (move_spd>ftofix(0.2))
         move_spd=fmul(move_spd,(ftofix(0.35*crossing))>16);
        lastcol=&map->buoys[n];
        break;
      }
    }
    for (n=0;n<map->nrocks;n++) if (lastcol!=&map->rocks[n]) {
      sprw=((BITMAP*)(data[dat_rock1+map->rocks[n].type].dat))->w;
      sprh=((BITMAP*)(data[dat_rock1+map->rocks[n].type].dat))->h;
      if (CheckCollision(
        (x>>16)-sz/2,(y>>16)-sz/2,sz,sz,
        (map->rocks[n].x>>16)-sprw/2,(map->rocks[n].y>>16)-sprh/2,
        sprw,sprh
      )) {
        if (!nosound)
         play_sample((SAMPLE*)(data[dat_collide].dat),180,128,1000,0);
        if (move_spd>ftofix(0.1))
         move_spd=fmul(move_spd,(ftofix(0.1*crossing))>>16);
        lastcol=&map->rocks[n];
        break;
      }
    }
    sprw=((BITMAP*)(data[dat_nenuphar].dat))->w;
    sprh=((BITMAP*)(data[dat_nenuphar].dat))->h;
    for (n=0;n<map->nnenuphars;n++) {
      if (CheckCollision(
        (x>>16)-sz/2,(y>>16)-sz/2,sz,sz,
        (map->nenuphars[n].x>>16)-sprw/2,(map->nenuphars[n].y>>16)-sprh/2,
        sprw,sprh
      )) {
        if (!nosound)
         play_sample((SAMPLE*)(data[dat_nenuslide].dat),64,128,1000,0);
        if (type!=2) // Everglades handle nenuphars perfectly
         if (move_spd>itofix(2))
          move_spd=fmul(move_spd,(ftofix(0.9*crossing))>>16);
        break;
      }
    }
  }

  // Update voice
  if (!nosound)
   voice_set_frequency(voice,22050+1024*MID(0,(move_spd>>16),255));

  // Check victory
  return (waypoint==map->nwaypoints);
}

void Ship::Draw(fixed x,fixed y,int sx,int sy,int sw,int sh)
{
  rotate_scaled_sprite(
    drawable,image,
    sx+(this->x>>16)-(x>>16)-image->w/2,sy+(this->y>>16)-(y>>16)-image->h/2,
    head_dir,
    jumping?(itofix(1)+2*(fsin(itofix(128)*jumping/jump_length))):itofix(1)
  );
}

static void line_plotter(BITMAP *water,int x,int y,int d)
{
  putpixel(water,x+(rand()&3)-1,y+(rand()&3)-1,d);
}

void Ship::DrawTrail(fixed x,fixed y,int sx,int sy,int sw,int sh)
{
  int n;

  set_clip(
    water2,
    sx/WATERSCALE,sy/WATERSCALE,(sx+sw)/WATERSCALE-1,(sy+sh)/WATERSCALE-1
  );
  if (!jumping) {
    for (n=0;n<8;n++) putpixel(
      water2,
      (sx+(this->x>>16)-(x>>16)-image->w/2+(rand()&7)-3)/WATERSCALE,
      (sy+(this->y>>16)-(y>>16)-image->h/2+(rand()&7)-3)/WATERSCALE,
      255
    );
/*
    do_line(
      water2,
      (sx+(this->x>>16)-(x>>16)-image->w/2)/WATERSCALE,
      (sy+(this->y>>16)-(y>>16)-image->h/2)/WATERSCALE,
      (sx+(this->x>>16)-(last_render_x>>16)-image->w/2)/WATERSCALE,
      (sy+(this->y>>16)-(last_render_y>>16)-image->h/2)/WATERSCALE,
      255,line_plotter
    );
*/
  }
  if (landed) {
    circle(
      water2,
      (sx+(this->x>>16)-(x>>16)-image->w/2)/WATERSCALE,
      (sy+(this->y>>16)-(y>>16)-image->h/2)/WATERSCALE,
      8,255
    );
    circle(
      water2,
      (sx+(this->x>>16)-(x>>16)-image->w/2)/WATERSCALE,
      (sy+(this->y>>16)-(y>>16)-image->h/2)/WATERSCALE,
      14,255
    );
    landed=0;
  }
  last_render_x=x;
  last_render_y=y;
}

void Ship::Status(int sx,int sy)
{
  textprintf(
    drawable,font,sx,sy,color,
    "Speed: %2.2f",fixtof(move_spd)
  );
  textprintf(
    drawable,font,sx,sy+12,color,
    "Next waypoint: %d",waypoint
  );
}

ArrowShip::ArrowShip(): Ship()
{
  turn_rate=ftofix(1.33);
  power_rate=ftofix(0.25*1.333);
  crossing=ftofix(0.2);
  image=(BITMAP*)(data[dat_arrow].dat);
  type=0;
}

CatamaranShip::CatamaranShip(): Ship()
{
  turn_rate=ftofix(2.0);
  power_rate=ftofix(0.1*1.333);
  crossing=ftofix(0.4);
  image=(BITMAP*)(data[dat_catamaran].dat);
  type=1;
}

EvergladesShip::EvergladesShip(): Ship()
{
  turn_rate=ftofix(2.66);
  power_rate=ftofix(0.1*1.333);
  crossing=ftofix(1.0);
  image=(BITMAP*)(data[dat_everglades].dat);
  type=2;
}

MediumShip::MediumShip(): Ship()
{
  turn_rate=ftofix(2.0);
  power_rate=ftofix(0.2*1.333);
  crossing=ftofix(0.6);
  image=(BITMAP*)(data[dat_medium].dat);
  type=3;
}

StarshipShip::StarshipShip(): Ship()
{
  turn_rate=ftofix(1.33);
  power_rate=ftofix(0.2*1.333);
  crossing=ftofix(0.4);
  image=(BITMAP*)(data[dat_starship].dat);
  type=4;
}

ZodiacShip::ZodiacShip(): Ship()
{
  turn_rate=ftofix(3.33);
  power_rate=ftofix(0.15*1.333);
  crossing=ftofix(0.4);
  image=(BITMAP*)(data[dat_zodiac].dat);
  type=5;
}


