#ifndef LIFE_H
#define LIFE_H

#include "loadsave.h"
#include "util.h"

/*** Defines ***/

#define CAGE_SIZE               8
#define BLOCK_SIZE              4
#define WORLD_CAGES             125000
#define WORLD_SIZE              (CAGE_SIZE*WORLD_CAGES)

#define MAX_TURNS_STABLE        7
#define MAX_TURNS_OSCILLATING   7

#define XY_HASH_BITS            7
#define XY_HASH_SIZE            (1 << (XY_HASH_BITS*2))
#define XY_HASH_MASK            ((1 << XY_HASH_BITS) - 1)

#define NORTH_EDGE_MASK         0x000F
#define SOUTH_EDGE_MASK         0xF000
#define WEST_EDGE_MASK          0x1111
#define EAST_EDGE_MASK          0x8888
#define NW_CORNER_MASK          0x0001
#define NE_CORNER_MASK          0x0008
#define SW_CORNER_MASK          0x1000
#define SE_CORNER_MASK          0x8000

#define TURNS_STABLE_MASK       0x07
#define TURNS_OSCILLATING_MASK  0x38
#define SLEEPING_MASK           0x40
#define PREDICTABLE_MASK        0x80
#define ONSCREEN_MASK           0x100

/*** Macros ***/

#define IS_EMPTY(c)                 (!((c)->bnw[parity]) && !((c)->bne[parity]) && \
                                     !((c)->bsw[parity]) && !((c)->bse[parity]))
#define IS_ASLEEP(c)                ((c)->flags &   SLEEPING_MASK)
#define SET_ASLEEP(c)               ((c)->flags |=  SLEEPING_MASK)
#define SET_AWAKE(c)                ((c)->flags &= ~SLEEPING_MASK)
#define IS_PREDICTABLE(c)           ((c)->flags &   PREDICTABLE_MASK)
#define SET_PREDICTABLE(c)          ((c)->flags |=  PREDICTABLE_MASK)
#define SET_UNPREDICTABLE(c)        ((c)->flags &= ~PREDICTABLE_MASK)
#define IS_ONSCREEN(c)              ((c)->flags &   ONSCREEN_MASK)
#define SET_ONSCREEN(c)             ((c)->flags |=  ONSCREEN_MASK)
#define SET_OFFSCREEN(c)            ((c)->flags &= ~ONSCREEN_MASK)
#define TURNS_STABLE(c)             ((c)->flags &   TURNS_STABLE_MASK)
#define RESET_TURNS_STABLE(c)       ((c)->flags &= ~TURNS_STABLE_MASK)
#define INC_TURNS_STABLE(c)         ((c)->flags++)
#define DEC_TURNS_STABLE(c)         ((c)->flags--)
#define TURNS_OSCILLATING(c)        (((c)->flags & TURNS_OSCILLATING_MASK) >> 3)
#define RESET_TURNS_OSCILLATING(c)  ((c)->flags &= ~TURNS_OSCILLATING_MASK)
#define INC_TURNS_OSCILLATING(c)    ((c)->flags += 8)
#define DEC_TURNS_OSCILLATING(c)    ((c)->flags -= 8)

#define FIRST_BYTE(x)               *((uint8*)(&(x)))
#define SECOND_BYTE(x)              *((uint8*)(&(x)) + 1)

#define POPULATION_COUNT(nw,ne,sw,se)  (bitcount[FIRST_BYTE(nw)] + bitcount[SECOND_BYTE(nw)] + \
                                        bitcount[FIRST_BYTE(ne)] + bitcount[SECOND_BYTE(ne)] + \
                                        bitcount[FIRST_BYTE(sw)] + bitcount[SECOND_BYTE(sw)] + \
                                        bitcount[FIRST_BYTE(se)] + bitcount[SECOND_BYTE(se)])

#define XY_HASH(x,y)                ((((y) & XY_HASH_MASK) << XY_HASH_BITS) | ((x) & XY_HASH_MASK))

/*** Enums ***/

/* Possible drawing methods as passed to draw_cell() */
typedef enum {DRAW_SET, DRAW_UNSET}  draw_method_type;

/* Possible results from load_pattern(): success, file not found, system error (use strerror to get
 * the error message), unrecognized file format, unrecognized GLF version, unrecognized LIF
 * version, or unsupported structured XLife format detected. */
typedef enum {
    LOAD_SUCCESS,
    LOAD_SYS_ERROR,
    LOAD_UNRECOGNIZED_FORMAT,
    LOAD_BAD_GLF_VERSION,
    LOAD_BAD_LIF_VERSION,
    LOAD_STRUCTURED_XLIFE
} load_result_type;

/* Possible results from save_pattern(): success, system error (use strerror to get the error
 * message, or description out of bounds for chosen format (see LIF_DESC_MAX_LINES,
 * LIF_DESC_MAX_COLS, and RLE_DESC_MAX_COLS). */
typedef enum {
    SAVE_SUCCESS,
    SAVE_SYS_ERROR,
    SAVE_DESC_INVALID
}  save_result_type;

/*** Types ***/

/* An 8x8 cell block, with each 4x4 block (upper left, upper right, lower
 * left, lower right) stored as a uint16, and with pointers to neighbors.
 *
 * Flags:
 *   Bits 0-2: # of ticks since this cage last changed    (TURNS_STABLE)
 *   Bits 3-5: # of ticks this cage has been oscillating  (TURNS_OSCILLATING)
 *   Bit    6: sleeping?     (IS_ASLEEP)
 *   Bit    7: predictable?  (IS_PREDICTABLE)
 *   Bit    8: onscreen?     (IS_ONSCREEN)
 */
typedef struct cage_struct {
    uint32  x, y;    /* start position by blocks */
    uint16  flags;
    uint16  bnw[2], bne[2], bsw[2], bse[2];    /* element 0 on even generations */
    struct cage_struct  *north, *south, *west, *east;
    struct cage_struct  *nw, *ne, *sw, *se;
    struct cage_struct  *next, *prev;
    struct cage_struct  *hash_next, *hash_prev;
} cage_type;

/*** Externs ***/

extern uint32      tick;
extern boolean     parity;    /* on an odd (1) or even (0) tick? */
extern uint32      population;
extern uint32      num_cages;

extern char**      pattern_description;
extern int32       desc_num_lines;

/*** Public Prototypes ***/

void              clear_world(void);
void              clear_cage_list(cage_type** start);
void              draw_cell(int32 x, int32 y, draw_method_type draw_method);
boolean           find_active_cell(int32* x, int32* y);
load_result_type  load_pattern(const char* path, file_format_type* detected_format);
cage_type*        loop_cages(void);
cage_type*        loop_cages_onscreen(void);
void              mask_cage(cage_type* cage, uint16 nw_mask, uint16 ne_mask, uint16 sw_mask,
                            uint16 se_mask);
void              next_tick(boolean update_display);
void              reset_ticker(void);
save_result_type  save_pattern(const char* path, file_format_type format);
void              set_description(const char* description);
void              set_viewport(int32 xstart, int32 ystart, int32 xend, int32 yend);

#endif
