/* Headers for utils.c */

// Location and direction of movement of Puck, in Vec coordinates.  This allows sub-cell accuracy of
// movement.  Wouldn't be needed for an ASCII implementation except for its ability to fine-control the
// speed of movement.  The code to walk the maze performing a breadth-first-search also uses an Actor

typedef struct MazeCoord {
  int8_t col, row;
  // col is the x coordinate of the cell in ascii units
  // and dx is -1 or 0 or 1 to link to the adjacent cell.
  // It is NOT to be confused with the Actor.dx motion units.
  // These need really only be 2 bits each (to store -1, 0, 1).
  // Since x and y are both <= 31 (i.e. use only 5 bits),
  // We could easily store dx packed into X and dy into Y
  // especially if we use C bitfields to make them more
  // readable.
} MazeCoord;

typedef struct ScreenCoord {
  int8_t Y,X;  // Vectrex coords -128:127
} ScreenCoord;

typedef struct GHOST {
  ScreenCoord screen;
  uint8_t dir;              // 0:3 for direction, synonymous with dx[dir],dy[dir], cached as .dx, .dy
  int8_t dx, dy;            // These are an offset of +1, 0, -1 to the adjacent cell.
  int8_t sdx, sdy;          // These are the number of screen units that the actor will move on each
                            // frame, i.e. a combination of both its direction and speed.
  MazeCoord rowcol;         // ranges 0:31, 0:28
  //  uint8_t last_junction;    // where Puck has been, and where he might go if reversing direction were allowed
  //  uint8_t next_junction;    // where Puck is heading next. Ghost is currently between last junction and next junction
  uint8_t target_junction;  // Where Ghost is heading // TO DO
  uint8_t State;            // Alive or dead? Later: in jail, scared, etc.
} GHOST;

typedef struct PLAYER {
  ScreenCoord screen;
  uint8_t dir;       // 0:4 for direction, synonymous with dx[dir],dy[dir], cached as .dx, .dy
                     // direction[4] is 'stuck', where dx == dy == 0
  int8_t dx, dy;     // These are an offset of +1, 0, -1 to the adjacent cell.
  int8_t sdx, sdy;   // These are the number of screen units that the actor will move on each
                     // frame, i.e. a combination of both its direction and speed.
  MazeCoord rowcol;  // ranges 0:31, 0:28
  // TO DO: move dx,dx into rowcol, move sdx,sdy into screen.
  uint8_t last_junction;  // where Puck has been, and where he might go if he reverses direction.
                          // When Puck exits a decision square at the end of a corridor,
                          // 'last_junction' is set to that square, and 'next_junction' is set to
                          // the decision square at the end of the junction he is about to head down.
  //uint8_t corridor_code;  // where Puck is now.  (or 0 if on a junction) TO DO: for when ghost arrives at final junction, this tells it which corridor to go down to get Puck
                          // (an idea... maybe set to corridor id of exit, which should always be known?)
  //uint8_t next_junction;  // where Puck is heading next
} PLAYER;

static inline uint8_t junction_dist_and_dir(uint8_t mapno, uint8_t j1, uint8_t j2);

#ifdef VECTREX

static uint8_t dotScale = 125;

//static inline bool isalpha(char c);
//static inline void DrawPowerPill(int8_t PP);
static int8_t spriteYoffset;
static int8_t spriteXoffset;
static uint8_t spriteMoveScale;


#else // not Vectrex, ie Linux

static uint8_t dotScale = 132;

//static inline void DrawPowerPill(int8_t PP);
static inline int8_t Vec2CursesCol(int8_t VX);
static inline int8_t Vec2CursesRow(int8_t VY);
#endif // linux
static inline void framesync(void);
//static inline bool iscode(char c);

static inline bool alignedGhostX(void);
static inline bool alignedGhostY(void);
static inline bool alignedPlayerX(void);
static inline bool alignedPlayerY(void);
static inline int8_t Map2VecCol(int8_t ax);
static inline int8_t Map2VecRow(int8_t ay);
static inline int8_t Vec2MapCol(int8_t VX);
static inline int8_t Vec2MapRow(int8_t VY);

static inline bool wall_above_player(void);
static inline bool wall_below_player(void);
static inline bool wall_above_or_below_player(int8_t dy);
static inline bool wall_to_left_of_player(void);
static inline bool wall_to_right_of_player(void);
static inline bool wall_to_left_or_right_of_player(int8_t dx);

static inline bool wall_above_ghost(void);
static inline bool wall_below_ghost(void);
static inline bool wall_to_left_of_ghost(void);
static inline bool wall_to_right_of_ghost(void);

#ifdef NOT_USED_YET
static inline int Path(MazeCoord *M, uint8_t direction);
#endif

static inline int system_terminate(void);

//static inline void DrawGhost(void);
static inline void DrawPuck(void);
static inline void set_scale(uint8_t AbsScale);
static inline void system_init(void);

static int system_terminate(void);

#ifdef NEVER
static unsigned int vecRandom(void);
#endif

#ifdef LINUX
static int64_t millis(void);
//static void initRandom(uint8_t s1, uint8_t s2, uint8_t s3, uint8_t x0);
static void debugf_inner(char *filename, int line, char *s, ...);
//static void Intensity_7F(void);
#else // not linux, i.e. vectrex
#ifdef CHECKS
static void SHOW_NUM(char *dest, int16_t num);
#endif // CHECKS
#endif // not linux
static void system_init(void);

#if defined(LINUX) || defined(SEEK_PUCK)
static uint8_t decode_corridor_code(char c);
#endif

static uint8_t junction_code_to_index(char code);
#ifdef LINUX
static char junction_index_to_code(uint8_t num);
static uint8_t rangecheck_inner(char *var, uint8_t idx, uint8_t lower, uint8_t upper, int line);
#endif

static bool is_eaten(uint8_t dotno);
static void clear_eaten_flags(void);
#ifdef CHECKS
static void crash_inner(char *mess, char *filename, long line);
#endif // CHECKS
//static void Draw_Dots(void);

static void DrawPuck(void);

#ifdef SMARTGHOSTS
static uint8_t balanced(int thisPermutation) /*__attribute__ ((optimize(0)))*/;
static void constructDistanceMatrix(uint8_t *Target);
static void evaluateBestAssignment(uint8_t (*costFunction)(int thisPermutation));
static void printAssignmentsAndDistances(void);
static void reassignTargets(void);
#endif

static void set_eaten_flag(uint8_t dotno);
/* End of headers for utils.c */

#ifndef KEY_ESC
#define KEY_ESC 27
#endif

// NOTE: I just found out that one assumption is wrong: it *is* possible to
// have two junctions immediately next to each other.  I believe there are
// cases where this matters. (the middle two maps show them - view with 'j')

// TO DO: when the ghosts get themselves into a train, following right behind
// Puck, find a way to peel off all but the first so that Puck isn't dragging
// them around by the nose... (do this *after* proper pincer movement code is
// fully working.  The problem occurs when the paths to the kill zones happen
// to all be through the path Puck is taking anyway.  So they'll have to take
// the long way round to get ahead of him which is better than never catching
// him and allowing him free reign to go anywhere in the Maze with the ghosts
// all following predictably behind...)

/*
These symbols can be passed on the command line or set here:
   GCC_BUG_FIXED LINUX PARM_ARRAY USE_CRAPPY_DIAMOND VECTREX LOGFILE
 */

// TO DO: replace uint8_t with pseudo-strongly-typed types instead (eg 'DISTANCE'
// or 'JUNCTION_CODE').  Then once consistently in place, redefine them as structs
// so that we actually get strong type checking on assignments etc.  I've had more
// than one bug caused by not being careful about what was an ascii code and what
// was the binary index that the code mapped to.  Fortunately most of those were
// easily caught due to the array bound checks.

// WARNING: Editing this file in Vide corrupts the embedded literal Unicode characters,
// so edit it on linux and download with scp before building.  Make sure main.c edit window
// is closed in Vide before downloading otherwise too easy to accidentally revert.

// Select which map is used on starting the game.  (Note that I refer to 'level'
// a lot when I really mean map because for now the two are equivalent.  But
// eventually levels will re-use previous maps so I will need to be more careful
// in terminology and use of the 'level' vs 'mapno' variables.  Currently there
// is only a 'level' variable with mapno being inferred from level&3 ...)

// Set this to -1..2 to force initial map when testing
#define INIT_LEVEL 255U  // 1 is added immediately so we start on map 0 (level 1).
//#define INIT_LEVEL 0U  // 1 is added immediately so we start on map 1 (level 2).
//#define INIT_LEVEL 1U  // 1 is added immediately so we start on map 2 (level 3).
//#define INIT_LEVEL 2U  // 1 is added immediately so we start on map 3 (level 4).

// There are only 4 maps, but multiple levels.  We cycle through the levels,
// which correspond to the maps numbered (level&3).
static uint8_t level, mapno;
static uint8_t ghostno;

// number of small pills on each level

static uint8_t maxpill, pills_remaining;  // initialised to MAXPILL[mapno] below at start of level.

// These are fudge-factors applied *only by the dot drawing routine* after ideal Vec coordinates have
// been determined.  Used only for small positioning tweaks of the whole maze.

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#ifdef LINUX
#define Dot_M_HOFFSET 8
#define Dot_M_VOFFSET 8

#define Pow_M_VOFFSET 0
#else  // Not LINUX
#define Dot_M_HOFFSET 12
#define Dot_M_VOFFSET 16

#define Pow_M_VOFFSET 8
#endif  // Not LINUX

// Ghosts, Puck
#define DEAD 0
#define ALIVE 1

// Power pills
#define EATEN 0
#define UNEATEN 1

static uint8_t frame;  // main() loops a counter through 0 to 255, which
                       // is only used to control frequency of flashing etc.
                       // (At 30 FPS this is over 8 seconds, so an unsigned
                       // byte is enough for any of the timers in this game.)

// To speed up the development cycle, this code compiles and runs on a plain linux as well as for the Vectrex.
// The Linux version uses ncurses and keyboard I/O.  It is not a perfect mapping of the Vectrex graphics so
// take care when working on sections that don't use common code to ensure that both environments continue to work.

#ifdef LINUX

#ifndef LOGFILE
#define LOGFILE "debug.log"
#endif

WINDOW *mazewin, *scrollwin, *debugwin;  // ncurses windows.
#define COL2 130                         // later get window width and divide by 2.

//static int debug = 0;

// A very few Vectrex procedures are implemented here to avoid having to litter the code with more
// conditional tests than we already have done.
//static int8_t VecX = 0, VecY = 0;

static int console_height = 24, console_width = 80;  // fallback values. These *must* be "int" or curses breaks!

static bool show_junctions = FALSE, show_corridors = FALSE, redraw_maze = FALSE;  // debugging info

#endif


static int8_t button = -1;

static PLAYER Player;   // Player's X,Y coordinate
static GHOST Ghost[4];  // Ghosts' positions

// RAM!
static uint8_t eaten[MAXMAXPILL+1];  // byte array. faster to access but heavy on space  // MISSING +1 WAS THE BUG!

// Power-pill locations on each level (y, x in separate arrays)
static const int8_t PPy[4][4] IN_ROM = {
  {1 + 1, 1 + 1, 1 + 26, 1 + 26},
  {1 + 3, 1 + 3, 1 + 25, 1 + 25},
  {1 + 2, 1 + 2, 1 + 22, 1 + 22},
  {1 + 2, 1 + 2, 1 + 26, 1 + 26}
};
static const int8_t PPx[4] IN_ROM = {
  1 + 0, 1 + 25, 1 + 0, 1 + 25
};

// Power-pill eaten or not?  (4-byte array could be replaced by one byte but would cost speed)
static int8_t PState[4] = {UNEATEN, UNEATEN, UNEATEN, UNEATEN};

// Directions and Movements for pathfinding

#define LEFT 0
#define RIGHT 1
#define UP 2
#define DOWN 3
#define STUCK 4

#define DIRS 5
// (the 5th non-direction is only for Puck.  Ghosts are not allowed to stop moving)
static const uint8_t directions[DIRS] IN_ROM = {LEFT, RIGHT, UP, DOWN, STUCK};

static const uint8_t reverse[DIRS] IN_ROM = {RIGHT, LEFT, DOWN, UP, STUCK};

static const char *const name[DIRS] IN_ROM = {"left", "right", "up", "down", "stuck" };

static const int8_t dx[DIRS] IN_ROM = {-1, 1, 0, 0, 0};
static const int8_t dy[DIRS] IN_ROM = {0, 0, -1, 1, 0};

static uint8_t Target[4];

#ifdef SMARTGHOSTS
// Table of distances between each ghost and each target.
static uint8_t distanceMatrix[N][N];
// This is the re-ordering of ghosts that will be used to assign each ghost to a specific target destination:
static int bestPerm;
#endif

// There are only 24 possible ways to assign ghosts to targets so why not evalulate them all and pick the best one?
static const uint8_t order[PERMS][N] IN_ROM = {
    {0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1}, {1, 0, 2, 3}, {1, 0, 3, 2},
    {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0}, {2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0},
    {2, 3, 0, 1}, {2, 3, 1, 0}, {3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}};
