/*
 *  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 "context.h"



uint32_t version = 0;
uint32_t options = BO_GFX;
enum LayerMode mode = LM_OVERLAY;

char dname[] = "GLCube";
char desc[] = "OpenGL cube";

static uint8_t faces[6];

#define NORMAL(X) do {            \
    glEnable(GL_COLOR_LOGIC_OP);  \
    glLogicOp(GL_COPY);           \
    glBegin(GL_QUADS);            \
    X;                            \
    glDisable(GL_COLOR_LOGIC_OP); \
    glEnd();                      \
  } while (0)
#define INVERT(X) do {            \
    glEnable(GL_COLOR_LOGIC_OP);  \
    glLogicOp(GL_COPY_INVERTED);  \
    glBegin(GL_QUADS);            \
    X;                            \
    glDisable(GL_COLOR_LOGIC_OP); \
    glEnd();                      \
  } while (0)


/* many things shamefully backported from the EC_cube project */
#define ZERO 0.0
#define ONE  1.0

static float CUBE[8][3] = {
  { ONE,  ONE, -ONE},   // 1
  { ONE, -ONE, -ONE},   // 2
  {-ONE, -ONE, -ONE},
  {-ONE,  ONE, -ONE},   // 4
  {-ONE,  ONE,  ONE},
  { ONE,  ONE,  ONE},   // 6
  { ONE, -ONE,  ONE},
  {-ONE, -ONE,  ONE}
};

#define CE(x) glVertex3fv(CUBE[x-1])

#define coucou(X) face(ctx, X)


int8_t
create(Context_t *ctx)
{
  int e, v;
  const float ar = WIDTH / (float)HEIGHT;

  /* scale the cube */
  for (e = 0; e < 8; e++)
    for (v = 0; v < 3; v++)
      if (v != 1) {
        CUBE[e][v] *= ar;
      } else {
        CUBE[e][v] *= 1.15;  /* FIXME empirical tuning */
      }

  return 1;
}


static void
cube(void)
{
  glDisable(GL_TEXTURE_2D);
  glColor3f(1, 1, 1);
  glLineWidth(2);
  glBegin(GL_LINES);
  CE(1);
  CE(2);
  CE(2);
  CE(3);
  CE(3);
  CE(4);
  CE(4);
  CE(5);
  CE(5);
  CE(8);
  CE(8);
  CE(3);
  CE(1);
  CE(6);
  CE(6);
  CE(7);
  CE(7);
  CE(2);
  CE(6);
  CE(5);
  CE(5);
  CE(8);
  CE(8);
  CE(7);
  CE(6);
  CE(1);
  CE(1);
  CE(4);
  CE(4);
  CE(5);
  CE(7);
  CE(2);
  CE(3);
  CE(8);
  glEnd();
}


static inline void t1(void)
{
  glTexCoord2f(ZERO, ZERO);
}
static inline void t2(void)
{
  glTexCoord2f(ZERO, ONE);
}
static inline void t3(void)
{
  glTexCoord2f(ONE, ONE);
}
static inline void t4(void)
{
  glTexCoord2f(ONE, ZERO);
}


void
get_texture(Context_t *ctx, const uint8_t face, const uint8_t rgba)
{
  if (rgba) {
    Context_make_GL_RGBA_texture(ctx, face);
  } else {
#ifdef WITH_WEBCAM
    Context_make_GL_gray_texture(ctx, face);
#else
    Context_make_GL_RGBA_texture(ctx, face);
#endif
  }
}


static void
face(Context_t *ctx, const char f)
{
  switch (f) {
    case 0: // front face
      get_texture(ctx, ACTIVE_BUFFER, 1);
      NORMAL(
        t1(); CE(8);
        t2(); CE(5);
        t3(); CE(6);
        t4(); CE(7));
      break;

    case 1: // back face
      get_texture(ctx, ACTIVE_BUFFER, 1);
      INVERT(
        t4(); CE(2);
        t3(); CE(1);
        t2(); CE(4);
        t1(); CE(3));
      break;

    case 2: // left face
#ifdef WITH_WEBCAM
      get_texture(ctx, (ctx->webcams >= 2) ? 1 : 0, 0);
#else
      get_texture(ctx, 0, 0);
#endif
      NORMAL(
        t4(); CE(3);
        t3(); CE(4);
        t2(); CE(5);
        t1(); CE(8));
      break;

    case 3: // right face
      get_texture(ctx, 0, 0);
      if (
#ifdef WITH_WEBCAM
        ctx->webcams > 1
#else
        1
#endif
      ) {
        if (1)
          NORMAL(
            t1(); CE(7);
            t2(); CE(6);
            t3(); CE(1);
            t4(); CE(2));
      } else {
        INVERT(
          t1(); CE(7);
          t2(); CE(6);
          t3(); CE(1);
          t4(); CE(2));
      }
      break;

    case 4: // up face
      NORMAL({});
      break;
      t1();
      CE(5);
      t2();
      CE(4);
      t3();
      CE(1);
      t4();
      CE(6);
      break;

#if 0
    case 5: // bottom face
      t1();
      CE(3);
      t2();
      CE(8);
      t3();
      CE(7);
      t4();
      CE(2);
      break;
#endif
  }
}


void
textures(Context_t *ctx)
{
  char ibou; /* :) */

  for (ibou = 0; ibou < 6; ibou++) {
    coucou(ibou);  /* :) again */
  }
}


void
run(Context_t *ctx)
{
  memset(&faces, 0, 6*sizeof(uint8_t));
  textures(ctx);
  cube();
  ctx->gl_done = 1;
}
