#include "../../ta_parameter.hpp"

#include "../../graphics_primitive.hpp"
#include "../../interpreter.hpp"

#include "notes.hpp"

const int line_column_total_advance = (3 + 2 + 2 + 1 + 2) * glyph::hori_advance;
const int line_column_width = line_column_total_advance + 6 + 3;
const int line_rows_half = 15;
const int line_rows = line_rows_half * 2 + 1;
const int line_column_height = line_rows * glyph::vert_advance + 3 + 1;

const int depth = 0.0001f;

static inline int transfer_line_index(ta_parameter_writer& writer, int line_index, int x, int y)
{
  const int dark = 0x5d646b;
  const int light = 0xa7a7a7;

  int base_color = ((line_index % 4) == 0) ? dark : light;

  int len = transfer_integer(writer, line_index, x, y, depth, 2, ' ', base_color);
  x += glyph::hori_advance * len;

  return x;
}

static inline int transfer_fill(ta_parameter_writer& writer, int count, int x, int y,
                                int base_color)
{
  for (int i = 0; i < count; i++) {
    transfer_glyph(writer, glyph::interpunct, x, y, depth, base_color);
    x += glyph::hori_advance;
  }
  return x;
}

static inline int transfer_note(ta_parameter_writer& writer, int note, int x, int y)
{
  static const char note_names[12][2] = {
    {'C', '-'},
    {'C', '#'},
    {'D', '-'},
    {'D', '#'},

    {'E', '-'},
    {'F', '-'},
    {'F', '#'},
    {'G', '-'},

    {'G', '#'},
    {'A', '-'},
    {'A', '#'},
    {'B', '-'},
  };

  int base_color = 0xffffff;

  if (note == 97) {
    transfer_glyph(writer, glyph::horizontal_bar, x, y, depth, base_color);
    x += glyph::hori_advance;
    transfer_glyph(writer, glyph::horizontal_bar, x, y, depth, base_color);
    x += glyph::hori_advance;
    transfer_glyph(writer, glyph::horizontal_bar, x, y, depth, base_color);
    x += glyph::hori_advance;
  } else if (note != 0) {
    int c0_note = note - 1;
    const char * note_name = note_names[c0_note % 12];
    int octave = c0_note / 12;
    transfer_glyph(writer, note_name[0], x, y, depth, base_color);
    x += glyph::hori_advance;
    transfer_glyph(writer, note_name[1], x, y, depth, base_color);
    x += glyph::hori_advance;
    transfer_glyph(writer, '0' + octave, x, y, depth, base_color);
    x += glyph::hori_advance;
  } else {
    x = transfer_fill(writer, 3, x, y, base_color);
  }
  return x;
}

static inline int transfer_instrument(ta_parameter_writer& writer, int instrument, int x, int y)
{
  int base_color = 0x9393ff;

  if (instrument != 0) {
    int len = transfer_hex_integer(writer, instrument, x, y, 2, glyph::interpunct, base_color);
    x += glyph::hori_advance * len;
  } else {
    x = transfer_fill(writer, 2, x, y, base_color);
  }
  return x;
}

static inline int transfer_volume_column_byte(ta_parameter_writer& writer, int volume_column_byte, int x, int y)
{
  int base_color = 0xa7c9f1;

  if (volume_column_byte != 0) {
    int len = transfer_hex_integer(writer, volume_column_byte, x, y, 2, glyph::interpunct, base_color);
    x += glyph::hori_advance * len;
  } else {
    x = transfer_fill(writer, 2, x, y, base_color);
  }
  return x;
}

static inline int effect_type_char(int effect_type)
{
  if (effect_type >= 0 && effect_type <= 9)
    return effect_type + '0';
  if (effect_type >= 10 && effect_type <= 36)
    return (effect_type - 10) + 'a';
  return 128;
}

static inline int transfer_effect_type(ta_parameter_writer& writer, int effect_type, int x, int y)
{
  int base_color = 0xffffff;

  if (effect_type != 0) {
    int c = effect_type_char(effect_type);
    transfer_glyph(writer, c, x, y, depth, base_color);
    x += glyph::hori_advance;
  } else {
    x = transfer_fill(writer, 1, x, y, base_color);
  }
  return x;
}

static inline int transfer_effect_parameter(ta_parameter_writer& writer, int effect_parameter, int x, int y)
{
  int base_color = 0x7f7f80;

  if (effect_parameter != 0) {
    int len = transfer_hex_integer(writer, effect_parameter, x, y, 2, glyph::interpunct,
                                   base_color);
    x += glyph::hori_advance * len;
  } else {
    x = transfer_fill(writer, 2, x, y, base_color);
  }
  return x;
}

static void transfer_line(ta_parameter_writer& writer, int line_index, int x, int y)
{
  using namespace interpreter;

  x += 3;
  x += 1;
  x = transfer_line_index(writer, line_index, x, y);
  x += 1;
  x += 3;

  int line_pattern_index = line_index * state.xm.number_of_channels;
  const xm_pattern_format_t * pattern = state.xm.pattern[state.pattern_index];
  for (int ch = 0; ch < state.xm.number_of_channels; ch++) {
    const xm_pattern_format_t& pf = pattern[line_pattern_index + ch];

    x += 1;
    x = transfer_note(writer, pf.note, x, y);
    x += 1;
    x = transfer_instrument(writer, pf.instrument, x, y);
    x += 1;
    x = transfer_volume_column_byte(writer, pf.volume_column_byte, x, y);
    x += 1;
    x = transfer_effect_type(writer, pf.effect_type, x, y);
    x += 1;
    x = transfer_effect_parameter(writer, pf.effect_parameter, x, y);
    x += 1;

    x += 3;
  }
}

namespace scene::tracker::notes {

void borders(ta_parameter_writer& writer, int x, int y)
{
  using namespace interpreter;

  int x0 = x;

  transfer_vertical_border(writer, x, y, line_column_height);
  x += 3 + glyph::hori_advance * 2 + 2;

  for (int ch = 0; ch < (state.xm.number_of_channels); ch++) {
    transfer_vertical_border(writer, x, y, line_column_height);
    x += line_column_width;
  }
  transfer_vertical_border(writer, x, y, line_column_height);

  transfer_horizontal_border(writer, x0, y,
                             x - x0);

  transfer_horizontal_border(writer, x0, y + line_column_height,
                             x - x0);
}

void transfer_lines(ta_parameter_writer& writer, int x, int y)
{
  using namespace interpreter;

  y += 3 + 1;

  int pattern_line_count = state.xm.pattern_note_count[state.pattern_index] / state.xm.number_of_channels;
  for (int i = 0; i < line_rows; i++) {
    int line_ix = state.line_index - line_rows_half + i;
    if (line_ix >= 0 && line_ix < pattern_line_count)
      transfer_line(writer, line_ix, x, y);
    y += glyph::vert_advance;
  }
}

void transfer_middle_line(ta_parameter_writer& writer, float x, float y)
{
  using namespace interpreter;

  int middle_width = line_column_width * (state.xm.number_of_channels) + (2 * glyph::hori_advance) + 2 + 3;
  int middle_height = glyph::vert_advance - 1;

  y += 3;

  y += glyph::vert_advance * line_rows_half;

  quad_type_0(writer,
              {x + 0, y + 0, 1.0/15.0},
              {x + 0, y + 1, 1.0/15.0},
              {x + middle_width, y + 1, 1.0/15.0},
              {x + middle_width, y + 0, 1.0/15.0},
              0x555555);

  y += 1;

  quad_type_0(writer,
              {x + 0, y + 0, 1.0/15.0},
              {x + 0, y + middle_height, 1.0/15.0},
              {x + middle_width, y + middle_height, 1.0/15.0},
              {x + middle_width, y + 0, 1.0/15.0},
              0x404040);

  y += middle_width;

  quad_type_0(writer,
              {x + 0, y + 0, 1.0/15.0},
              {x + 0, y + 1, 1.0/15.0},
              {x + middle_width, y + 1, 1.0/15.0},
              {x + middle_width, y + 0, 1.0/15.0},
              0x202020);
}

}
