#include "common.hpp"
#include "MrboomHelper.hpp"

#ifdef __LIBRETRO__
#include <time/rtime.h>
#else
#include <time.h>
#endif

#pragma GCC diagnostic ignored "-Warray-bounds"

void addOneAIPlayer()
{
   db *keys = m.total_t;

   keys[64 + 5 + m.nombre_de_dyna * 7] = 1;
   keys[8 * 7 + 2] = 1;
   m.nb_ai_bombermen++;
}

void addXAIPlayers(int x)
{
   db *keys = m.total_t;

   for (int i = 0; i < x; i++)
   {
      keys[64 + 5 + i * 7] = 1;
   }
   keys[8 * 7 + 2] = 1;
}

void pressStart()
{
   db *keys = m.total_t;

   keys[8 * 7] = 1;
   keys[8 * 7 + 2] = 1;
}

void pressESC()
{
   m.sortie = 1;
}

bool isInTheApocalypse()
{
   return (m.in_the_apocalypse == 1);
}

bool hasRollers(int player)
{
   return (m.patineur[player] == 1);
}

bool hasRemote(int player)
{
   return (m.j1[4 + player * 5]);
}

bool hasPush(int player)
{
   return (m.pousseur[player] == 1);
}

bool hasTriBomb(int player)
{
   return (m.tribombe[player] == 1);
}

// Warning will be zero if less then 1
int pixelsPerFrame(int player)
{
   return (CELLPIXELSSIZE / framesToCrossACell(player));
}

int framesToCrossACell(int player)
{
   bool speed = hasSpeedDisease(player);
   bool slow = hasSlowDisease(player);

   if (hasRollers(player))
   {
      if (slow)
      {
         return ((CELLPIXELSSIZE / 2) * 4); //32
      }
      if (speed)
      {
         return ((CELLPIXELSSIZE / 2) / 4); //2
      }
      return (CELLPIXELSSIZE / 2); //8
   }

   if (slow)
   {
      return (CELLPIXELSSIZE * 4); //64
   }
   if (speed)
   {
      return (CELLPIXELSSIZE / 4); //4
   }
   return (CELLPIXELSSIZE); //16
}

int nbLives(int player)
{
   if (isAlive(player))
   {
      return (m.nombre_de_coups[player] + 1);
   }
   else
   {
      return (0);
   }
}

bool isDead(int player)
{
   return (m.vie[player] == 16);
}

bool isAlive(int player)
{
   return (m.vie[player] == 1);
}

bool isAIActiveForPlayer(int player)
{
   return ((m.control_joueur[player] >= 64) && (m.control_joueur[player] <= 64 * 2));
}

bool playerGotDisease()
{
   static int current = 0;
   int diseases = 0;
   bool result = false;

   for (int i = 0; i < numberOfPlayers(); i++)
   {
      if (isAlive(i) && hasAnyDisease(i))
      {
         diseases++;
      }
   }
   if (current < diseases)
   {
      result = true;
   }
   current = diseases;
   return (result);
}

bool hasAnyDisease(int player)
{
   return (m.maladie[player * 2] != 0);
}

bool hasSlowDisease(int player)
{
   return (m.maladie[player * 2] == 2);
}

bool hasSpeedDisease(int player)
{
   return (m.maladie[player * 2] == 1);
}

bool hasInvertedDisease(int player)
{
   return (m.maladie[player * 2] == 4);
}

bool hasDiarrheaDisease(int player)
{
   return (m.maladie[player * 2] == 3);
}

bool hasSmallBombDisease(int player)
{
   return (m.maladie[player * 2] == 6);
}

bool hasConstipationDisease(int player)
{
   return (m.maladie[player * 2] == 5);
}

void setDisease(int player, int disease, int duration)
{
   m.maladie[player * 2] = disease;
   m.maladie[player * 2 + 1] = duration;
}

int numberOfPlayers()
{
   return (m.nombre_de_dyna);
}

bool replay()
{
   return (m.action_replay != 0);
}

int level()
{
   if (replay())
   {
      return (-1);
   }
   if (inTheMenu())
   {
      return (-1);
   }
   return (m.viseur_liste_terrain);
}

void chooseLevel(int level)
{
   m.viseur_liste_terrain = level;
}

bool inTheMenu()
{
   return ((isGameActive() == false) && m.ordre == 'S');
}

bool isGameActive()
{
   if ((m.ordre == 1) && (m.ordre2 == 3))
   {
      return (true);
   }
   return (false);
}

bool isAboutToWin()
{
   return (isGameActive() && (m.attente_avant_med < 100));
}

bool isDrawGame()
{
   return (m.ordre2 == 'D');
}

bool won()
{
   return (m.ordre2 == 'Z');
}

int nbBombsLeft(int player)
{
   if (m.nombre_de_vbl_avant_le_droit_de_poser_bombe)
   {
      return (0);
   }
   if (hasConstipationDisease(player))
   {
      return (0);
   }
   if (isAboutToWin())
   {
      return (0);
   }
   return (m.j1[player * 5]); //nb of bombs
}

bool isApocalypseSoon()
{
   return (isGameActive() && ((m.temps & 0x3FFF) < 3));
}

void activeApocalypse()
{
   m.temps = 2;
}

int invincibility(int player)
{
   return (m.invinsible[player]);
}

void activeCheatMode()
{
   log_info("activeCheatMode\n");
   // m.temps = 1;
   for (db i = 0; i < nb_dyna; i++)
   {

      if (isAIActiveForPlayer(i))
         continue;

      /*
       * if (i>= m.nombre_de_dyna)
       *      m.nombre_de_coups[i]=1; //number of lifes
       * else
       *      m.nombre_de_coups[i]=99;
       */

      if (i < m.nombre_de_dyna)
      {
         if (i%2) {
            setDisease(i,1,1000); // 3:Diarrhea
         }
         m.j1[i * 5] = 3; //nb of bombs
         //m.j1[1+i*5]=5; // power of bombs
         m.j1[4 + i * 5] = 1; // remote
         m.pousseur[i] = 1;   // bomb pusher
         m.lapipipino[i] = 1;
         m.tribombe[i] = 1;
         m.invinsible[i] = 60*10;
         m.nombre_de_coups[i]++;
      }
   }
   setNoMonsterMode(true);
}

void setNoMonsterMode(bool on)
{
   m.nomonster = on;
}

bool bonusPlayerWouldLike(int player, enum Bonus bonus)
{
   switch (bonus)
   {
   case no_bonus:
   case bonus_skull:
   case bonus_time:
      return (false);

   case bonus_roller:
      return (hasRollers(player) == false);

   case bonus_remote:
      return (hasRemote(player) == false);

   case bonus_tribomb:
      return (false); // because AI doesn't know how to use it
      //		return (hasTriBomb(player)==false);

   case bonus_push:
      return (hasPush(player) == false);

   case bonus_egg:
      return (hasKangaroo(player) == false);

   default:
      break;
   }
   return (true);
}

void setFrameNumber(int frame)
{
   m.changement = frame;
}

int flameSize(int player)
{
   if (hasSmallBombDisease(player))
   {
      return (1);
   }
   return (m.j1[1 + player * 5]);
}

void setTeamMode(int teamMode)
{
   m.team3_sauve = teamMode;
}

int teamMode()
{
   return (m.team3_sauve);
}

void setAutofire(bool on)
{
   if (on)
   {
      m.autofire = 1;
   }
   else
   {
      m.autofire = 0;
   }
}

bool autofire()
{
   return (m.autofire == 1);
}

int xPlayer(int player)
{
   return ((m.donnee[player] + DELTA_X) / CELLPIXELSSIZE);
}

int yPlayer(int player)
{
   return ((m.donnee[nb_dyna + player] + DELTA_Y) / CELLPIXELSSIZE);
}

int cellPlayer(int player)
{
   return (xPlayer(player) + yPlayer(player) * grid_size_x);
}

bool tracesDecisions(int player)
{
   return (debugTracesPlayer(player) && (traceMask & DEBUG_MASK_BOTTREEDECISIONS));
}

bool isInMiddleOfCell(int player)
{
   int step = pixelsPerFrame(player);

   assert(step <= MAX_PIXELS_PER_FRAME);
   int x = GETXPIXELSTOCENTEROFCELL(player);
   int y = GETYPIXELSTOCENTEROFCELL(player);
   if (step < 1)
   {
      return ((!x) && (!y));
   }
   return ((x >= -step / 2) && (x <= step / 2) && (y <= step / 2) && (y >= -step / 2));
}

int dangerousCellForMonster(int player)
{
   int cell = cellPlayer(player);
   int index = m.viseur_change_in[player] / 4;

   index--;
   if (index < 0)
   {
      index = 15;
   }
   switch (m.changeiny[index])
   {
   case 0:
      return (cell + grid_size_x);

   case 8:
      return (cell + 1);

   case 16:
      return (cell - 1);

   case 24:
      return (cell - grid_size_x);
   }
   assert(0);
   return (0);
}

int victories(int player)
{
   int mode = teamMode();

   switch (mode)
   {
   case 0: // selfie mode
      return (m.victoires[player]);

      break;

   case 1: // color mode
      return (m.victoires[player & ~1]);

      break;

   case 2: // sex mode
      return (m.victoires[player % 2]);

      break;

   case 4: // skynet mode
      return (m.victoires[player]);

      break;

   default:
      assert(0);
      break;
   }
   return (0);
}

void pauseGameButton()
{
   if (replay())
   {
      pressESC();
      return;
   }
   if (m.pauseur2)
   {
      m.pauseur2 = 0;
   }
   else
   {
      m.pauseur2 = 4;
   }
}

bool isGameUnPaused()
{
   bool result = false;
   static bool prev = isGamePaused();

   if ((isGamePaused() == false) && (prev))
   {
      result = true;
   }
   prev = isGamePaused();
   return (result);
}

bool isGamePaused()
{
   return (m.pauseur2 && isGameActive());
}

bool isSuicideOK(int player)
{
   int myTeam = teamOfPlayer(player);
   int nbLivesEnemies = 0;
   int nbLivesFriends = 0;

   for (int i = 0; i < numberOfPlayers(); i++)
   {
      if (myTeam == teamOfPlayer(i))
      {
         nbLivesFriends += nbLives(i);
         if (invincibility(i))
         {
            nbLivesFriends++;
         }
      }
      if (myTeam != teamOfPlayer(i))
      {
         nbLivesEnemies += nbLives(i);
         if (invincibility(i))
         {
            nbLivesEnemies++;
         }
      }
   }
   return ((nbLivesFriends > 1) && (nbLivesEnemies == 1));
}

bool someHumanPlayersAlive() // About to die players are considered dead
{
   for (int i = 0; i < numberOfPlayers(); i++)
   {
      if (isAIActiveForPlayer(i) == false)
      {
         if (isAlive(i))
         {
            return (true);
         }
      }
   }
   return (false);
}

bool someHumanPlayersNotDead() // About to die players are considered alive
{
   for (int i = 0; i < numberOfPlayers(); i++)
   {
      if (isAIActiveForPlayer(i) == false)
      {
         if (!isDead(i))
         {
            return (true);
         }
      }
   }
   return (false);
}

int getInputForPlayer(unsigned int player)
{
   if (m.control_joueur[player] == 512) {
      return -1;
   }
   return m.control_joueur[player] / 7;
}

bool isXmasPeriod()
{
   static bool res = false;
   static bool init = false;
   if (init)
   {
      return res;
   }
   init = true;

   time_t rawtime;
   struct tm *timeinfo;

   rawtime = time(NULL);
#ifdef __LIBRETRO__
   struct tm timeinfo_retro;
   rtime_localtime(&rawtime, &timeinfo_retro);
   timeinfo = &timeinfo_retro;
#else
   timeinfo = localtime(&rawtime);
#endif

   if (timeinfo->tm_mon == 11 && (timeinfo->tm_mday >= 20 && timeinfo->tm_mday <= 31))
   {
      res = true;
   }
   return res;
}
