#include "main.h"


/* Note that this is 100% C code, not C++. :p */

#ifndef MIN
#  define MIN(a,b)   ((a) > (b) ? (b) : (a))
#endif

float          *cam_matrix = (float *)0x00B6F99C;

uint32_t       __time_current;
struct pool    *pool_actor;
struct pool    *pool_vehicle;

static struct cheat_state  __cheat_state;
struct cheat_state         *cheat_state = NULL;



static void cheat_main_actor(float time_diff)
{
   struct actor_info *info = actor_info_get(ACTOR_SELF, 0);


   if(info == NULL)
   {
      log_debug("wtf, actor_info_get() returned NULL.");
      return;
   }

   vect3_copy(&info->base.matrix[4*3], cheat_state->actor.coords);

   cheat_handle_freeze_vehicles(NULL, info);

   cheat_handle_hp(NULL, info, time_diff);
   cheat_handle_teleport(NULL, info, time_diff);
   /* the following functions can be found in cheat_actor.cpp */
   cheat_handle_actor_air_brake(info, time_diff);
   cheat_handle_actor_autoaim(info, time_diff);
   cheat_handle_stick(NULL, info, time_diff);
}


static void cheat_main_vehicle(float time_diff)
{
   struct vehicle_info *info = vehicle_info_get(VEHICLE_SELF, 0);


   if(info == NULL)
   {
      log_debug("wtf, vehicle_info_get() returned NULL.");
      return;
   }

   vect3_copy(&info->base.matrix[4*3], cheat_state->vehicle.coords);
   cheat_handle_teleport(info, NULL, time_diff);

   cheat_handle_freeze_vehicles(info, NULL);

   cheat_handle_hp(info, NULL, time_diff);
   /* the following functions can be found in cheat_vehicle.cpp */
   cheat_handle_vehicle_unflip(info, time_diff);
   cheat_handle_vehicle_nitro(info, time_diff);
   cheat_handle_vehicle_air_brake(info, time_diff);
   cheat_handle_vehicle_warp(info, time_diff);
   cheat_handle_stick(info, NULL, time_diff);
   cheat_handle_vehicle_self_destruct(info, time_diff);
   cheat_handle_vehicle_protection(info, time_diff);
   cheat_handle_vehicle_quick_turn(info, time_diff);
   cheat_handle_vehicle_brake(info, time_diff);
   cheat_handle_vehicle_hop(info, time_diff);
}


void cheat_hook(HWND wnd)
{
   static uint32_t time_last;
   static int chatting;
   struct actor_info *actor_info;
   struct vehicle_info *vehicle_info;
   int i;


   /* for teh time_get() macro */
   __time_current = __time_get();

   keyhook_maybe_install(wnd);
   menu_maybe_init();

   /* initialize state */
   if(cheat_state == NULL)
   {
      cheat_state = &__cheat_state;
      cheat_state->generic.spoof_weapon         = -1;
      cheat_state->generic.money                = set.money_enabled;
      cheat_state->generic.weapon               = set.weapon_enabled;
      cheat_state->generic.vehicles_unlock      = set.vehicles_unlock;
      cheat_state->generic.vehicles_warp_invert = set.vehicles_warp_invert;
      cheat_state->actor.invulnerable           = set.hp_indestructible;
      cheat_state->vehicle.invulnerable         = set.hp_indestructible;
      cheat_state->vehicle.hp_tire_support      = set.hp_tire_support;
      cheat_state->vehicle.hp_minimum_on        = 1;
      cheat_state->vehicle.hp_damage_reduce_on  = 1;
      cheat_state->vehicle.hp_regen_on          = 1;
      cheat_state->game_speed                   = 1.0f;

      /* install patches from the .ini file */
      for(i=0; i<INI_PATCHES_MAX; i++)
      {
         if(set.patch[i].name != NULL && set.patch[i].ini_auto)
            patcher_install(&set.patch[i]);
      }
   }

   cheat_state->state = CHEAT_STATE_NONE;

   /* set up actor pool */
   pool_actor = *(struct pool **)ACTOR_POOL_POINTER;
   if(pool_actor == NULL)          return;
   if(pool_actor->start == NULL)   return;
   if(pool_actor->size <= 0)       return;

   /* set up vehicle pool */
   pool_vehicle = *(struct pool **)VEHICLE_POOL_POINTER;
   if(pool_vehicle == NULL)          return;
   if(pool_vehicle->start == NULL)   return;
   if(pool_vehicle->size <= 0)       return;

   actor_info = actor_info_get(ACTOR_SELF, ACTOR_ALIVE);
   vehicle_info = vehicle_info_get(VEHICLE_SELF, 0);

   /* no vehicle, and no actor. exit. */
   if(vehicle_info == NULL && actor_info == NULL)
   {
      if(cheat_state->actor.air_brake   || cheat_state->actor.stick ||
         cheat_state->vehicle.air_brake || cheat_state->vehicle.stick)
      {
         cheat_state->actor.air_brake = 0;
         cheat_vehicle_air_brake_set(0);
         cheat_state->actor.stick = 0;
         cheat_state->vehicle.stick = 0;
         cheat_state_text("Air brake / stick disabled");
      }
   }
   else
   {
      if(vehicle_info == NULL)
      {
         if(cheat_state->vehicle.air_brake || cheat_state->vehicle.stick)
         {
            cheat_vehicle_air_brake_set(0);
            cheat_state->vehicle.stick = 0;
            cheat_state_text("Air brake / stick disabled");
         }
         cheat_state->state = CHEAT_STATE_ACTOR;
      }
      else
      {
         if(cheat_state->actor.air_brake || cheat_state->actor.stick)
         {
            cheat_state->actor.air_brake = 0;
            cheat_state->actor.stick = 0;
            cheat_state_text("Air brake / stick disabled");
         }
         cheat_state->state = CHEAT_STATE_VEHICLE;
      }
   }


   if(set.d3dtext_chat)
   {
      struct samp_chat_info *info = samp_chat_info_get();
      if(info != NULL && info->enabled)
      {
         log_debug("Disabling SA:MP chat text.");
         info->enabled = 0;
         set.d3dtext_chat = 0;
      }
   }
   if(set.d3dtext_kill)
   {
      struct samp_kill_info *info = samp_kill_info_get();
      if(info != NULL && info->enabled)
      {
         log_debug("Disabling SA:MP kill list.");
         info->enabled = 0;
         set.d3dtext_kill = 0;
      }
   }


   if(rand() % 5000 == 0)
      srand(rand() + time_get());

   keyhook_old_run();

   if(cheat_panic())
   {
      cheat_state->state = CHEAT_STATE_NONE;
      goto out;
   }

   menu_run();

   /* install volatile patches from the .ini file */
   for(i=0; i<INI_PATCHES_MAX; i++)
   {
      if(set.patch[i].name != NULL && set.patch[i].has_volatile && set.patch[i].installed)
         patcher_install(&set.patch[i]);
   }

   /* generic stuff - these want to run even though we're dead */
   cheat_handle_debug(wnd);
   cheat_handle_misc();
   cheat_handle_spoof_weapon();
   gta_game_speed_set(cheat_state->game_speed);

   if(cheat_state->state != CHEAT_STATE_NONE)
   {
      /* generic stuff */
      cheat_handle_weapon_disable();
      cheat_handle_money();
      cheat_handle_weapon();
      cheat_handle_unlock();
      cheat_handle_checkpoint();

      /* do stuff :p */
      if(cheat_state->state == CHEAT_STATE_VEHICLE)
         cheat_main_vehicle(TIME_TO_FLOAT(time_get() - time_last));
      else if(cheat_state->state == CHEAT_STATE_ACTOR)
         cheat_main_actor(TIME_TO_FLOAT(time_get() - time_last));

      for(i=0; i<INI_PATCHES_MAX; i++)
      {
         if(set.patch[i].name != NULL && KEY_PRESSED(set.patch[i].ini_hotkey))
         {
            if(set.patch[i].installed || set.patch[i].failed)
               patcher_remove(&set.patch[i]);
            else
               patcher_install(&set.patch[i]);
         }
      }
   }

out:;
   keyhook_run();
   time_last = time_get();
}

