/** @file frontend_sdl.c
  SDL2 frontend for Licar.
*/

#include <stdio.h>
#include <SDL2/SDL.h>

#define LCR_SETTING_LOG_LEVEL 2
#define DATA_FILE_NAME "data"
#define LCR_LOADING_COMMAND SDL_PumpEvents();

#define LCR_FPS_GET_MS SDL_GetTicks() // uncomment for FPS measuring

#ifdef __EMSCRIPTEN__
  #define LCR_SETTING_RESOLUTION_X 640
  #define LCR_SETTING_RESOLUTION_Y 480
  #define LCR_SETTING_MUSIC 0
  #define LCR_SETTING_CAR_SHADOW 0
  #define LCR_SETTING_TEXTURE_SUBSAMPLE 4
  #define LCR_SETTING_RESOLUTION_SUBDIVIDE 2
#endif

#include "game.h"

SDL_Window *window;
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Surface *screenSurface;

uint8_t running = 1, fullscreen = 1;

#if LCR_SETTING_332_COLOR
uint8_t
#else
uint16_t
#endif
screen[LCR_SETTING_RESOLUTION_X * LCR_SETTING_RESOLUTION_Y];

FILE *musicFile = 0;
FILE *dataFile = 0;

char LCR_getNextDataFileChar(void)
{
#ifdef __EMSCRIPTEN__
  return 0;
#else
  if (!dataFile)
    return 0;

  int c = fgetc(dataFile);

  if (c == EOF)
  {
    rewind(dataFile);
    return 0;
  }

  return c;
#endif
}

void LCR_appendDataStr(const char *str)
{
#ifndef __EMSCRIPTEN__
  if (!dataFile)
    return;

  if (str == 0 || *str == 0)
    rewind(dataFile);
  else
  {
#if LCR_SETTING_LOG_LEVEL > 1
    printf("SDL: appending data \"%s\"\n",str);
#endif 

    fclose(dataFile);
    
    dataFile = fopen(DATA_FILE_NAME,"a");
    
    if (dataFile)
    {
      fprintf(dataFile,"%s",str);
      fclose(dataFile);
      dataFile = fopen(DATA_FILE_NAME,"r");
    }
  }
#else
  printf("%s",str);
#endif
}

void audioFillCallback(void *userdata, uint8_t *s, int l)
{
  if (musicFile && LCR_gameMusicOn())
  {
    if (!fread(s,1,l,musicFile))
      rewind(musicFile);
  }
  else
    for (int i = 0; i < l; ++i)
      s[i] = 128;

  for (int i = 0; i < l; ++i)
    s[i] = s[i] / 2 + LCR_gameGetNextAudioSample() / 2;
}

const uint8_t *keyboardState;

uint8_t LCR_keyPressed(uint8_t key)
{
  switch (key)
  {
    case LCR_KEY_UP: return
      keyboardState[SDL_SCANCODE_W] | keyboardState[SDL_SCANCODE_UP]; break;

    case LCR_KEY_RIGHT: return
      keyboardState[SDL_SCANCODE_D] | keyboardState[SDL_SCANCODE_RIGHT]; break;

    case LCR_KEY_DOWN: return
      keyboardState[SDL_SCANCODE_S] | keyboardState[SDL_SCANCODE_DOWN] |
      keyboardState[SDL_SCANCODE_SPACE]; break;

    case LCR_KEY_LEFT: return
      keyboardState[SDL_SCANCODE_A] | keyboardState[SDL_SCANCODE_LEFT]; break;

    case LCR_KEY_A: return
      keyboardState[SDL_SCANCODE_K] | keyboardState[SDL_SCANCODE_RETURN]; break;

    case LCR_KEY_B: return
      keyboardState[SDL_SCANCODE_L] | keyboardState[SDL_SCANCODE_ESCAPE]; break;

    default: break;
  }

  return 0;
}

#ifdef __EMSCRIPTEN__
typedef void (*em_callback_func)(void);
void emscripten_set_main_loop(
  em_callback_func func, int fps, int simulate_infinite_loop);
#endif

void LCR_sleep(uint16_t timeMs)
{
#ifndef __EMSCRIPTEN__
  SDL_Delay(timeMs);
#endif
}

void LCR_drawPixel(unsigned long index, uint16_t color)
{
  screen[index] = color;
}

void LCR_log(const char *str)
{
  printf("LOG: %s\n",str);
}

void printHelp(void)
{
  printf(
    "Licar, 3D racing game, v. " LCR_VERSION ", SDL frontend, args:\n"
    "  -h   print help and quit\n"
    "  -wN  window (N = 1) or fullscreen (N = 0)\n"
    LCR_ARG_HELP_STR);
}

void mainLoopIteration(void)
{
  SDL_Event event;

  running = 1;

  while (SDL_PollEvent(&event))
    if (event.type == SDL_QUIT)
      running = 0;

  keyboardState = SDL_GetKeyboardState(NULL);

  running &= LCR_gameStep(SDL_GetTicks());

  SDL_UpdateTexture(texture,NULL,screen,
    LCR_SETTING_RESOLUTION_X * sizeof(
#if LCR_SETTING_332_COLOR
    uint8_t
#else
    uint16_t
#endif
    ));

  SDL_RenderClear(renderer);
  SDL_RenderCopy(renderer,texture,NULL,NULL);
  SDL_RenderPresent(renderer);
}

int main(int argc, char *argv[])
{
  for (int i = 0; i < argc; ++i)
    if (argv[i][0] == '-')
      switch (argv[i][1])
      {
        case 'h':
          printHelp();
          return 0;
          break;

        case 'w':
          fullscreen = argv[i][2] == '0';
          break;

        default: break;
      }

#ifdef __EMSCRIPTEN__
  fullscreen = 0;
#else
  dataFile = fopen(DATA_FILE_NAME,"r");

  if (!dataFile)
    LCR_log("couldn't open data file");

  musicFile = fopen("assets/music","rb");

  if (!musicFile)
    fputs("could not open music file",stderr);
#endif

  LCR_log("initializing game");
  LCR_gameInit(argc,(const char **) argv);

  LCR_log("initializing SDL");
  SDL_Init(SDL_INIT_AUDIO);

  LCR_log("initializing audio");
  SDL_AudioSpec audioSpec;
  SDL_memset(&audioSpec,0,sizeof(audioSpec));
  audioSpec.callback = audioFillCallback;
  audioSpec.channels = 1;
  audioSpec.freq = 8000;
  audioSpec.format = AUDIO_U8;

#ifdef __EMSCRIPTEN__
  audioSpec.samples = 1024;
#else
  audioSpec.samples = 64;
#endif

  if (SDL_OpenAudio(&audioSpec,NULL) < 0)
    fputs("could not initialize audio",stderr);

  SDL_PauseAudio(0);

  window =
    SDL_CreateWindow("Licar", SDL_WINDOWPOS_UNDEFINED,
    SDL_WINDOWPOS_UNDEFINED, LCR_SETTING_RESOLUTION_X, LCR_SETTING_RESOLUTION_Y,
    SDL_WINDOW_SHOWN); 

  if (!window)
  {
    fputs("ERROR: couldn't create window",stderr);
    return 1;
  }

  if (fullscreen)
    SDL_SetWindowFullscreen(window,SDL_WINDOW_FULLSCREEN_DESKTOP);

  renderer = SDL_CreateRenderer(window,-1,0);

  if (!renderer)
  {
    fputs("ERROR: couldn't create renderer",stderr);
    return 1;
  }

  texture =
    SDL_CreateTexture(renderer,
#if LCR_SETTING_332_COLOR
    SDL_PIXELFORMAT_RGB332,
#else
    SDL_PIXELFORMAT_RGB565,
#endif
    SDL_TEXTUREACCESS_STATIC,
    LCR_SETTING_RESOLUTION_X,LCR_SETTING_RESOLUTION_Y);

  if (!texture)
  {
    fputs("ERROR: couldn't create texture",stderr);
    return 1;
  }

  screenSurface = SDL_GetWindowSurface(window);

  SDL_ShowCursor(0);

  SDL_PumpEvents();
  keyboardState = SDL_GetKeyboardState(NULL);

  LCR_log("starting game loop");

#ifdef __EMSCRIPTEN__
  emscripten_set_main_loop(mainLoopIteration,0,1);
#else
  while (running)
    mainLoopIteration();
#endif

  LCR_log("ending");

  if (musicFile)
    fclose(musicFile);

#ifndef __EMSCRIPTEN__
  if (dataFile)
    fclose(dataFile);
#endif

  SDL_PauseAudio(1);
  SDL_CloseAudio();

  SDL_DestroyTexture(texture);
  SDL_DestroyRenderer(renderer); 
  SDL_DestroyWindow(window); 

  LCR_gameEnd();

  return 0;
}
