// MIT License

// Copyright (c) 2017 Vadim Grigoruk @nesbox // grigoruk@gmail.com

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

#pragma once

#include "tic80.h"
#include "defines.h"

#define TIC_VRAM_SIZE (16*1024) //16K
#define TIC_RAM_SIZE (TIC_VRAM_SIZE+80*1024) //16K+80K
#define TIC_FONT_WIDTH 6
#define TIC_FONT_HEIGHT 6
#define TIC_ALTFONT_WIDTH 4
#define TIC_PALETTE_BPP 4
#define TIC_PALETTE_SIZE (1 << TIC_PALETTE_BPP)
#define TIC_PALETTES 2
#define TIC_SPRITESIZE 8

#define TIC_DEFAULT_BIT_DEPTH 4
#define TIC_DEFAULT_BLIT_MODE 2

#define TIC80_OFFSET_LEFT ((TIC80_FULLWIDTH-TIC80_WIDTH)/2)
#define TIC80_OFFSET_TOP ((TIC80_FULLHEIGHT-TIC80_HEIGHT)/2)

#define BITS_IN_BYTE 8
#define TIC_BANK_SPRITES (1 << BITS_IN_BYTE)
#define TIC_SPRITE_BANKS 2
#define TIC_FLAGS (TIC_BANK_SPRITES * TIC_SPRITE_BANKS)
#define TIC_SPRITES (TIC_BANK_SPRITES * TIC_SPRITE_BANKS)

#define TIC_SPRITESHEET_SIZE 128
#define TIC_SPRITESHEET_COLS (TIC_SPRITESHEET_SIZE / TIC_SPRITESIZE)

#define TIC_MAP_ROWS (TIC_SPRITESIZE)
#define TIC_MAP_COLS (TIC_SPRITESIZE)
#define TIC_MAP_SCREEN_WIDTH (TIC80_WIDTH / TIC_SPRITESIZE)
#define TIC_MAP_SCREEN_HEIGHT (TIC80_HEIGHT / TIC_SPRITESIZE)
#define TIC_MAP_WIDTH (TIC_MAP_SCREEN_WIDTH * TIC_MAP_ROWS)
#define TIC_MAP_HEIGHT (TIC_MAP_SCREEN_HEIGHT * TIC_MAP_COLS)

#define TIC_PERSISTENT_SIZE (1024/sizeof(s32)) // 1K
#define TIC_SAVEID_SIZE 64

#define TIC_SOUND_CHANNELS 4
#define TIC_STEREO_CHANNELS 2
#define SFX_TICKS 30
#define SFX_COUNT_BITS 6
#define SFX_COUNT (1 << SFX_COUNT_BITS)
#define SFX_SPEED_BITS 3
#define SFX_DEF_SPEED (1 << SFX_SPEED_BITS)

#define NOTES 12
#define OCTAVES 8
#define MAX_VOLUME 15
#define MUSIC_PATTERN_ROWS 64
#define MUSIC_PATTERNS 60
#define MUSIC_CMD_BITS 3
#define TRACK_PATTERN_BITS 6
#define TRACK_PATTERN_MASK ((1 << TRACK_PATTERN_BITS) - 1)
#define TRACK_PATTERNS_SIZE (TRACK_PATTERN_BITS * TIC_SOUND_CHANNELS / BITS_IN_BYTE)
#define MUSIC_FRAMES 16
#define MUSIC_TRACKS_BITS 3
#define MUSIC_TRACKS (1 << MUSIC_TRACKS_BITS)
#define DEFAULT_TEMPO 150
#define DEFAULT_SPEED 6
#define PITCH_DELTA 128
#define NOTES_PER_BEAT 4
#define PATTERN_START 1
#define MUSIC_SFXID_LOW_BITS 5
#define WAVES_COUNT 16
#define WAVE_VALUES 32
#define WAVE_VALUE_BITS 4
#define WAVE_MAX_VALUE ((1 << WAVE_VALUE_BITS) - 1)
#define WAVE_SIZE (WAVE_VALUES * WAVE_VALUE_BITS / BITS_IN_BYTE)

#define TIC_BANKSIZE_BITS 16
#define TIC_BANK_SIZE (1 << TIC_BANKSIZE_BITS) // 64K
#define TIC_BANK_BITS 3
#define TIC_BANKS (1 << TIC_BANK_BITS)

#define TIC_CODE_SIZE (TIC_BANK_SIZE * TIC_BANKS)

#define TIC_GAMEPADS (sizeof(tic80_gamepads) / sizeof(tic80_gamepad))

#define SFX_NOTES {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"}
#define TIC_FONT_CHARS 256

enum
{
    NoteNone = 0,
    NoteStop,
    NoteNone2,
    NoteNone3,
    NoteStart,
};

typedef enum
{
    tic_color_black,
    tic_color_purple,
    tic_color_red,
    tic_color_orange,
    tic_color_yellow,
    tic_color_light_green,
    tic_color_green,
    tic_color_dark_green,
    tic_color_dark_blue,
    tic_color_blue,
    tic_color_light_blue,
    tic_color_cyan,
    tic_color_white,
    tic_color_light_grey,
    tic_color_grey,
    tic_color_dark_grey,
} tic_color;

typedef enum
{
    tic_no_flip = 0b00,
    tic_horz_flip = 0b01,
    tic_vert_flip = 0b10,
} tic_flip;

typedef enum
{
    tic_no_rotate,
    tic_90_rotate,
    tic_180_rotate,
    tic_270_rotate,
} tic_rotate;

typedef enum
{
    tic_bpp_4 = 4,
    tic_bpp_2 = 2,
    tic_bpp_1 = 1,
} tic_bpp;

typedef struct
{
    u8 start:4;
    u8 size:4;
} tic_sound_loop;

typedef struct
{
    
    struct
    {
        u8 volume:4;
        u8 wave:4;
        u8 chord:4;
        s8 pitch:4;
    } data[SFX_TICKS];

    struct
    {
        u8 octave:3;
        u8 pitch16x:1; // pitch factor
        s8 speed:SFX_SPEED_BITS;
        u8 reverse:1; // chord reverse
        u8 note:4;
        u8 stereo_left:1;
        u8 stereo_right:1;
        u8 temp:2;
    };

    union
    {
        struct
        {
            tic_sound_loop wave;
            tic_sound_loop volume;
            tic_sound_loop chord;
            tic_sound_loop pitch;
        };

        tic_sound_loop loops[4];
    };

} tic_sample;

typedef struct
{
    union
    {
        struct
        {
            s8 wave;
            s8 volume;
            s8 chord;
            s8 pitch;
        };

        s8 data[4];
    };
} tic_sfx_pos;

typedef struct
{
    u8 data[WAVE_SIZE];
}tic_waveform;

typedef struct
{
    tic_waveform items[WAVES_COUNT];
} tic_waveforms;

#define MUSIC_CMD_LIST(macro)                                               \
    macro(empty,    0, "")                                                  \
    macro(volume,   M, "master volume for LEFT=X / RIGHT=Y channel")        \
    macro(chord,    C, "play chord, X=3 Y=7 plays +0,+3,+7 notes")          \
    macro(jump,     J, "jump to FRAME=X / BEAT=Y")                          \
    macro(slide,    S, "slide to note (legato) with TICKS=XY")              \
    macro(pitch,    P, "finepitch UP/DOWN=XY-" DEF2STR(PITCH_DELTA))        \
    macro(vibrato,  V, "vibrato with PERIOD=X and DEPTH=Y")                 \
    macro(delay,    D, "delay triggering a note with TICKS=XY")

typedef enum
{
#define ENUM_ITEM(name, ...) tic_music_cmd_##name,
    MUSIC_CMD_LIST(ENUM_ITEM)
#undef ENUM_ITEM

    tic_music_cmd_count
} tic_music_command;

typedef struct
{
    u8 note     :4;
    u8 param1   :4;
    u8 param2   :4;
    u8 command  :MUSIC_CMD_BITS; // tic_music_command
    u8 sfxhi    :1;
    u8 sfxlow   :MUSIC_SFXID_LOW_BITS;
    u8 octave   :3;

} tic_track_row;

typedef struct
{
    tic_track_row rows[MUSIC_PATTERN_ROWS];

} tic_track_pattern;

typedef struct
{
    u8 data[MUSIC_FRAMES * TRACK_PATTERNS_SIZE]; // sfx - 6bits per channel = 24 bit

    s8 tempo; // delta value, rel to 120 bpm * 10 [32-255]
    u8 rows; // delta value, rel to 64 rows, can be [1-64]
    s8 speed; // delta value, rel to 6 [1-31]

} tic_track;

typedef struct
{
    tic_track_pattern data[MUSIC_PATTERNS];
} tic_patterns;

typedef struct
{
    tic_track data[MUSIC_TRACKS];
} tic_tracks;

typedef struct
{
    tic_sample data[SFX_COUNT];
} tic_samples;

typedef struct
{
    tic_waveforms waveforms;
    tic_samples samples;
}tic_sfx;

typedef struct
{
    tic_patterns patterns;
    tic_tracks tracks;
}tic_music;

typedef enum
{
    tic_music_stop,
    tic_music_play_frame,
    tic_music_play,
} tic_music_status;

typedef struct
{
    struct
    {
        s8 track;
        s8 frame;
        s8 row;
    } music;
    
    struct
    {
        u8 music_loop:1;
        u8 music_status:2; // enum tic_music_status
        u8 music_sustain:1;
        u8 unknown:4;
    } flag;

} tic_music_state;

typedef union
{
    struct
    {
        u8 left1:4;
        u8 right1:4;

        u8 left2:4;
        u8 right2:4;

        u8 left3:4;
        u8 right3:4;

        u8 left4:4;
        u8 right4:4;
    };

    u32 data;
} tic_stereo_volume;

typedef struct
{
    struct
    {
        u16 freq:12;
        u16 volume:4;
    };

    tic_waveform waveform;
} tic_sound_register;

typedef struct
{
    u8 data[TIC_MAP_WIDTH * TIC_MAP_HEIGHT];
} tic_map;

typedef struct
{
    u8 data[TIC_SPRITESIZE * TIC_SPRITESIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];
} tic_tile;

typedef union
{
    char data[TIC_CODE_SIZE];
} tic_code;

typedef struct
{
    u8 r;
    u8 g;
    u8 b;
} tic_rgb;

typedef union
{
    tic_rgb colors[TIC_PALETTE_SIZE];

    u8 data[TIC_PALETTE_SIZE * sizeof(tic_rgb)];
} tic_palette;

typedef struct
{
    tic_tile data[TIC_BANK_SPRITES];
} tic_tiles, tic_sprites;

typedef struct
{
    u8 data[TIC_FLAGS];
} tic_flags;

typedef struct
{
    tic_palette scn;
    tic_palette ovr;
} tic_palettes;

typedef struct
{
    u8 data[TIC80_WIDTH * TIC80_HEIGHT * TIC_PALETTE_BPP / BITS_IN_BYTE];
} tic_screen;

typedef struct
{
    tic_screen      screen;
    tic_tiles       tiles;
    tic_sprites     sprites;
    tic_map         map;
    tic_sfx         sfx;
    tic_music       music;
    tic_flags       flags;
    tic_palettes    palette;
} tic_bank;

typedef struct
{
    union
    {
        tic_bank bank0;
        tic_bank banks[TIC_BANKS];
    };

    tic_code code;    

} tic_cartridge;

typedef struct
{
    u8 data[TIC_FONT_CHARS * BITS_IN_BYTE];
} tic_font;

typedef union
{
    struct
    {
        tic_screen screen;
        tic_palette palette;
        u8 mapping[TIC_PALETTE_SIZE * TIC_PALETTE_BPP / BITS_IN_BYTE];

        struct
        {
            union
            {
                u8 colors;
                            
                struct
                {
                    u8 border:TIC_PALETTE_BPP;
                    u8 tmp:TIC_PALETTE_BPP;
                };
            };

            struct
            {
                s8 x;
                s8 y;
            } offset;

            struct
            {
                u8 sprite:7;
                bool system:1;
            } cursor;

        } vars;

        struct
        {
            u8 segment:4;
            u8 reserved:4;
        } blit;

        u8 reserved[3];
    };
    
    u8 data[TIC_VRAM_SIZE];
} tic_vram;

typedef struct
{
    u32 data[TIC_PERSISTENT_SIZE];
} tic_persistent;

typedef union
{
    struct
    {
        tic_vram            vram;
        tic_tiles           tiles;
        tic_sprites         sprites;
        tic_map             map;
        tic80_input         input;
        tic_sfx_pos         sfxpos[TIC_SOUND_CHANNELS];
        tic_sound_register  registers[TIC_SOUND_CHANNELS];
        tic_sfx             sfx;
        tic_music           music;
        tic_music_state     music_state;
        tic_stereo_volume   stereo;
        tic_persistent      persistent;
        tic_flags           flags;
        tic_font            font;

        u8 free;
    };

    u8 data[TIC_RAM_SIZE];

} tic_ram;

typedef enum
{
    tic_key_unknown,

    tic_key_a,
    tic_key_b,
    tic_key_c,
    tic_key_d,
    tic_key_e,
    tic_key_f,
    tic_key_g,
    tic_key_h,
    tic_key_i,
    tic_key_j,
    tic_key_k,
    tic_key_l,
    tic_key_m,
    tic_key_n,
    tic_key_o,
    tic_key_p,
    tic_key_q,
    tic_key_r,
    tic_key_s,
    tic_key_t,
    tic_key_u,
    tic_key_v,
    tic_key_w,
    tic_key_x,
    tic_key_y,
    tic_key_z,

    tic_key_0,
    tic_key_1,
    tic_key_2,
    tic_key_3,
    tic_key_4,
    tic_key_5,
    tic_key_6,
    tic_key_7,
    tic_key_8,
    tic_key_9,

    tic_key_minus,
    tic_key_equals,
    tic_key_leftbracket,
    tic_key_rightbracket,
    tic_key_backslash,
    tic_key_semicolon,
    tic_key_apostrophe,
    tic_key_grave,
    tic_key_comma,
    tic_key_period,
    tic_key_slash,
    
    tic_key_space,
    tic_key_tab,

    tic_key_return,
    tic_key_backspace,
    tic_key_delete,
    tic_key_insert,

    tic_key_pageup,
    tic_key_pagedown,
    tic_key_home,
    tic_key_end,
    tic_key_up,
    tic_key_down,
    tic_key_left,
    tic_key_right,

    tic_key_capslock,
    tic_key_ctrl,
    tic_key_shift,
    tic_key_alt,

    tic_key_escape,
    tic_key_f1,
    tic_key_f2,
    tic_key_f3,
    tic_key_f4,
    tic_key_f5,
    tic_key_f6,
    tic_key_f7,
    tic_key_f8,
    tic_key_f9,
    tic_key_f10,
    tic_key_f11,
    tic_key_f12,

    ////////////////

    tic_keys_count
} tic_keycode;

typedef enum
{
    tic_mouse_left,
    tic_mouse_middle,
    tic_mouse_right,
} tic_mouse_btn;

typedef enum
{
    tic_cursor_arrow,
    tic_cursor_hand,
    tic_cursor_ibeam,
} tic_cursor;
