#include "EventProgrammer.h"
#include "Constants.h"
#include <stdlib.h>

static Uint8 _isInitialized = 0;
static Uint16 _frameMs;
static Uint32 _delayBetweenEventsMs;
static SDL_TimerID _timer;

#define DELAY_BETWEEN_EVENTS_IN_FRAMES 10   // 3 is too short, 4 begins to work
                                            // 6 sometimes fails in Debug mode

// wait this long from the time CPU starts ...
#define SECONDS_FROM_CPU_START_TO_COPYRIGHT_MESSAGE (2.5f)
// .. plus a random value between 0.0s and this
#define RANDOM_INTERVAL_SECONDS_FROM_CPU_START_TO_COPYRIGHT_MESSAGE (0.75f)

#define MAX_EVENTS 100
static SDL_Event _loadQuoteQuoteEvents[MAX_EVENTS];
static Uint16 _loadQuoteQuoteNumEvents;
static Uint16 _eventIndex;

Uint32 _process_event(void* param) {
    SDL_PushEvent(&_loadQuoteQuoteEvents[_eventIndex++]);

    if (_eventIndex == _loadQuoteQuoteNumEvents) {
        return 0;   // we're out of events, so we cancel timer
    }

    return _delayBetweenEventsMs;   // we delay until next event
}

void eventprogrammer_run_LOAD_QUOTE_QUOTE() {
    // in case we're already generating events
    SDL_RemoveTimer(_timer);

    _eventIndex = 0;

    // insert a short, random amount of delay to simulate the fact that a human wouldn't
    // type in LOAD "" at exactly the same time after bootup
    // once LOAD "" is typed, the ZX Spectrum ROM disables interrupts because it needs
    // accurate timing for the tape loading routines
    // without this random delay, some games like Hyperaction generate the same
    // level 1 maze every single run
    double randomSeconds = ((double)rand()) / ((double)RAND_MAX);   // between 0.0 and 1.0
    randomSeconds = randomSeconds * RANDOM_INTERVAL_SECONDS_FROM_CPU_START_TO_COPYRIGHT_MESSAGE;

    double secondsAfterCPUStarts = (double)SECONDS_FROM_CPU_START_TO_COPYRIGHT_MESSAGE + randomSeconds;
    
    Uint32 insertKeystrokesDelayMs = VIDEO_DELAY_TIMER_START_MS +                          // delay before CPU actually starts
        (Uint32)((secondsAfterCPUStarts * 1000.0f / (double)DEFAULT_FRAME_MS) * _frameMs); // delay before (c) message

    SDL_TimerID _timer = SDL_AddTimer(insertKeystrokesDelayMs, (SDL_TimerCallback)_process_event, NULL);
}

// presses then releases a key
void _type_key(Uint16* eventIndex, SDL_Keycode keyCode) {
    SDL_Event e;
    e.type = SDL_KEYDOWN;
    e.key.keysym.sym = keyCode;
    _loadQuoteQuoteEvents[(*eventIndex)++] = e;
    e.type = SDL_KEYUP;
    e.key.keysym.sym = keyCode;
    _loadQuoteQuoteEvents[(*eventIndex)++] = e;
}

// types a 16bit positive integer
void _type_positive_integer(Uint16* eventIndex, Uint16 number) {
    #define COUNT 6
    char digits[COUNT];
    _itoa_s(number, digits, COUNT, 10);
    
    int index = 0;
    while (digits[index] != 0) {
        _type_key(eventIndex, digits[index++]);
    }
}

// types a random number between 1 and 65535 inclusive
void _type_random_integer(Uint16* eventIndex) {
    Uint16 number = rand();
    number = (number % 65535) + 1;
    _type_positive_integer(eventIndex, number);
}

void eventprogrammer_start(Uint16 desiredFrameMs) {
    _frameMs = desiredFrameMs;
    _delayBetweenEventsMs = _frameMs * DELAY_BETWEEN_EVENTS_IN_FRAMES;

    SDL_Event e;
    Uint16 index = 0;

    //// type "t" to get "RANDOMIZE"
    //_type_key(&index, SDLK_t);
    //
    //// type a random number, seeding randomly every run
    //_type_random_integer(&index);

    //// hold shift
    //e.type = SDL_KEYDOWN;
    //e.key.keysym.sym = SDLK_RSHIFT;
    //_loadQuoteQuoteEvents[index++] = e;

    //// type "z" to get colon
    //_type_key(&index, SDLK_z);

    //// release shift
    //e.type = SDL_KEYUP;
    //e.key.keysym.sym = SDLK_RSHIFT;
    //_loadQuoteQuoteEvents[index++] = e;

    // type "j" to get "LOAD"
    _type_key(&index, SDLK_j);

    // hold shift
    e.type = SDL_KEYDOWN;
    e.key.keysym.sym = SDLK_RSHIFT;
    _loadQuoteQuoteEvents[index++] = e;

    // press "p" to get first double quote
    _type_key(&index, SDLK_p);

    // press "p" again to get second double quote
    _type_key(&index, SDLK_p);

    // release shift
    e.type = SDL_KEYUP;
    e.key.keysym.sym = SDLK_RSHIFT;
    _loadQuoteQuoteEvents[index++] = e;

    // press ENTER
    _type_key(&index, SDLK_RETURN);

    _loadQuoteQuoteNumEvents = index;

    _isInitialized = 1;
}

void eventprogrammer_destroy() {
    if (!_isInitialized) {
        return;
    }
    SDL_RemoveTimer(_timer);
    _isInitialized = 0;
}