#ifndef RAMJET_H_INCLUDED
#define RAMJET_H_INCLUDED

#include <ctype.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>

// player mapping
typedef enum {WHITE, BLACK} PLAYER;
#define IS_PL_BAD(pl) ((pl) & ~1)
#define FOE(pl) ((pl) ^ 1)

// piece-type and piece-code mappings
typedef enum {PT_NULL, PAWN, KNIGHT, BISHOP, ROOK, QUEEN, KING} PIECETYPE;
#define PT_2_PC(pt, pl) (((pl) << 3) | pt)
#define PC_2_PL(pc) ((pc) >> 3)
#define PC_2_PT(pc) ((pc) & 7)
extern char pc_2_char[16];
#define PT_2_CHAR(pt) (pc_2_char[(pt) + 8])
extern int char_2_pc[UCHAR_MAX + 1];

// square mapping
typedef enum {A8, B8, C8, D8, E8, F8, G8, H8,
              A7, B7, C7, D7, E7, F7, G7, H7,
              A6, B6, C6, D6, E6, F6, G6, H6,
              A5, B5, C5, D5, E5, F5, G5, H5,
              A4, B4, C4, D4, E4, F4, G4, H4,
              A3, B3, C3, D3, E3, F3, G3, H3,
              A2, B2, C2, D2, E2, F2, G2, H2,
              A1, B1, C1, D1, E1, F1, G1, H1} SQUARE;
#define SQ_NULL 64
#define IS_SQ_BAD(sq) ((sq) & ~0x3F)
#define SQ_RANK(sq) ((sq) >> 3)
#define SQ_FILE(sq) ((sq) & 7)
#define SQ_MIRROR(sq, pl) ((sq) ^ (0x38 & -((int) (pl))))
#define SQ_FRONT(sq, pl) ((sq) + ((pl) << 4) - 8)
#define SQ_DIA_W(sq, pl) (SQ_FRONT(sq, pl) - 1)
#define SQ_DIA_E(sq, pl) (SQ_FRONT(sq, pl) + 1)
#define SQ_K_CASTLE(east, pl) (SQ_MIRROR(C1 + ((east) << 2), pl))
#define SQ_R_CASTLE(east, pl) (SQ_MIRROR(D1 + (east) + (east), pl))
extern const char* const sq_2_str[];
#define STR_2_SQ(str) ((('8' - (str)[1]) << 3) + (tolower((str)[0]) - 'a'))
#define ALG_2_SQ(file, rank) ((file) + ((rank) << 3))

// hash functions map to 64-bit integers
typedef uint64_t HASHKEY;
#define HK_CONST(val) (UINT64_C(val))
#define HK_NULL (HK_CONST(0x0000000000000000))

// 64-bit node counter data type
typedef uint64_t NODECOUNT;
#define NC_PRINT PRIu64
#define NC_SCAN SCNu64
#define NC_MAX UINT64_MAX

// 64-bit time stamp in milliseconds
typedef uint64_t MILLISEC;
#define MS_PRINT PRIu64
#define MS_SCAN SCNu64
#define MS_MAX UINT32_MAX

// initialise the constants above
void rj_init();

// return a pointer to the first non-white character,
// force a terminator at the end of the first token,
// and set the original pointer after it
char* grab_token(char** str);

// return a pointer to the first non-white character after the specified token
char* after_token(char **str, const char *match);

// return a pointer to the end of the string,
// after appending a space character to it
char* append_space(char* str);

// print the string both on the file and stdout adding a newline at the end
void print_line(FILE* fp, const char* str);

// run a single-argument function in a new thread
void new_thread(void (*func)(void*), void* arg);

// get a time-stamp from the operating system
MILLISEC time_now();

// return the time elapsed in milliseconds
MILLISEC time_diff(MILLISEC start);

// test the functions above
int rj_test();

#endif // RAMJET_H_INCLUDED
