#include "main.h"


void cheat_vehicle_teleport(struct vehicle_info *info, const float pos[3], int interior_id)
{
   if(info == NULL)
      return;

   vehicle_detachables_teleport(info, &info->base.matrix[4*3], pos);
   vect3_copy(pos, &info->base.matrix[4*3]);

   vect3_mult(info->speed, 0.0f, info->speed);
   vect3_mult(info->spin, 0.0f, info->spin);

   gta_interior_id_set(interior_id);
   info->base.interior_id = (uint8_t)interior_id;
}


void cheat_handle_vehicle_unflip(struct vehicle_info *info, float time_diff)
{
   static float unflip_rotation;


   /* Unflip */
   if(KEY_PRESSED(set.key_unflip))
   {
      /* get the vehicle's yaw angle (z-axis) */
      unflip_rotation = atan2f(info->base.matrix[4*1+0], info->base.matrix[4*1+1]);
   }

   /* Rotate vehicle while the unflip key is held down */
   if(KEY_DOWN(set.key_unflip))
   {
      float a = unflip_rotation;
      float *m = info->base.matrix;
      float matrix[16] = {
            cosf(a),  -sinf(a),   0.0f,      0.0f,
            sinf(a),   cosf(a),   0.0f,      0.0f,
            0.0f,      0.0f,      1.0f,      0.0f,
            m[4*3+0],  m[4*3+1],  m[4*3+2],  1.0f
         };

      unflip_rotation += time_diff * M_PI;

      matrix_copy(matrix, info->base.matrix);
      vect3_mult(info->spin, 0.0f, info->spin);
   }
}


void cheat_handle_vehicle_air_break(struct vehicle_info *info, float time_diff)
{
   static float orig_matrix[16];
   static int orig_matrix_set;


   if(KEY_PRESSED(set.key_air_break_mod))
   {
      matrix_copy(info->base.matrix, orig_matrix);
      orig_matrix_set = 1;
      cheat_state->vehicle.air_break = set.air_break_toggle ?
            cheat_state->vehicle.air_break^1
          : 0;
   }

   if(KEY_PRESSED(set.key_air_break_mod2) && cheat_state->vehicle.air_break)
      cheat_state->vehicle.air_break_slowmo = set.air_break_toggle ?
            cheat_state->vehicle.air_break_slowmo^1
          : 0;

   if(KEY_DOWN(set.key_air_break_mod) || cheat_state->vehicle.air_break)
   {
      /* prevent all kinds of movement */
      vect3_mult(info->speed, 0.0f, info->speed);
      vect3_mult(info->spin, 0.0f, info->spin);
      if(!KEY_DOWN(set.key_unflip))    /* allow the unflip rotation feature to work */
         matrix_copy(orig_matrix, info->base.matrix);
   }
   else
   {
      cheat_state->vehicle.air_break = 0;
      cheat_state->vehicle.air_break_slowmo = 0;
   }

   if(KEY_DOWN(set.key_air_break_mod) || cheat_state->vehicle.air_break)
   {
      static uint32_t time_start;
      float *matrix = info->base.matrix;
      float d[4] = { 0.0f, 0.0f, 0.0f, time_diff * set.air_break_speed };
      float xyvect[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; /* rotation vector */
      float zvect[4]  = { 0.0f, 0.0f, 0.0f, 0.0f }; /* rotation vector */
      float theta = set.air_break_rot_speed * time_diff * M_PI * 2.0f;

      if(!orig_matrix_set)
      {
         log_debug("wtffffff");
         return;
      }


      if(KEY_DOWN(set.key_air_break_mod2) || cheat_state->vehicle.air_break_slowmo)
      {
         d[3] /= 10.0f;
         theta /= 2.0f;
      }

      if(KEY_DOWN(set.key_air_break_forward))   d[0] += 1.0f;
      if(KEY_DOWN(set.key_air_break_backward))  d[0] -= 1.0f;
      if(KEY_DOWN(set.key_air_break_left))      d[1] += 1.0f;
      if(KEY_DOWN(set.key_air_break_right))     d[1] -= 1.0f;
      if(KEY_DOWN(set.key_air_break_up))        d[2] += 1.0f;
      if(KEY_DOWN(set.key_air_break_down))      d[2] -= 1.0f;

      if(!near_zero(set.air_break_accel_time))
      {
         if(!vect3_near_zero(d))
            time_start = (time_start == 0) ? time_get() : time_start;
         else
            time_start = 0;   /* no keys pressed */

         /* acceleration */
         if(time_start != 0)
         {
            float t = TIME_TO_FLOAT(time_get() - time_start);
            if(t < set.air_break_accel_time)
               d[3] *= t / set.air_break_accel_time;
         }
      }

      /* pitch (x-axis) */
      if(KEY_DOWN(set.key_air_break_rot_pitch1))   xyvect[0] += 1.0f;
      if(KEY_DOWN(set.key_air_break_rot_pitch2))   xyvect[0] -= 1.0f;
      /* roll (y-axis) */
      if(KEY_DOWN(set.key_air_break_rot_roll1))    xyvect[1] += 1.0f;
      if(KEY_DOWN(set.key_air_break_rot_roll2))    xyvect[1] -= 1.0f;
      /* yaw (z-axis) */
      if(KEY_DOWN(set.key_air_break_rot_yaw1))     zvect[2] -= 1.0f;
      if(KEY_DOWN(set.key_air_break_rot_yaw2))     zvect[2] += 1.0f;

      if(!vect3_near_zero(xyvect))
      {
         vect3_normalize(xyvect, xyvect);

         matrix_vect4_mult(matrix, xyvect, xyvect);
         matrix_vect3_rotate(matrix, xyvect, theta, matrix);
      }

      if(!vect3_near_zero(zvect))
      {
         vect3_normalize(zvect, zvect);

         matrix_vect3_rotate(matrix, zvect, theta, matrix);
      }

      switch(set.air_break_behaviour)
      {
      case 0:  /* s0beit mode */
         matrix[4*1+0] += d[0] * d[3];
         matrix[4*1+1] += d[1] * d[3];
         matrix[4*1+2] += d[2] * d[3];
         break;

      case 1:  /* mixed 2d/3d mode */
         {
            /* convert the vehicle's 3d rotation vector to a 2d angle */
            float a = atan2f(matrix[4*1+0], matrix[4*1+1]);

            /* calculate new position */
            matrix[4*3+0] += (d[0] * sinf(a) - d[1] * cosf(a)) * d[3];
            matrix[4*3+1] += (d[0] * cosf(a) + d[1] * sinf(a)) * d[3];
            matrix[4*3+2] += d[2] * d[3];
         }
         break;

      case 2:  /* full 3d movement */
         if(!vect3_near_zero(d))
         {
            float vect[4] = { -d[1], d[0], d[2], 0.0f };  /* swap x & y + invert y */
            float out[4];

            /* out = matrix * norm(d) */
            vect3_normalize(vect, vect);
            matrix_vect4_mult(matrix, vect, out);

            matrix[4*3+0] += out[0] * d[3];
            matrix[4*3+1] += out[1] * d[3];
            matrix[4*3+2] += out[2] * d[3];
         }
         break;
      }

      matrix_copy(matrix, orig_matrix);
   }
}


void cheat_handle_vehicle_warp(struct vehicle_info *info, float time_diff)
{
   static struct vehicle_state state;
   static uint32_t warp_time = 0;
   static int warping = 0;


   if(warp_time != 0)
   {
      if(time_get() - warp_time > MSEC_TO_TIME(100))
      {
         warp_time = 0;
      }
      else
      {
         vehicle_state_restore(info, &state);
      }
   }

   /* warp factor: fuck you */
   if(KEY_PRESSED(set.key_warp_mod))
   {
      float dir[4] = { 0.0f, 1.0f, 0.0f, 0.0f };
      float vect[4];

      if(set.warp_use_speed &&
         !vect3_near_zero(info->speed) &&
         vect3_length(info->speed) >= 0.01f)
      {
         /* use normalized speed vector */
         vect3_copy(info->speed, dir);
         vect3_normalize(dir, vect);
      }
      else
      {
         /* use vehicle direction matrix */
         matrix_vect4_mult(info->base.matrix, dir, vect);
         if(vect3_near_zero(vect))
            return;  /* shouldn't happen */
      }

      vehicle_state_store(info, &state);
      warp_time = 0;
      vect3_mult(vect, set.warp_speed, info->speed);
   }

   if(KEY_RELEASED(set.key_warp_mod))
   {
      warp_time = time_get();
      vehicle_state_restore(info, &state);
      info->collision_something = 0.0f;
   }
}


void cheat_handle_vehicle_nitro(struct vehicle_info *info, float time_diff)
{
   static uint32_t timer;
   static float speed_off;
   static int decelerating;


   if(KEY_PRESSED(set.key_nitro_mod))
   {
      speed_off = vect3_length(info->speed);
      decelerating = 0;
      timer = time_get();
   }

   /* "nitro" */
   if(KEY_DOWN(set.key_nitro_mod) && !vect3_near_zero(info->speed))
   {
      float speed = set.nitro_high;
      float etime = TIME_TO_FLOAT(time_get() - timer);

      if(etime < set.nitro_accel_time)
      {
         float time_div;

         if(!near_zero(set.nitro_accel_time))
         {
            if(!near_zero(set.nitro_high))
               time_div = (set.nitro_high - speed_off) / set.nitro_high;
            else
               time_div = 1.0f;

            speed = speed_off + (speed - speed_off) *
                    ((etime / set.nitro_accel_time) / time_div);
         }
         /* else { don't touch speed, it's set to set.nitro_high } */
      }

      if(!vect3_near_zero(info->speed))
      {
         vect3_normalize(info->speed, info->speed);
         vect3_mult(info->speed, speed, info->speed);
         if(vect3_near_zero(info->speed))
         {
            int i;
            log_debug("wtf :(");
            for(i=0; i<3; i++)
               info->speed[i] = 0.0f;
         }
      }
   }

   /* reduce speed when the nitro key is released */
   if(KEY_RELEASED(set.key_nitro_mod))
   {
      if(vect3_length(info->speed) > set.nitro_low)
      {
         speed_off = vect3_length(info->speed);
         decelerating = 1;
         timer = time_get();
      }
   }

   if(decelerating)
   {
      float speed = set.nitro_low;
      float etime = TIME_TO_FLOAT(time_get() - timer);

      if(etime < set.nitro_decel_time)
         speed = speed_off - (speed_off - speed) * (etime / set.nitro_decel_time);
      else
         decelerating = 0;

      if(!vect3_near_zero(info->speed))
      {
         vect3_normalize(info->speed, info->speed);
         vect3_mult(info->speed, speed, info->speed);
      }
   }
}


void cheat_handle_vehicle_self_destruct(struct vehicle_info *info, float time_diff)
{
   if(KEY_DOWN(set.key_self_destruct))
      info->hitpoints = 1.0f;
}


void cheat_handle_vehicle_protection(struct vehicle_info *info, float time_diff)
{
   if(KEY_PRESSED(set.key_protection))
      cheat_state->vehicle.protection ^= 1;

   if(cheat_state->vehicle.protection)
   {
      if(!vect3_near_zero(info->spin) && vect3_length(info->spin) > set.protection_spin_cap)
      {
         /* prevent rapid spinning */
         vect3_normalize(info->spin, info->spin);
         vect3_mult(info->spin, set.protection_spin_cap, info->spin);
      }

      vehicle_prevent_below_height(info, set.protection_min_height);
#if 0
      //if(set.protection_flip)
      {
         const float z[3] = {
            info->base.matrix[4*0+2],
            info->base.matrix[4*1+2],
            info->base.matrix[4*2+2]
         };

         /* have we flipped over? :) */
         if(z[2] <= 0.0f)
         {
            info->spin[0] = z[1] * 0.1f;
            info->spin[1] = z[0] * 0.1f;
         }
      }
#endif
   }
}

