#pragma once

#define _MAIN_HEADER
#define _CRT_SECURE_NO_WARNINGS

#include <malloc.h>
#include <math.h>
#include <memory.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>

/* Ecco 2 Addresses
 *
 * 0x11e4 - Loads Palettes
 * 0010b0de - Selection Scr Palette 
 * 0010b0fe
 * 0010b11e
 * 0010b13e
 * 0x2c000 - Dolphin sprite location
 */

/* common */

#define m_alloc(x) (x *)malloc(sizeof(x));
#define m_array(x, y) (x *)malloc(sizeof(x) * y);
#define m_parray(x, y) (x **)malloc(sizeof(x *) * y);

#define NOT_EQUAL(x, y) ((x) != (y))
#define NOT_NULL(x) ((x) != NULL)
#define NOT_ZERO(x) ((x) != 0)

#define AND_EQUAL(x, y) ((x & y) == y)
#define AND_NEQUAL(x, y) ((x & y) != y)

#define SIGNX(x) ((x) | 0xffffffffffffff00)

#define PROGRAM_NAME L"Project Black Omen"
#define PROGRAM_VERSION L"00.05162020"

#define VIEWPORT_W 640
#define VIEWPORT_H 480

#define DIAGNOSTICS_LOG "diagnostics.log"
#define MEMORY_DUMP "memory.dmp"
#define DUMP_DIR L"DUMP1"
#define DUMP_DIR2 L"DUMP2"

#define PATTERN_TABLE_SIZE 0x10000
#define MD_RAM_ADDRESS 0xffff0000
#define MD_RAM_MASK 0x0000ffff

#define MD_4BIT_PATTERN 2
#define RGBA_32BIT_COLOR 4

#define PLOT_STATE_TOTAL 1024
#define PS_STATUS_VACANT 0
#define PS_STATUS_OCCUPIED 1

#define SPRITECEL_MAX 64

#define ETD_RAW_METATILE_SIZE 0x100 * 2
#define ETD_METATILE_SIZE 0x100

#define ETD_SPRITECEL_SIZE 6
#define PC_SPRITE_CEL_SIZE 20
#define PC_SPRITE_SEQUENCE 12
#define PLZ_PALETTE_SIZE 256

#define ECCO_US_ROM "ECCO_US_OCT_1992.MD"

#define ECCO2_SAVE_STATE "ECCO_2_US_JUNE_1994.gsx"
#define ECCO2_US_ROM "ECCO_2_US_JUNE_1994.MD"

#define ECCO2_SPRITE_DOLPHIN 0x1e0248
#define ECCO2_SPRITE_SHARK 0x1e359c

#define ECCOPC_PLZ_HOME "HOME.PLZ"
#define ECCOPC_ZLM_DOLPHIN "A_DELFIN.ZLM"
#define ECCOPC_ZLM_SHARK "SHARK.ZLM"

#define ECCOPC_PALETTE_00 0xc

#define MD_STATE_CRAM 0x112
#define MD_STATE_VRAM 0x12478
#define MD_STATE_68KRAM 0x2478

#define PS_TRANSPARENTPIXEL (0x1)
#define PS_PRIORITY (0x1 << 1)

#define PS_SHADOW (0x1 << 16)
#define PS_HIGHLIGHT (0x1 << 17)
#define PS_VFLIP (0x1 << 18)
#define PS_HFLIP (0x1 << 19)

#define PS_VHFLIP (PS_VFLIP | PS_HFLIP)

#define PS_MASK_COLORINDEX 0x0000ffff
#define PS_MASK_MODE 0xffff0000
#define PS_MASK_FLIP PS_VHFLIP
#define PS_MASK_PRIORITY (PS_SHADOW | PS_HIGHLIGHT)

#define PS_DIRECT 0x00000000
#define PS_PALETTE 0x00000000

typedef char t_unicode;

typedef uint8_t t_byte;
typedef uint16_t t_word;
typedef uint32_t t_dword;
typedef uint64_t t_qword;

t_dword lbyte(t_dword x);
t_dword hbyte(t_dword x);
t_dword lword(t_dword x);
t_dword hword(t_dword x);
t_dword bswap(t_dword x);
t_dword wswap(t_dword x);

t_dword unpack_dword(t_byte *u, t_qword v);
t_word unpack_word(t_byte *u, t_qword v);

/* string.c */

t_unicode *create_string(int length);
t_unicode *hex_string(int x);
t_unicode *int_string(int x);
t_unicode *float_string(float x);

t_unicode *copy_string(const t_unicode *x);

t_unicode *copy_append_string(const t_unicode *x, const t_unicode *y);
t_unicode *append_string_x(t_unicode *x, const t_unicode *y);
t_unicode *append_string_y(const t_unicode *x, t_unicode *y);
t_unicode *append_string_xy(t_unicode *x, t_unicode *y);

t_unicode *copy_append_nl(const t_unicode *v, const t_unicode *w);
t_unicode *copy_append_nl_hex(const t_unicode *v, const t_unicode *w, int x);
t_unicode *copy_append_nl_int(const t_unicode *v, const t_unicode *w, int x);
t_unicode *copy_append_nl_float(const t_unicode *v, const t_unicode *w, float x);

t_unicode *append_nl(t_unicode *w, const t_unicode *x);
t_unicode *append_nl_hex(t_unicode *w, const t_unicode *x, int y);
t_unicode *append_nl_int(t_unicode *w, const t_unicode *x, int y);
t_unicode *append_nl_float(t_unicode *w, const t_unicode *x, float y);

int append_log(const t_unicode *filename, const t_unicode *x);
int append_log_nl(const t_unicode *filename, const t_unicode *x);
int append_log_nl_hex(const t_unicode *filename, const t_unicode *x, int y);
int append_log_nl_int(const t_unicode *filename, const t_unicode *x, int y);
int append_log_nl_float(const t_unicode *filename, const t_unicode *x, float y);

int log_int(const t_unicode *x, int y);
int log_hex(const t_unicode *x, int y);
int dump_memory(const t_byte *x, size_t size);

/* language.c */

#define LNG_RECORD_TOTAL 128

#define LNG_UNKNOWN_E       	0
#define LNG_COM_INITIALIZE_F	1
#define LNG_MEMm_alloc_F			2
#define LNG_WIN_CREATE_F		3

#define LNG_D2D_CREATE_F		4
#define LNG_D2D_RTARGET_F		5
#define LNG_D2D_BITMAP_F		6

#define LNG_FILE_EOF		    7
#define LNG_FILE_UNKNOWN_E      8
#define LNG_FILE_OPEN_F		    9
#define LNG_FILE_READ_F		    10
#define LNG_FILE_WRITE_F		11

const t_unicode *get_language_record(const t_unicode **table, int id);
char **create_language_table(void);
void release_language_table(t_unicode **language);
void get_eng_language(t_unicode **language);

/* plot.c */

typedef struct t_region
{
    int x, y, w, h;
} t_region;

typedef struct t_bitmap
{
    int palette_id;

    int w;
    int h;
    int size;

    t_dword *resource;
} t_bitmap;

typedef struct t_process_pipeline
{
   int (*process)(void *);
   t_process_pipeline *next;

} t_process_pipeline;

typedef struct t_plot_state
{
    double rotate;

    int id;
    int class_id; // Determins type of plot state (preprocessing, post, post with pre).
    int status_id; // Used to determine is a plot state is active or inactive.

    int flag;
    int mode;

    t_dword *palette;
    t_dword *source;
    t_dword *destination;

    int palette_id;
    int palette_index;
    int palette_w;
    int palette_total;

    // Need to add this into the toolset.
    int source_id;
    int source_index;
    int source_w;
    int source_h;
    int source_size;

    int destination_w;
    int destination_h;
    int destination_size;

    t_region *source_clip;
    t_region *destination_clip;

    int (*input)(t_plot_state *, int, int);
    int (*output)(t_plot_state *, int, int);

    int (*plot)(t_plot_state *);

    t_plot_state *next; // "user" defined.
    t_plot_state *_next; // next physical (free) plot_state.

} t_plot_state;

typedef struct t_plot_process
{
    t_dword *palette;
    t_dword *source;
    t_dword *destination;

    int palette_id;
    int palette_w;
    int palette_total;

    int source_w;
    int source_h;
    int source_size;

    int destination_w;
    int destination_h;
    int destination_size;

    t_plot_state *ps;
} t_plot_process;

void copy_md16_md32_palette(t_dword *palette, t_dword *md_palette, int total);

void convert32_md_rgba_palette(t_dword *palette, int color_total);
void convert32_rgba_bgra_palette(t_dword *palette, int color_total);

void unpack_md4_md32_pattern(t_byte *md4_pattern, t_dword *md32_pattern, int total);

t_region *create_region(int x, int y, int w, int h);
void set_region(t_region *region, int x, int y, int w, int h);
void clone_region(t_region *x, t_region *y);

void set_horizontal_clip(
    int x,
    int source_w,
    int destination_w,
    t_region *u,
    t_region *v);

void set_vertical_clip(
    int y,
    int source_h,
    int destination_h,
    t_region *u,
    t_region *v);

t_bitmap *create_bitmap(int w, int h);

t_plot_process *create_plot_process(void);
void plot_process_set_source(
    t_plot_process *pp,
    t_dword *source,
    int w,
    int h);

void plot_process_set_destination(
    t_plot_process *pp,
    t_dword *destination,
    int w,
    int h);

t_plot_state *create_plot_state(void);
void release_plot_state(t_plot_state *plot_state);

void clear_plot_state(t_plot_state *ps);
void clone_plot_state(t_plot_state *x, t_plot_state *y);

void plot_state_set_palette(
    t_plot_state *ps,
    t_dword *palette,
    int w,
    int total,
    int id);

void plot_state_set_source( t_plot_state *ps, t_dword *source, int w, int h);
void plot_state_set_destination(t_plot_state *ps, t_dword *destination, int w, int h);

void plot_state_set_source_region(
    t_plot_state *ps,
    int x, int y, int w, int h,
    int pattern_id);

t_plot_state *plot_state_add(t_plot_state *plot_state);

t_plot_state *plot_state_get_vacant(t_plot_state *plot_state);
t_plot_state *plot_state_add_vacant(t_plot_state *plot_state);

t_plot_state *plot_state_push_vacant(t_plot_state *plot_state);
void plot_state_pop_vacant(t_plot_state *plot_state);

void plot_state_clear_all(t_plot_state *plot_state);

void set_palette_id(t_plot_state *ps, int id);
int get_palette_color_index(t_plot_state *ps, int color_id);
void set_pattern_id(t_plot_state *ps, int id);

int ps_input_default(t_plot_state *ps, int x, int y);
int ps_input_hflip(t_plot_state *ps, int x, int y);
int ps_input_vflip(t_plot_state *ps, int x, int y);
int ps_input_vhflip(t_plot_state *ps, int x, int y);

int ps_output_default(t_plot_state *ps, int x, int y);
int ps_plot_default(t_plot_state *plot_state);
int ps_plot_direct(t_plot_state *plot_state);
int ps_plot_zlm(t_plot_state *ps);
int ps_plot_mask(t_plot_state *ps);
int ps_plot_md_indexed(t_plot_state *ps);

void plot_state_set_flip(t_plot_state *ps, int flag);

int plot_state_plot_all(t_plot_state *ps);

int ps_plot_update_palette(t_plot_state *ps);

int plot_bitmap_indexed(
    t_plot_state *ps,
    t_dword *palette,
    t_bitmap *source,
    t_bitmap *destination,
    int x, int y,
    int palette_id,
    int mode);

int plot_bitmap_direct(
    t_plot_state *ps,
    t_bitmap *source,
    t_bitmap *destination,
    int x, int y,
    int flag);

int _plot_bitmap_direct(
    t_plot_state *ps,
    t_dword *source,
    t_bitmap *destination,
    int x, int y,
    int mode);

/* etd_sprite */

typedef struct t_etd_sprite_cel
{
    int palette_id;
    int vflip;
    int hflip;

    int terminate;
    int duplicate;
    int pattern_id;

    int row_total;
    int column_total;

    int relative_x;
    int relative_y;
} t_etd_sprite_cel;

void clear_etd_sprite_cel(t_etd_sprite_cel *sp);
t_etd_sprite_cel *create_etd_sprite_cel(void);
int etd_sprite_get_cel_raw(
    t_etd_sprite_cel *sp,
    t_byte *table,
    int index);

int plot_etd_sprite(
    t_plot_process *pp,
    t_byte *resource);

/* zlm_format */

typedef struct t_zlm_sprite_cel
{
    int relative_x;
    int relative_y;
    int width;
    int height;

    // Mask width is adjusted to fit on an even byte boundry.
    int mask_width;
    int mask_column_size; // (mask_width/sizeof(byte))

    t_dword bitmap_offset;
    t_dword mask_offset;
} t_zlm_sprite_cel;

typedef struct t_zlm_header
{
    t_dword id;
    t_dword size;
    t_word sequence_total;
    t_word cel_total;

    t_dword *sequence;
} t_zlm_header;

t_zlm_sprite_cel *create_zlm_sprite_cel(void);
t_zlm_header *create_zlm_header(void);

int zlm_header_set_resource(t_zlm_header *header, t_byte *resource);
void zlm_cel_set_resource(t_zlm_sprite_cel *cel, t_byte *resource);

t_dword *plz_palette_create_resource(t_byte *resource);
t_zlm_sprite_cel *zlm_cel_create_resource(
    t_byte *resource,
    int sequence_index,
    int cel_index,
    int *sequence_total,
    int *cel_total,
    int *_result);

int zlm_plot_sprite_bitmap(
    t_plot_process *pp,
    t_zlm_sprite_cel *cel,
    t_byte *resource);

int zlm_plot_sprite_mask(
    t_plot_process *pp,
    t_zlm_sprite_cel *cel,
    t_byte *resource);

/* md_metatile */

t_dword *get_metatile_ptable(t_byte *resource, int offset, int total);

typedef struct t_md_metatile
{
    int w;
    int h;
    t_byte *raw;
} t_md_metatile;

typedef struct t_md_pattern_name
{
    int priority;
    int pattern_id;
    int vflip;
    int hflip;
    int palette_id;
} t_md_pattern_name;

t_md_metatile **create_md_metatile_table(int total);

int get_rom_md_metatile_table(
    t_md_metatile **md_metatile,
    t_byte *rom,
    t_dword *ptable,
    int total,
    int w,
    int h
);

int get_ram_md_metatile_table(
    t_md_metatile **md_metatile,
    t_byte *ram,
    t_dword *ptable,
    int total,
    int w,
    int h
);

void plot_etd_tile(
    t_plot_state *ps,
    t_plot_state *pp,
    t_md_pattern_name *pn,
    int x, int y);

void plot_etd_metatile(
    t_plot_state *ps,
    t_plot_state *pp,
    t_byte *resource,
    int position_x, int position_y,
    int w, int h);

/* ecco the dolphin level format */

typedef struct t_etd_level_cell
{
    int hflip;
    int vflip;
    int mtile_id;

} t_etd_level_cell;

t_bitmap **etd_create_mtile_table(
    t_plot_state *ps,
    t_plot_state *pp,
    t_byte *ram,
    t_byte *resource,
    t_dword offset,
    int total,
    int flag
);

int etd_generate_level_map
(
    t_plot_state *ps,
    t_plot_state *pp,
    t_byte *sav_resource,
    int level_w,
    int level_h,
    t_bitmap **mtile_table_1
);

typedef struct t_level_cell1
{
    int table_id;
    int hflip;
    int vflip;
    int mtile_id;

} t_level_cell1;

typedef struct t_level_cell2
{
    int table3_index;
    int relative_x;
    int relative_y;
    int mtile_id;

} t_level_cell2;

t_bitmap **create_mtile_table(
    t_plot_state *pp,
    t_plot_state *ps,
    t_md_metatile **md_metatile,
    int total
);

void set_level_cell2(t_level_cell2 *x, int y);

typedef struct t_ecco2_level_table
{
    t_dword id;

    t_dword foreground_w;
    t_dword foreground_h;

    t_dword background_w;
    t_dword background_h;

    t_dword palette_id;
    t_dword palette_offset;
    t_dword palette_index_offset;

    t_dword mtile_offset;
    t_dword mtile_custom_offset;

    t_dword mtile_total;
    t_dword mtile_custom_total;

    t_dword bg_nametable_offset;
    t_dword nametable_properties_offset;

    t_unicode *save_state_filename;

} t_ecco2_level_table;

t_ecco2_level_table **get_ecco2_level_table(void);

int generate_level_map
(
    t_plot_state *ps,
    t_plot_state *pp,
    t_byte *sav_resource,
    int level_w,
    int level_h,
    t_bitmap **mtile_table_1,
    t_bitmap **mtile_table_2
);

/* BEGIN */

void plot_ecco_sprite_tile(
    t_plot_state *ps,
    t_dword *source,
    t_bitmap *destination,
    t_etd_sprite_cel *cel,

    int column,
    int row,
    int pattern_index);

t_bitmap *plot_ecco_global_sprite(
    t_plot_state *ps,
    t_dword *source,
    t_byte *resource);

t_bitmap *plot_ecco_local_sprite(
    t_plot_state *ps,
    t_dword *source,
    t_byte *resource);

#include "ecco-stage.h"
#include "ecco2-stage.h"

/* END */



typedef struct t_core
{
    double rotate;

	t_unicode **language;

    
    t_byte *resource;
    int resource_size;

    t_dword *surface;
    int surface_w;
    int surface_h;

    t_plot_state *pp;
    t_plot_process *plot_process;

    t_dword palette_offset;
    t_dword pattern_offset;

    t_byte *metatile;
	t_dword *palette;
    t_dword *pattern;
    t_dword *viewport;

    int viewport_w, viewport_h;

    t_plot_state *plot_state;

} t_core;

t_core *create_core(void);
void release_core(t_core *core);


/* testing */

int core_setup(t_core *core);
int core_process(t_core *core);