#include "holly/holly.hpp"
#include "holly/core_bits.hpp"
#include "holly/texture_memory_alloc9.hpp"

#include "framebuffer.hpp"

struct framebuffer framebuffer(640, 480);

void framebuffer_init()
{
  int x_size = framebuffer.px_width;
  int y_size = framebuffer.px_height;

  // write

  holly.FB_X_CLIP = fb_x_clip::fb_x_clip_max(x_size - 1)
                  | fb_x_clip::fb_x_clip_min(0);

  holly.FB_Y_CLIP = fb_y_clip::fb_y_clip_max(y_size - 1)
                  | fb_y_clip::fb_y_clip_min(0);

  // read

  holly.FB_R_SIZE = fb_r_size::fb_modulus(1)
                  | fb_r_size::fb_y_size(y_size - 1)
                  | fb_r_size::fb_x_size((x_size * bytes_per_pixel) / 4 - 1);

  holly.FB_R_CTRL = fb_r_ctrl::vclk_div::pclk_vclk_1
                  | fb_r_ctrl::fb_depth::_565_rgb_16bit
                  | fb_r_ctrl::fb_enable;
}

void scaler_init()
{
  holly.Y_COEFF = y_coeff::coefficient_1(0x80)
                | y_coeff::coefficient_0_2(0x40);

  // in 6.10 fixed point; 0x0400 is 1x vertical scale
  holly.SCALER_CTL = scaler_ctl::vertical_scale_factor(0x0400);

  holly.FB_BURSTCTRL = fb_burstctrl::wr_burst(0x09)
                     | fb_burstctrl::vid_lat(0x3f)
                     | fb_burstctrl::vid_burst(0x39);
}

void spg_set_mode_720x480()
{
  holly.SPG_CONTROL
    = spg_control::sync_direction::output;

  holly.SPG_LOAD
    = spg_load::vcount(525 - 1)    // number of lines per field
    | spg_load::hcount(858 - 1);   // number of video clock cycles per line

  holly.SPG_HBLANK
    = spg_hblank::hbend(117)       // H Blank ending position
    | spg_hblank::hbstart(837);    // H Blank starting position

  holly.SPG_VBLANK
    = spg_vblank::vbend(40)        // V Blank ending position
    | spg_vblank::vbstart(520);    // V Blank starting position

  holly.SPG_WIDTH
    = spg_width::eqwidth(16 - 1)   // Specify the equivalent pulse width (number of video clock cycles - 1)
    | spg_width::bpwidth(794 - 1)  // Specify the broad pulse width (number of video clock cycles - 1)
    | spg_width::vswidth(3)        // V Sync width (number of lines)
    | spg_width::hswidth(64 - 1);  // H Sync width (number of video clock cycles - 1)

  holly.VO_STARTX
    = vo_startx::horizontal_start_position(117);

  holly.VO_STARTY
    = vo_starty::vertical_start_position_on_field_2(40)
    | vo_starty::vertical_start_position_on_field_1(40);

  holly.VO_CONTROL
    = vo_control::pclk_delay(22);

  holly.SPG_HBLANK_INT
    = spg_hblank_int::line_comp_val(837);

  holly.SPG_VBLANK_INT
    = spg_vblank_int::vblank_out_interrupt_line_number(21)
    | spg_vblank_int::vblank_in_interrupt_line_number(520);
}

void spg_set_mode_640x480()
{
  holly.SPG_CONTROL
    = spg_control::sync_direction::output;

  holly.SPG_LOAD
    = spg_load::vcount(525 - 1)    // number of lines per field
    | spg_load::hcount(858 - 1);   // number of video clock cycles per line

  holly.SPG_HBLANK
    = spg_hblank::hbend(126)       // H Blank ending position
    | spg_hblank::hbstart(837);    // H Blank starting position

  holly.SPG_VBLANK
    = spg_vblank::vbend(40)        // V Blank ending position
    | spg_vblank::vbstart(520);    // V Blank starting position

  holly.SPG_WIDTH
    = spg_width::eqwidth(16 - 1)   // Specify the equivalent pulse width (number of video clock cycles - 1)
    | spg_width::bpwidth(794 - 1)  // Specify the broad pulse width (number of video clock cycles - 1)
    | spg_width::vswidth(3)        // V Sync width (number of lines)
    | spg_width::hswidth(64 - 1);  // H Sync width (number of video clock cycles - 1)

  holly.VO_STARTX
    = vo_startx::horizontal_start_position(168);

  holly.VO_STARTY
    = vo_starty::vertical_start_position_on_field_2(40)
    | vo_starty::vertical_start_position_on_field_1(40);

  holly.VO_CONTROL
    = vo_control::pclk_delay(22);

  holly.SPG_HBLANK_INT
    = spg_hblank_int::line_comp_val(837);

  holly.SPG_VBLANK_INT
    = spg_vblank_int::vblank_out_interrupt_line_number(21)
    | spg_vblank_int::vblank_in_interrupt_line_number(520);
}

void core_param_init()
{
  uint32_t region_array_start = texture_memory_alloc.region_array.start;
  uint32_t isp_tsp_parameters_start = texture_memory_alloc.isp_tsp_parameters.start;
  uint32_t background_start = texture_memory_alloc.background[1].start;

  holly.REGION_BASE = region_array_start;
  holly.PARAM_BASE = isp_tsp_parameters_start;

  uint32_t background_offset = background_start - isp_tsp_parameters_start;

  holly.ISP_BACKGND_T
    = isp_backgnd_t::tag_address(background_offset / 4)
    | isp_backgnd_t::tag_offset(0)
    | isp_backgnd_t::skip(1);
  holly.ISP_BACKGND_D = _i(1.f/100000.f);

  holly.FB_W_CTRL
    = fb_w_ctrl::fb_packmode::_565_rgb_16bit;

  holly.FB_W_LINESTRIDE = (framebuffer.px_width * bytes_per_pixel) / 8;
}
