//This code is copyright Andrew Story (Kaslai/Aslai) All rights reserved.
//This and derivative works can be distributed as seen fit,
//so long as this notice remains unchanged and present

#pragma once
#include "global/stdinc.h"
#include "global/fileParse.h"

typedef int QUEUE;

enum {
    TUNE_SQUARE = 0,
    TUNE_TRIANGLE,
    TUNE_NOISE,
    TUNE_SINE,
    TUNE_PCM,
    TUNE_DECAY_NONE = 0,
    TUNE_DECAY_LINEAR,
    TUNE_DECAY_QUAD,
    TUNE_DECAY_EXP,
    TUNE_DECAY_INVLINEAR,
    TUNE_DECAY_INVQUAD,
    TUNE_DECAY_INVEXP,
    TUNE_DECAY_FREQ = false,
    TUNE_DECAY_VOL = true
};

struct Chiptunes{
    struct PCM{
        int samplerate; //dictates speed
        int basefreq; //should match frequency of samples
        void* samples;
        int sample_count;
        int samplepos;
        int overflowamt;
        bool finished,started;
    };
    struct Channel{
        int type;
        long long seek;
        int freq;
        int volume;
        int volumemod;
        int decaydelta;
        int decaymax;
        int decayoverflow;
        char decaytype;
        int duration; //-1 for never stopping
        int overflow;
        int prev;
        int dutycycle; //In 1/1000ths
        PCM* samples; //Only for PCM
        bool valid,playing,looping,decayeffect; //Looping is PCM only
    };
    enum QUEUETYPES{
        QUETYP_SAMPLERATE = 0,
        QUETYP_CHAN,
        QUETYP_PCM,
        QUETYP_SETSAMPLE,
        QUETYP_LBL,
        QUETYP_GO2,
        QUETYP_GOTOCOUNT,
        QUETYP_GOTOCOUNTID,
        QUETYP_GOTORAND,
        QUETYP_GOTORANDCOUNT,
        QUETYP_GOTORANDCOUNTID,
        QUETYP_RESETCOUNT,
        QUETYP_RESETCOUNTID,
        QUETYP_DECAY,
        QUETYP_DECAYTIME,
        QUETYP_START,
        QUETYP_STOP,
        QUETYP_LOOPPCM,
        QUETYP_FREQ,
        QUETYP_WAIT,
        QUETYP_DURMULT,
        QUETYP_DUTYCYCLE,
        QUETYP_VOLUME

        //Add Commands Here
    };
    struct Queue{
        int type;
        union{
            struct{
                int rate;
            }samplerate;
            struct{
                int channel;
                char* voice;
            }chan;
            struct{
                int whichpcm, samplenum, rate, freq;
                char* samples;
            }pcm;
            struct{
                int channel,which;
            }setsample;
            struct{
                char* id;
            }lbl;
            struct{
                char* id;
            }go2;
            struct{
                char* id;
                int count;
                //int curcount;
            }gotocount;
            struct{
                char* id;
                int count;
                char* countid;
                //int curcount;
            }gotocountid;
            struct{
                int num;
                char* labels;
            }gotorand;
            struct{
                int count, num;
                char* labels;
                //int curcount;
            }gotorandcount;
            struct{
                int count;
                char* id;
                int num;
                char* labels;
                //int curcount;
            }gotorandcountid;
            struct{
            }resetcount;
            struct{
                char* countid;
                int towhat;
            }resetcountid;
            struct{
                int channel, amount, toward;
                char *type, *towhat;
            }decay;
            struct{
                int samples;
            }decaytime;
            struct{
                int channel, freq, vol, duration;
            }start;
            struct{
                int channel;
            }stop;
            struct{
                int channel, whichpcm;
            }looppcm;
            struct{
                int channel, freq;
            }freq;
            struct{
                int duration;
            }wait;
            struct{
                int samples;
            }durmult;
            struct{
                int channel;
                int amount;
            }dutycycle;
            struct{
                int channel;
                int amount;
            }volume;

            //Add Commands Here

        }args;
    };

    struct QueueObj{

        std::vector<Queue> EventQueue;
        std::map<const char*, int, map_char> queue_label_map;
        std::map<const char*, int, map_char> queue_count_map;
        int queuepos;
        std::map<int,int,map_int> queuechan;
        std::vector<int> queuechantrack;
        std::map<int,int,map_int> achantrack;

        int durmult;
        int samplestowait;
        int decaytime;
        int playing;
        int execute_next_cmd();
        Chiptunes* parent;
        int volume; //In per-thousandths
    };

    struct wavhead{
        char ckid[4];
        unsigned int cksize;
        char waveid[4];
        char head[4];
        unsigned int cksize2;
        unsigned short wformattag;
        unsigned short channels;
        unsigned int samplerate;
        unsigned int datarate;
        unsigned short blocksize;
        unsigned short bps;
        char datah[4];
        unsigned int cksize3;
    };

    static void parse_samplerate( void* achip, int* args );
    static void parse_chan( void* achip, int* args );
    static void parse_pcm( void* achip, int* args );
    static void parse_setsample( void* achip, int* args );
    static void parse_lbl( void* achip, int* args );
    static void parse_goto( void* achip, int* args );
    static void parse_gotocount( void* achip, int* args );
    static void parse_gotocountid( void* achip, int* args );
    static void parse_gotorand( void* achip, int* args );
    static void parse_gotorandcount( void* achip, int* args );
    static void parse_gotorandcountid( void* achip, int* args );
    static void parse_resetcount( void* achip, int* args );
    static void parse_resetcountid( void* achip, int* args );
    static void parse_decay( void* achip, int* args );
    static void parse_decaytime( void* achip, int* args );
    static void parse_start( void* achip, int* args );
    static void parse_stop( void* achip, int* args );
    static void parse_looppcm( void* achip, int* args );
    static void parse_freq( void* achip, int* args );
    static void parse_wait( void* achip, int* args );
    static void parse_durmult( void* achip, int* args );
    static void parse_dutycycle( void* achip, int* args );
    static void parse_volume( void* achip, int* args );

    //Add Commands Here


    std::vector<Channel> channels;
    std::vector<QueueObj> Queues;
    bool modding;
    //int echolast;
    //int echodelays[22050/4];
    //int echodelaypos;
    AParse myparser;
    int Parse_Chan;

    Chiptunes();
    int sample_rate;
    int add_channel( int type );
    int rem_channel( int num );
    void* get_samples( int sample_count, void* buffer );
    void start( int channel, int frequency, int volume );
    void stop( int channel );
    int render_sample( int channel );
    void decay( int channel, int type, int goal, int delta, bool effect );
    void decay_end( int channel );
    void loop(int channel, bool should);
    void set_duration( int channel, int amount );
    Chiptunes::PCM* set_samples( int channel, void* samples, int sample_cnt, int rate, int basefreq );
    void push_queue( QUEUE num, int type, ... );
    void play_queue( QUEUE num );
    void stop_queue( QUEUE num );
    void pause_queue( QUEUE num );
    void volume_queue( QUEUE num, int volume );
    QUEUE create_queue();
    long long queue_length( QUEUE num, int unit );
    void queue_seek( QUEUE num, long long time, int unit );
    QUEUE load_kau_pt( char* fname );
    QUEUE load_kau_pt_mem( void* mem, int len );
    QUEUE load_kau_midi( char* fname );
    void save_kau_pt( QUEUE queuenum, char* fname );
    int cmd_to_str( Chiptunes::Queue que, char* buf, int len );
    void save_wav( QUEUE num, char* fname );

};



char* tune_to_string( int tun );
int string_to_tune( char* str );