#include "holly/ta_bits.hpp"
#include "holly/background.hpp"
#include "holly/holly.hpp"

#include "scene/scene.hpp"
#include "scene/options/scene.hpp"

#include "widget/button_label_tl.hpp"
#include "widget/left_aligned.hpp"
#include "widget/top_aligned.hpp"
#include "widget/label_tl.hpp"

#include "graphics_primitive.hpp"

#include "cursor.hpp"
#include "framebuffer.hpp"
#include "graphics.hpp"
#include "ta_parameter.hpp"
#include "sound.hpp"
#include "aica/aica.hpp"

#include "sh7091/sh7091_bits.hpp"

#define __length(c) ((sizeof (c)) / (sizeof (c[0])))

const static uint32_t alpha = 0xcf000000;
static int mvol = 0;
static int int_mouse_speed = 20;
static int int_stick_speed = 20;

void position_left()
{
  printf("left\n");
  int startx = holly.VO_STARTX & 0x3ff;
  int hbend = (holly.SPG_HBLANK >> 16) & 0x3ff;

  if (startx > hbend) {
    holly.VO_STARTX = startx - 1;
  }
}

void position_right()
{
  printf("right\n");
  int startx = holly.VO_STARTX & 0x3ff;
  int hbstart = (holly.SPG_HBLANK >> 0) & 0x3ff;

  if (startx < hbstart) {
    holly.VO_STARTX = startx + 1;
  }
}

void position_up()
{
  printf("up\n");
  int starty = holly.VO_STARTY & 0x3ff;
  int vbend = (holly.SPG_VBLANK >> 16) & 0x3ff;

  if (starty > vbend) {
    holly.VO_STARTY = starty - 1;
  }
}

void position_down()
{
  printf("down\n");
  int starty = holly.VO_STARTY & 0x3ff;
  int vbstart = (holly.SPG_VBLANK >> 0) & 0x3ff;

  if (starty < vbstart) {
    holly.VO_STARTY = starty + 1;
  }
}

void resolution_640x480()
{
  uint32_t sr;
  asm volatile ("stc sr,%0" : "=r" (sr));
  sr |= sh::sr::imask(15);
  asm volatile ("ldc %0,sr" : : "r" (sr));

  printf("640x480\n");
  spg_set_mode_640x480();
  framebuffer.px_width = 640;
  framebuffer.px_height = 480;
  framebuffer_init();
  graphics_scene_init(&scene::current_scene->opb_size);

  sr &= ~sh::sr::imask(15);
  asm volatile ("ldc %0,sr" : : "r" (sr));
}

void resolution_720x480()
{
  uint32_t sr;
  asm volatile ("stc sr,%0" : "=r" (sr));
  sr |= sh::sr::imask(15);
  asm volatile ("ldc %0,sr" : : "r" (sr));

  printf("720x480\n");
  spg_set_mode_720x480();
  framebuffer.px_width = 720;
  framebuffer.px_height = 480;
  framebuffer_init();
  graphics_scene_init(&scene::current_scene->opb_size);

  sr &= ~sh::sr::imask(15);
  asm volatile ("ldc %0,sr" : : "r" (sr));
}

void mvol_plus()
{
  mvol += 1;
  if (mvol > 0xf)
    mvol = 0xf;
  wait(); aica_sound.common.MVOL(mvol);
}

void mvol_minus()
{
  mvol -= 1;
  if (mvol < 0)
    mvol = 0;
  wait(); aica_sound.common.MVOL(mvol);
}

void mouse_speed_plus()
{
  int_mouse_speed += 1;
  if (int_mouse_speed > 99)
    int_mouse_speed = 99;
  cursor::mouse_speed = (float)int_mouse_speed / 20.0;
}

void mouse_speed_minus()
{
  int_mouse_speed -= 1;
  if (int_mouse_speed < 1)
    int_mouse_speed = 1;
  cursor::mouse_speed = (float)int_mouse_speed / 20.0;
}

void stick_speed_plus()
{
  int_stick_speed += 1;
  if (int_stick_speed > 99)
    int_stick_speed = 99;
  cursor::stick_speed = (float)int_stick_speed / 20.0;
}

void stick_speed_minus()
{
  int_stick_speed -= 1;
  if (int_stick_speed < 1)
    int_stick_speed = 1;
  cursor::stick_speed = (float)int_stick_speed / 20.0;
}

namespace scene::options {
  widget::button_label_tl up_button(31, 0, 30, 30, "\x18", position_up);
  widget::button_label_tl down_button(31, 0, 30, 30, "\x19", position_down);
  widget::button_label_tl right_button(30, 30, "\x1a", position_right);
  widget::button_label_tl left_button(30, 30, "\x1b", position_left);

  widget::button_label_tl _640x480_button(100, 46, "640x480", resolution_640x480);
  widget::button_label_tl _720x480_button(100, 46, "720x480", resolution_720x480);

  widget::button_label_tl mouse_minus_button(30, 30, "-", mouse_speed_minus);
  widget::button_label_tl mouse_plus_button(30, 30, "+", mouse_speed_plus);
  widget::button_label_tl stick_minus_button(30, 30, "-", stick_speed_minus);
  widget::button_label_tl stick_plus_button(30, 30, "+", stick_speed_plus);
  widget::button_label_tl master_volume_minus_button(30, 30, "-", mvol_minus);
  widget::button_label_tl master_volume_plus_button(30, 30, "+", mvol_plus);

  widget::widget spacer(30, 30);

  widget::label_tl position1_label(15 * glyph::hori_advance, 20, "screen position");
  widget::label_tl mouse_speed_label(11 * glyph::hori_advance, 20, "mouse speed");
  widget::label_tl stick_speed_label(11 * glyph::hori_advance, 20, "stick speed");
  widget::label_tl master_volume_label(13 * glyph::hori_advance, 20, "master volume");
  widget::label_tl resolution_label(10, 0, 10 * glyph::hori_advance, 20, "resolution");

  widget::widget * row1_children[] = {
    //&spacer,
    &up_button,
  };
  int row1_length = __length(row1_children);
  widget::widget * row2_children[] = {
    &left_button,
    &spacer,
    &right_button,
  };
  int row2_length = __length(row2_children);
  widget::widget * row3_children[] = {
    //    &spacer,
    &down_button,
  };
  int row3_length = __length(row3_children);
  widget::left_aligned row1(15, 0, 1, row1_children, row1_length);
  widget::left_aligned row2(15, 0, 1, row2_children, row2_length);
  widget::left_aligned row3(15, 0, 1, row3_children, row3_length);

  widget::widget * position_children[] = {
    &position1_label,
    &row1,
    &row2,
    &row3,
  };
  int position_length = __length(position_children);
  widget::top_aligned position(0, 0, 1, position_children, position_length);

  widget::widget * resolution_children[] = {
    &resolution_label,
    &_640x480_button,
    &_720x480_button,
  };
  int resolution_length = __length(resolution_children);

  widget::top_aligned resolution(0, 0, 1, resolution_children, resolution_length);

  widget::widget * video_children[] = {
    &resolution,
    &position,
  };
  int video_length = __length(video_children);
  widget::left_aligned video(0, 0, 20, video_children, video_length);

  widget::widget * mouse_speed_row1_children[] = {
    &mouse_minus_button,
    &mouse_plus_button,
  };
  int mouse_speed_row1_length = __length(mouse_speed_row1_children);
  widget::left_aligned mouse_speed_row1(0, 0, 30, mouse_speed_row1_children, mouse_speed_row1_length);

  widget::widget * mouse_speed_children[] = {
    &mouse_speed_label,
    &mouse_speed_row1,
  };
  int mouse_speed_length = __length(mouse_speed_children);

  widget::top_aligned mouse_speed(0, 0, 1, mouse_speed_children, mouse_speed_length);

  widget::widget * stick_speed_row1_children[] = {
    &stick_minus_button,
    &stick_plus_button,
  };
  int stick_speed_row1_length = __length(stick_speed_row1_children);
  widget::left_aligned stick_speed_row1(0, 0, 30, stick_speed_row1_children, stick_speed_row1_length);

  widget::widget * stick_speed_children[] = {
    &stick_speed_label,
    &stick_speed_row1,
  };
  int stick_speed_length = __length(stick_speed_children);

  widget::top_aligned stick_speed(0, 0, 1, stick_speed_children, stick_speed_length);

  widget::widget * master_volume_row1_children[] = {
    &master_volume_minus_button,
    &master_volume_plus_button,
  };
  int master_volume_row1_length = __length(master_volume_row1_children);
  widget::left_aligned master_volume_row1(0, 0, 30, master_volume_row1_children, master_volume_row1_length);

  widget::widget * master_volume_children[] = {
    &master_volume_label,
    &master_volume_row1,
  };
  int master_volume_length = __length(master_volume_children);

  widget::top_aligned master_volume(0, 0, 1, master_volume_children, master_volume_length);

  widget::widget * controls_children[] = {
    &mouse_speed,
    &stick_speed,
  };
  int controls_length = __length(controls_children);

  widget::left_aligned controls(0, 0, 15, controls_children, controls_length);

  widget::widget * top_children[] = {
    &video,
    &controls,
    &master_volume,
  };
  int top_length = __length(top_children);

  widget::top_aligned top(0, 0, 20, top_children, top_length);

  const struct scene::scene scene = {
    .ta_alloc = ta_alloc_ctrl::pt_opb::_32x4byte
              | ta_alloc_ctrl::tm_opb::no_list
              | ta_alloc_ctrl::t_opb::_32x4byte
              | ta_alloc_ctrl::om_opb::no_list
              | ta_alloc_ctrl::o_opb::no_list,
    .opb_size = {
      .opaque = 0,
      .opaque_modifier = 0,
      .translucent = 32 * 4,
      .translucent_modifier = 0,
      .punch_through = 32 * 4
    },
    .transfer = transfer,
    .interrupt = interrupt,
    .init = init,
    .done = done
  };

  void update()
  {
    for (int i = 0; i < 4; i++) {
      cursor::cursor& c = cursor::state[i];
      if (c.active && c.a) {
        widget::widget * w = top.pick(c.x, c.y);
        if (w != nullptr) {
          w->click();
        }
      }
    }
  }

  void draw_corners(ta_parameter_writer& writer)
  {
    global_polygon_translucent(writer);

    quad_type_0(writer,
                {0, 0, 0.5},
                {10, 0, 0.5},
                {10, 10, 0.5},
                {0, 10, 0.5},
                alpha | 0xff0000);

    float x = framebuffer.px_width - 10;
    float y = framebuffer.px_height - 10;

    quad_type_0(writer,
                {0 + x, 0, 0.5},
                {10 + x, 0, 0.5},
                {10 + x, 10, 0.5},
                {0 + x, 10, 0.5},
                alpha | 0x00ff00);

    quad_type_0(writer,
                {0, 0 + y, 0.5},
                {10, 0 + y, 0.5},
                {10, 10 + y, 0.5},
                {0, 10 + y, 0.5},
                alpha | 0x0000ff);

    quad_type_0(writer,
                {0 + x, 0 + y, 0.5},
                {10 + x, 0 + y, 0.5},
                {10 + x, 10 + y, 0.5},
                {0 + x, 10 + y, 0.5},
                alpha | 0xffffff);
  }

  void draw_shroud(ta_parameter_writer& writer)
  {
    global_polygon_translucent(writer);

    quad_type_0(writer,
                {0, 0, 0.5},
                {(float)framebuffer.px_width, 0, 0.5},
                {(float)framebuffer.px_width, (float)framebuffer.px_height, 0.5},
                {0, (float)framebuffer.px_height, 0.5},
                alpha | 0x202020);
  }

  void draw_values(ta_parameter_writer& writer)
  {
    transfer_global_polygon_glyph(writer);

    // mouse speed
    transfer_integer(writer, int_mouse_speed,
                     53, 182, 10.0 / 10.0,
                     2, ' ',
                     0xffffff);

    // stick speed
    transfer_integer(writer, int_stick_speed,
                     188, 182, 10.0 / 10.0,
                     2, ' ',
                     0xffffff);

    // master volume
    transfer_integer(writer, mvol,
                     52, 254, 10.0 / 10.0,
                     2, ' ',
                     0xffffff);
  }

  void transfer(ta_multiwriter& multi)
  {
    update();
    top.draw(multi);
    draw_corners(multi.op);
    draw_shroud(multi.op);
    draw_values(multi.pt);
  }

  void interrupt()
  {
  }

  void init()
  {
    /*
    background_parameter2(texture_memory_alloc.background[1].start,
                          0x110012);
    holly.VO_BORDER_COL = 0x110012;
    */
    top.freeze(15, 15);

    wait();
    mvol = 0xc;
  }

  int done()
  {
    return -1;
  }
}
