#pragma once
#include "game/game.hpp"
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
namespace Host {
  float table_R[256];
  float table_G[256];
  float table_B[256];
  ResizeCtx display_ctx = {};
  GLuint screen_tex = 0;  ///< OpenGL текстура экрана
#ifdef DISABLE_COLORMAP
  float* RGB_data = nullptr;
  void update_RGB_data();
#endif
//! растягивает OpenGL полотно
void ResizeWnd(uint mx, uint my, ResizeCtx& ctx) {
  if ( !mx || !my)
    return;
  switch (ctx.mode) {
    case ResizeMode::MULTYPLY: {
      if (mx < Game::X || my < Game::Y) {
        mx = Game::X;
        my = Game::Y;
      }
      ctx.y = Game::Y * int(my / Game::Y);
      ctx.x = ctx.y * (float(Game::X) / Game::Y);
      ctx.sx = std::max(0, int(mx) / 2 - int(ctx.x) / 2);
      ctx.sy = std::max(0, int(my) / 2 - int(ctx.y) / 2);
      ctx.ex = mx / 2 + ctx.x / 2;
      ctx.ey = my / 2 + ctx.y / 2;
      break;
    }
    case ResizeMode::Y_ONLY: {
      ctx.y = my;
      ctx.x = ctx.y * (float(Game::X) / Game::Y);
      ctx.sx = std::max(0, int(mx) / 2 - int(ctx.x) / 2);
      ctx.sy = std::max(0, int(my) / 2 - int(ctx.y) / 2);
      ctx.ex = mx / 2 + ctx.x / 2;
      ctx.ey = my / 2 + ctx.y / 2;
      break;
    }
    default:
    case ResizeMode::ALL: {
      ctx.x = ctx.ex = mx;
      ctx.y = ctx.ey = my;
      ctx.sx = ctx.sy = 0;
      break;
    }
  } // switch resize mode
  // настройка координат и проекций OpenGL:
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(0, ctx.x, ctx.y, 0, 0, 1);
  glViewport(ctx.sx, ctx.sy, ctx.x, ctx.y);
}
//! настройка индексированной палитры
void gen_palette() {
  constexpr float GRAY_MUL = 1.0f / Color::WHITE2;
	constexpr float RED_MUL = 1.0f / 31.0f;
  // генерация палитры HPW:
  for (uint i = 0; i < Color::WHITE2 + 1; ++i) {
    cauto col = i * GRAY_MUL;
    table_R[i] = table_G[i] = table_B[i] = col;
  }
  for (uint i = Color::WHITE2 + 1; i < Color::WHITE; ++i) {
    cauto col = (i - Color::WHITE2 - 1) * RED_MUL;
    table_R[i] = col;
    table_G[i] = table_B[i] = 0;
  }
  table_R[255] = table_G[255] = table_B[255] = 1;
  // показать все цвета палитры:
  #if defined(DEBUG) && defined(PALPRINT)
  a83::log("\nPalette:");
  a83::log("INDEX\tR,     G,     B");
  for (int i = 0; i < 256; ++i) {
    a83::log(a83::n2s(i) + '\t' +
      a83::n2s(table_R[i], 3) + ", " +
      a83::n2s(table_G[i], 3) + ", " +
      a83::n2s(table_B[i], 3) );
  }
  a83::log(""); // endl
  #endif
  #ifndef DISABLE_COLORMAP
  glPixelTransferi(GL_MAP_COLOR, 1); // включает использование палитры
  glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, table_R);
  glPixelMapfv(GL_PIXEL_MAP_I_TO_G, 256, table_G);
  glPixelMapfv(GL_PIXEL_MAP_I_TO_B, 256, table_B);
  #endif
}
#ifdef DISABLE_COLORMAP
#pragma message COLORMAP_DISABLED
void update_RGB_data() {
  auto table_ind = 0;
  for (int i = 0; i < Game::screen.size(); ++i) {
    cauto index = Game::screen.fast_get(i).val;
    RGB_data[table_ind + 0] = table_R[index];
    RGB_data[table_ind + 1] = table_G[index];
    RGB_data[table_ind + 2] = table_B[index];
    table_ind += 3;
  }
}	  
#endif
void ReshapeOpenGL(int w, int h, ResizeCtx& ctx) {
  using namespace a83;
  ResizeWnd(w, h, ctx);
  #ifdef DISABLE_COLORMAP
  zdelete_arr(RGB_data);
  RGB_data = new float[Game::X * Game::Y * 3];
  #endif
  if (ctx.bad())
    Game::status |= GF_NORENDER;
  else
    Game::status &= ~GF_NORENDER;
}
void initOpenGL() {
  #ifdef DISABLE_COLORMAP
  RGB_data = new float[Game::X * Game::Y * 3];
  #endif
  // OpenGL настройки
  glDisable(GL_DEPTH_TEST);
  glDepthMask(GL_FALSE);
  glDisable(GL_LIGHT0);
  glDisable(GL_LIGHTING);
  glDisable(GL_COLOR_MATERIAL);
  glDisable(GL_BLEND);
  ResizeWnd(display_ctx.x, display_ctx.y, display_ctx);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  glClearColor(0,0,0,0); // background color
  glIndexi(0);
  glGenTextures(1, &screen_tex);
  glEnable(GL_TEXTURE_2D);
  glBindTexture(GL_TEXTURE_2D, screen_tex);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
  gen_palette();
}
//! отрисовать кадр на текстуру
void DrawOpenGL(CN(ResizeCtx) ctx) {
  glClear(GL_COLOR_BUFFER_BIT);
  #ifndef DISABLE_COLORMAP
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
      Game::screen.getX(), Game::screen.getY(), 0,
      GL_COLOR_INDEX, GL_UNSIGNED_BYTE, Game::screen.get_data());
  #else
    update_RGB_data();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,
      Game::X, Game::Y, 0, GL_RGB, GL_FLOAT, RGB_data);
  #endif
  glBegin(GL_QUADS);
  glTexCoord2i(0, 0); glVertex2i(0, 0);
  glTexCoord2i(0, 1); glVertex2i(0, ctx.y);
  glTexCoord2i(1, 1); glVertex2i(ctx.x, ctx.y);
  glTexCoord2i(1, 0); glVertex2i(ctx.x, 0);
  glEnd();
  //glFinish(); // убирает статтеринг, но макс фпс снижается в 2 раза
}
//! вызывается в конце инита наследника
void post_init(int argc, char **argv) {
  initOpenGL();
  ReshapeOpenGL(display_ctx.x, display_ctx.y, display_ctx);
  Game::init(argc, argv);
}
void free() {
  Game::free();
  screen_tex = 0;
  display_ctx = {};
  #ifdef DISABLE_COLORMAP
  zdelete_arr(RGB_data);
  #endif
}
} // Host ns