/*
 *  Copyright 1994-2022 Olivier Girondel
 *
 *  This file is part of lebiniou.
 *
 *  lebiniou is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  lebiniou is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with lebiniou. If not, see <http://www.gnu.org/licenses/>.
 */

#include <SDL2/SDL.h>
#include <SDL2/SDL_opengl.h>
#include "biniou.h"
#include "defaults.h"
#include "commands.h"
#include "commands_key.h"

#define NO_MOUSE_CURSOR

uint32_t options = BO_NONE;
uint32_t version = 0;


static SDL_Window *window = NULL;
static Plugin_t *glcube = NULL;
static SDL_GLContext glcontext;


static void
SDL_get_event(Context_t *ctx)
{
  // TODO middle = change color, right = erase (3x3)
  SDL_Event evt;

  while (SDL_PollEvent(&evt)) {
    BKey_t key;

    switch (evt.type) {
      case SDL_KEYDOWN:
        key.val = evt.key.keysym.sym;
        key.mod = evt.key.keysym.mod;

        on_key(ctx, &key);
        break;

      case SDL_QUIT:
        json_decref(Context_process_command(ctx, CMD_APP_QUIT, NULL, BC_SDL2));
        break;

      case SDL_MOUSEMOTION:
        // printf("motion.state: %d\n", evt.motion.state);
        //printf("left: %d\n", SDL_BUTTON_LEFT);
        //printf("right: %d\n", SDL_BUTTON_RIGHT);

        switch (evt.motion.state) {
          /* TODO un switch pour le mouse drag/drop mode */
          case SDL_BUTTON_LEFT:
#ifdef WITH_GL
            ctx->params3d.gl_xe = evt.motion.x;
            ctx->params3d.gl_ye = evt.motion.y;
            Params3d_rotate_GL(&ctx->params3d);
#else
            ctx->params3d.xe = evt.motion.x;
            ctx->params3d.ye = evt.motion.y;
            Params3d_rotate(&ctx->params3d);
#endif
            // printf("left button motion @ %d %d\n",  evt.motion.x, evt.motion.y);
            break;

          case SDL_BUTTON_RIGHT+SDL_BUTTON_LEFT: /* <- WTF ? */
            // printf("right button motion @ %d %d\n",  evt.motion.x, evt.motion.y);
            set_pixel_nc(active_buffer(ctx), evt.motion.x, MAXY-evt.motion.y, 255);
            break;

          default:
            break;
        }
        break;

      case SDL_MOUSEBUTTONDOWN:
        /* printf("type= %d, button= %d\n", evt.button.type, evt.button.button); */
        switch (evt.button.button) {
          case SDL_BUTTON_LEFT:
#ifdef WITH_GL /* TODO switch GL/not-GL */
            ctx->params3d.gl_xs = evt.motion.x;
            ctx->params3d.gl_ys = evt.motion.y;
#else
            ctx->params3d.xs = evt.motion.x;
            ctx->params3d.ys = evt.motion.y;
#endif
            break;

          case SDL_BUTTON_RIGHT:
            // printf("button down @ %d %d\n",  evt.motion.x, evt.motion.y);
            set_pixel_nc(active_buffer(ctx), evt.motion.x, MAXY-evt.motion.y, 255);
            break;
        }
        break;

      case SDL_MOUSEWHEEL:
        if (evt.wheel.y > 0) { // scroll up
#ifdef WITH_GL /* TODO switch GL/not-GL */
          if (ctx->params3d.gl_fov > 1) {
            ctx->params3d.gl_fov--;
          }
          //printf("FOV: %f\n", ctx->params3d.gl_fov);
#else
          ctx->params3d.scale_factor /= 0.9;
          /* printf("scale: %d\n", ctx->params3d->scale_factor); */
#endif
        } else if (evt.wheel.y < 0) { // scroll down
#ifdef WITH_GL /* TODO switch GL/not-GL */
          ctx->params3d.gl_fov++;
          //printf("FOV: %f\n", ctx->params3d.gl_fov);
#else
          if (ctx->params3d.scale_factor > 11) {
            ctx->params3d.scale_factor *= 0.9;
          }
#endif
        }
        break;

      default:
        break;
    }
  }
}


/* "Le Superbe Rectangle Noir sur son Fond Blanc" */
void
test_gl(void)
{
  glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
  glBegin(GL_POLYGON);
  glColor3f(1,1,1);
  glVertex2f(0.1,0.1);
  glVertex2f(0.1,0.9);
  glVertex2f(0.9,0.9);
  glVertex2f(0.9,0.1);
  glEnd();
}


void
test_glbis(void)
{
  glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
  glBegin(GL_POLYGON);
  glColor3f(1,0,0.5);
  glVertex2f(0.1,0.25);
  glVertex2f(0.75,0.25);
  glVertex2f(0.75,0.75);
  glVertex2f(0.25,0.999);
  glEnd();
}


void
test_gl2(void)
{
  //  glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
  glBegin(GL_POLYGON);
  glColor3f(1,0,0);
  glVertex2f(0.0,0.0);
  glColor3f(0,1,0);
  glVertex2f(1.0, 0.0);
  glColor3f(0,0,1);
  glVertex2f(1,1);
  glColor3f(1,1,1);
  glVertex2f(0,1);
  glEnd();
}


static inline void
tex_point(const float x, const float y)
{
  glTexCoord2f(x, y);
  glVertex2f(x, y);
}


void
render(Context_t *ctx)
{
  Context_make_GL_RGBA_texture(ctx, ACTIVE_BUFFER);

  // TODO: quad texture
  glBegin(GL_TRIANGLE_STRIP);
  tex_point(0, 0);
  tex_point(1, 0);
  tex_point(1, 1);

  tex_point(0, 0);
  tex_point(0, 1);
  tex_point(1, 1);
  glEnd();
}


void
reshape(int w, int h)
{
  glViewport(0, 0, (GLsizei)w, (GLsizei)h);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}


void
run(Context_t *ctx)
{
  if (!ctx->gl_done) {
    if (ctx->force_cube && (NULL != glcube)) {
      glcube->run(ctx);
    } else {
      reshape(WIDTH, HEIGHT);
      render(ctx);
    }
  }

  SDL_GL_SwapWindow(window);

  SDL_get_event(ctx);
}


void
fullscreen(const int fs)
{
  if (fs) {
    printf("[S] Set full-screen\n");
  } else {
    printf("[S] Unset full-screen\n");
  }
  SDL_SetWindowFullscreen(window, fs ? SDL_WINDOW_FULLSCREEN : 0);
}


void
destroy(Context_t *ctx)
{
  SDL_GL_DeleteContext(glcontext);
  SDL_DestroyWindow(window);
  SDL_Quit();
}


void
switch_cursor(void)
{
  SDL_ShowCursor(SDL_ShowCursor(SDL_QUERY) ? SDL_DISABLE : SDL_ENABLE);
}


static void
init_GL(void)
{
  glShadeModel(GL_SMOOTH);
  glClearColor(0, 0, 0, 0);
  glClearDepth(1.0f);
  glEnable(GL_DEPTH_TEST);
  glDepthFunc(GL_LEQUAL);
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}


int8_t
create(Context_t *ctx)
{
  char *icon_file;
  SDL_Surface *icon = NULL;
  Uint32 colorkey;
  char *window_title;
  const int flags = SDL_WINDOW_OPENGL | SDL_GL_DOUBLEBUFFER;

  glcube = Plugins_find("GLCube");
  if (NULL != glcube) {
    printf("[i] glcube found @%p\n", glcube);
    if (NULL != glcube->create) {
      (void)glcube->create(ctx);
    }
  }

  /* First, initialize SDL's video subsystem. */
  if (!SDL_WasInit(SDL_INIT_VIDEO))
    if (SDL_Init(SDL_INIT_VIDEO) < 0) {
      /* Failed, exit. */
      xerror("Video initialization failed: %s\n", SDL_GetError());
    }

  /*
   * Now, we want to setup our requested
   * window attributes for our OpenGL window.
   * We want *at least* 5 bits of red, green
   * and blue. We also want at least a 16-bit
   * depth buffer.
   *
   * The last thing we do is request a double
   * buffered window. '1' turns on double
   * buffering, '0' turns it off.
   *
   * Note that we do not use SDL_DOUBLEBUF in
   * the flags to SDL_SetVideoMode. That does
   * not affect the GL attribute state, only
   * the standard 2D blitting setup.
   */
  SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
  SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
  SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

  /* This makes our buffer swap syncronized with the monitor's vertical refresh */
  SDL_GL_SetSwapInterval(1);

  window_title = g_strdup_printf("Le Biniou OpenGL (%dx%d)", WIDTH, HEIGHT);
  window = SDL_CreateWindow(window_title,
                            SDL_WINDOWPOS_CENTERED,
                            SDL_WINDOWPOS_CENTERED,
                            WIDTH, HEIGHT, flags);
  g_free(window_title);

  if (NULL == window) {
    xerror("Couldn't set %dx%d video mode: %s\n", WIDTH, HEIGHT, SDL_GetError());
  }

  icon_file = g_strdup_printf("%s/lebiniou.bmp", DEFAULT_DATADIR);
  icon = SDL_LoadBMP(icon_file);
  g_free(icon_file);
  colorkey = SDL_MapRGB(icon->format, 0, 0, 0);
  SDL_SetColorKey(icon, SDL_TRUE, colorkey);
  SDL_SetWindowIcon(window, icon);
  SDL_FreeSurface(icon);

  /* Create an OpenGL context associated with the window. */
  glcontext = SDL_GL_CreateContext(window);

  init_GL();

#ifdef NO_MOUSE_CURSOR
  SDL_ShowCursor(SDL_DISABLE);
#endif

  glViewport(0, 0, WIDTH, HEIGHT);

  return 1;
}
