#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;

#if 0
static uint32_t raknet_ret_addr;


static void __cdecl hook_encrypt(uint8_t *stack)
{
   const uint8_t *data = (uint8_t *)(UINT_PTR)stack[3];
   int len = (int)stack[2];

   log_debug("encrypt %d bytes", len);
}

static void __declspec(naked) __hook_encrypt(void)
{
   __asm pushad
   __asm mov edi, esp
   __asm add edi, 0x28     ; pushad, 2x ret addr
   __asm push esp
   __asm call hook_encrypt
   __asm add esp, 0x04
   __asm popad

   /* samp.dll
      00886440 >/$  83EC 18       sub esp,18
      00886443  |.  53            push ebx
      00886444  |.  55            push ebp
      00886445  |.  56            push esi */
   __asm sub esp, 0x18
   __asm push ebx
   __asm push ebp
   __asm push esi
   __asm jmp dword ptr ds:[raknet_ret_addr]
}

static uint32_t raknet_addr = (uint32_t)(UINT_PTR)&__hook_encrypt;


static void hook(void)
{
   static int init;
   /* jmp far dword ptr [__rakpeer_send] */
   static const struct {
      #pragma pack(1)
      uint8_t b1, b2;
      uint32_t addr;
   } jmp_far = { 0xFF, 0x25, (uint32_t)(UINT_PTR)&raknet_addr };
   static struct patch_set patch = {
      "hook", 0, 0, {
      { 6, (void *)((uint8_t *)dll_baseptr_get("samp.dll") + 0x00016440),
           (uint8_t *)"\x83\xEC\x18\x53\x55\x56",
           (uint8_t *)&jmp_far, NULL }
      } };

   if(!init)
   {
      raknet_ret_addr = (uint32_t)(UINT_PTR)patch.chunk->ptr + 6;
      patcher_install(&patch);
      init = 1;
   }
}
#endif


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_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_break(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_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_break(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);
}


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.force_weather        = (set.force_weather != -1);
      cheat_state->generic.force_time           = (set.force_hour != -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;
      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, ACTOR_ALIVE);

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

   keyhook_old_run();

   menu_run();

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

   if(cheat_state->state != CHEAT_STATE_NONE)
   {
      /* generic stuff */
      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]);
         }
      }
   }

   keyhook_run();
   time_last = time_get();
}

