/**********************************************************************
This file is part of Crack dot Com's free source code release of Golgotha.
for information about compiling & licensing issues visit this URL
 If that doesn't help, contact Jonathan Clark at 
  golgotha_source@usa.net (Subject should have "GOLG" in it) 
***********************************************************************/

#include 
#include "objs/model_id.hh"
#include "objs/model_draw.hh"
#include "lisp/li_class.hh"
#include "math/pi.hh"
#include "math/trig.hh"
#include "math/angle.hh"
#include "g1_rand.hh"
#include "saver.hh"
#include "map_cell.hh"
#include "map.hh"
#include "map_man.hh"
#include "player.hh"
#include "object_definer.hh"
#include "objs/moneycrate.hh"
#include "objs/bank.hh"
#include "objs/bases.hh"
#include "objs/moneyplane.hh"
#include "lisp/li_class.hh"
#include "li_objref.hh"
#include "resources.hh"

#include "image_man.hh"
static g1_team_icon_ref radar_im("bitmaps/radar/moneyplane.tga");

enum
{
  RETURNING=0,
  TOBASE,
  DROPPING,
  LAUNCHING,
  TOBANK,
  GRABBING,
  LIFTING,
  DYING,
};

static li_symbol_ref stank_factory("mainbasepad");
static li_g1_ref_class_member li_crate("crate");
static li_int_class_member li_mode("mode");

const i4_float VSPEED = 0.04;
const i4_float FLY_HEIGHT = 3.0;
const i4_float DROP_HEIGHT = 2.7;

S1_SFX(mo_money, "narrative/money_recieved_22KHz.wav", S1_STREAMED, 200);

static g1_model_ref model_ref("moneyplane"), lod_ref("moneyplane_lod");
static i4_3d_vector crate_attach;

static void g1_moneyplane_init();
g1_object_definer
g1_moneyplane_def("moneyplane",
                  g1_object_definition_class::TO_MAP_PIECE |
                  g1_object_definition_class::EDITOR_SELECTABLE |
                  g1_object_definition_class::MOVABLE, 
                  g1_moneyplane_init);

i4_float damp_distance;

static void g1_moneyplane_init()
{
  crate_attach.set(0,0,0);
  model_ref()->get_mount_point("mountcrate",crate_attach);
  crate_attach -= g1_moneycrate_class::crate_attach();

  damp_distance = g1_moneyplane_def.defaults->speed/-log(0.9);
}

g1_moneyplane_class::g1_moneyplane_class(g1_object_type id,
                           g1_loader_class *fp)
  : g1_map_piece_class(id, fp)
{  
  draw_params.setup(model_ref.id(), 0, lod_ref.id());

  init_rumble_sound(G1_RUMBLE_JET);
  radar_type=G1_RADAR_VEHICLE;  
  radar_image=&radar_im;
  set_flag(BLOCKING      |
           TARGETABLE    |
           AERIAL        | 
           DANGEROUS, 1);

}
   
g1_moneycrate_class *g1_moneyplane_class::crate()
{
  li_class_context c(vars);
  return (g1_moneycrate_class *)(li_crate()->value());
}

void g1_moneyplane_class::set_crate(g1_moneycrate_class *c)
{
  vars->set(li_crate, new li_g1_ref(c));
}

i4_bool g1_moneyplane_class::move(const i4_3d_vector &d)
{
  i4_3d_vector np(x,y,h);

  np += d;

  unoccupy_location();
  x = np.x; 
  y = np.y;
  h = np.z;
  
  if (!occupy_location())
    return i4_F;

  if (crate())
  {
    i4_transform_class t;
    i4_3d_vector p;
    calc_world_transform(1.0, &t);
    t.transform(crate_attach, p);
    crate()->follow(p, i4_3d_vector(roll, pitch, theta));
  }

  return i4_T;
}

void g1_moneyplane_class::damage(g1_object_class *obj, int hp, i4_3d_vector _damage_dir)
{
  li_class_context c(vars);

  if (li_mode() != DYING)
  {
    g1_map_piece_class::damage(obj,hp,_damage_dir);
    if (health<20)
    {
      i4_float &roll_speed  = dest_x;
      i4_float &pitch_speed = dest_y;
      
      health = 20;
      set_flag(DANGEROUS,0);
      roll_speed  = 0;
      pitch_speed = 0;
      li_mode() = DYING;
      if (crate())
      {
        crate()->release();
        set_crate(0);
      }
    }
  }

}

void g1_moneyplane_class::think()
{
  int mode = li_mode();

  check_life(i4_F);
  
  i4_float height = g1_get_map()->terrain_height(x,y);
  switch (li_mode())
  {
    case RETURNING:
    {
      if (h::iterator p = g1_factory_list.begin();
      
        while (p!=g1_factory_list.end())
        {
          if (p->player_num==player_num &&
              p->id==g1_get_object_type(stank_factory.get()))
            attack_target = &*p;
          p++;
        }

        if (attack_target.valid())
        {
          dest_x = attack_target->x;
          dest_y = attack_target->y;
          dest_theta = 0;
          mode = TOBASE;
        }
      }
    } break;
    case TOBANK:
    case TOBASE:
    {
      i4_3d_vector d(dest_x - x,dest_y - y,0);
      i4_float
        dist = d.x*d.x+d.y*d.y;

      i4_float dangle;

      if (mode == TOBANK)
        d.z = height+FLY_HEIGHT - h;
      else
        d.z = height+DROP_HEIGHT - h;

      if (dist<0.0025)
      {
        if (theta!=dest_theta)
          dangle = i4_rotate_to(theta, dest_theta, defaults->turn_speed);
        else if (mode == TOBANK)
          mode = GRABBING;
        else
          mode = DROPPING;
        d.set(0,0,0);
      }
      else
      {
        i4_float angle = i4_atan2(d.y,d.x);
        i4_normalize_angle(angle);    

        if (dist>damp_distance)
        {
          dangle = i4_rotate_to(theta, angle, defaults->turn_speed);
          if (speedspeed)
            speed += defaults->accel;
          else
            speed = defaults->speed;
        }
        else
        {
          speed = (dist+0.1)*-log(0.9);
          if (theta!=dest_theta)
            dangle = i4_rotate_to(theta, dest_theta, defaults->turn_speed);
        }
        
        d.x = cos(angle)*speed;
        d.y = sin(angle)*speed;
      }

      if (dangle<-0.01)
        roll += (0.7 - roll)*0.1;
      else if (dangle>0.01)
        roll += (-0.7 - roll)*0.1;
      else
        roll += -roll*0.1;

      pitch += -pitch*0.1;

      move(d);
    } break;
    case DROPPING:
      if (crate())
      {
        g1_player_man.get(player_num)->money() += crate()->value();
        char msg[100];
        sprintf(msg, "Money Received : $%d", crate()->value());
        g1_player_man.show_message(msg, 0x00ff00, player_num);

        mo_money.play();
        crate()->release();
        set_crate(0);
      }
      mode = LAUNCHING;
      break;
    case LAUNCHING:
      if (h0)
      {
        int n=g1_rand(43)%g1_bank_list_count[player_num];
        
        i4_isl_list::iterator p = g1_bank_list[player_num].begin();
        for (int i=0; icrate_location(pos);
          pos -= crate_attach;
          dest_x = pos.x;
          dest_y = pos.y;
          dest_z = pos.z;
          dest_theta = p->theta;
          mode = TOBANK;
        }
      }
      break;
    case GRABBING:
      if (!attack_target.valid())
        mode = RETURNING;
      else
      {
        i4_bool ok=i4_T;

        ok = (i4_rotate_to(theta,0,defaults->turn_speed)==0) && ok;
        ok = (i4_rotate_to(pitch,0,defaults->turn_speed)==0) && ok;
        ok = (i4_rotate_to(roll, 0,defaults->turn_speed)==0) && ok;

        if (h>dest_z)
        {
          h -= VSPEED;
          ok = i4_F;
        }

        if (ok)
        {
          g1_bank_class *bank = (g1_bank_class *)attack_target.get();
          set_crate(bank->crate());
          bank->detach_crate();

          mode = LIFTING;
        }
      }
      break;
    case LIFTING:
    {
      if (h