/*
   Ludovic Jacomme <Ludovic.Jacomme@gmail.com>
*/

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspsdk.h>
#include <pspctrl.h>
#include <pspthreadman.h>
#include <stdlib.h>
#include <stdio.h>

#include <zlib.h>
#include <string>
#include <psppower.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>

#include "global.h"
#include "psp_fmgr.h"
#include "psp_kbd.h"
#include "psp_danzeff.h"
#include "psp_sdl.h"

#include "Cartridge.h"
#include "ProSystem.h"
#include "Database.h"
#include "Sound.h"

extern "C" {

static word display_palette16[256] = {0};

void
psp_atari_reset()
{
  prosystem_Reset();
}

int
psp_atari_load_state(char *filename)
{
  int error;

  std::string std_filename(filename);
  error = prosystem_Load(filename) ? 0 : 1;

  return error;
}

int
psp_atari_save_state(char *filename)
{
  int error;

  std::string std_filename(filename);
  error = prosystem_Save(filename, true) ? 0 : 1;

  return error;
}

int 
psp_atari_load_rom(char *filename) 
{
  std::string std_filename(filename);
  if(! cartridge_Load(std_filename)) return 1;

# if 0
    sound_Stop( );
    display_Clear( );
# endif
  database_Load(cartridge_digest);

  prosystem_Reset();

# if 0
    std::string title = std::string(CONSOLE_TITLE) + " - " + common_Trim(cartridge_title);
    SetWindowText(console_hWnd, title.c_str( ));
    console_AddRecent(filename);
    display_ResetPalette( );
    console_SetZoom(display_zoom);
    sound_Play( );
# endif
  return 0;
}

/* LUDO: */
void
atari_synchronize(void)
{
  static u32 nextclock = 1;

  if (nextclock) {
   u32 curclock;
   do {
    curclock = clock();
   } while (curclock < nextclock);

  nextclock = curclock + ((CLK_TCK * ATARI.atari_speed_limiter) / 60);
 }
}

void
psp_atari_init_palette16()
{
  uint rsize  =  6;
  uint gsize  =  5;
  uint bsize  =  6;
      
  for(uint index = 0; index < 256; index++) {
    word r = palette_data[(index * 3) + 0];
    word g = palette_data[(index * 3) + 1];
    word b = palette_data[(index * 3) + 2];
    display_palette16[index] = psp_sdl_rgb(r, g, b);
  }
}

typedef unsigned char byte;
typedef unsigned short word;
typedef unsigned int dword;

/* 

  LUDO: 16-bit HiColor (565 format) 
  see http://www.compuphase.com/graphic/scale3.htm

 */
static inline word loc_coloraverage(word a, word b)
{
  return (word)(((a ^ b) & 0xf7deU) >> 1) + (a & b);
}

static inline void 
render16bpp_X15_pixel(word *dist, const byte *b_src)
{
  word src[4];

  src[0] = display_palette16[b_src[0]];
  src[1] = display_palette16[b_src[1]];
  src[2] = display_palette16[b_src[2]];
  src[3] = display_palette16[b_src[3]];

  dist[0] = src[0];
  dist[1] = loc_coloraverage(src[0], src[1]);
  dist[2] = src[1];
  dist[3] = src[2];
  dist[4] = loc_coloraverage(src[2], src[3]);
  dist[5] = src[3];
}

static inline void 
render16bpp_X125_pixel(word *dist, const byte *b_src)
{
  word src[4];

  src[0] = display_palette16[b_src[0]];
  src[1] = display_palette16[b_src[1]];
  src[2] = display_palette16[b_src[2]];
  src[3] = display_palette16[b_src[3]];

  dist[0] = src[0];
  dist[1] = loc_coloraverage(src[0], src[1]);
  dist[2] = src[1];
  dist[3] = src[2];
  dist[4] = src[3];
}

void
psp_atari_put_image_x125()
{
  uint height = maria_visibleArea.GetHeight( ); /* 223 */
  uint width = maria_visibleArea.GetLength( ); /* 320 */

  const byte* src_vram = maria_surface + 
      ((maria_visibleArea.top - maria_displayArea.top) * maria_visibleArea.GetLength( ));

  ushort* dst_vram = psp_sdl_get_vram_addr(40, 0);
  const byte* src_vram_line = src_vram;
  ushort* dst_vram_line = dst_vram;

  int src_y;
  int dst_x;
  int dst_y;
  int count;

  for (dst_y = 0; dst_y < 272; dst_y++) {

    src_y = (dst_y * height) / 272;

    src_vram_line = &src_vram[(src_y * width)];
    dst_vram_line = dst_vram;

    count = 320;
    while (count > 0) {
      render16bpp_X125_pixel(dst_vram_line, src_vram_line);
      src_vram_line += 4;
      dst_vram_line += 5;
      count -= 4;
    }

    dst_vram += PSP_LINE_SIZE;
  }
}

static void
psp_atari_put_image_x15(void)
{
  uint height = maria_visibleArea.GetHeight( ); /* 223 */
  uint width = maria_visibleArea.GetLength( ); /* 320 */

  const byte* src_vram = maria_surface + 
      ((maria_visibleArea.top - maria_displayArea.top) * maria_visibleArea.GetLength( ));

  ushort* dst_vram = psp_sdl_get_vram_addr(0, 0);
  const byte* src_vram_line = src_vram;
  ushort* dst_vram_line = dst_vram;

  int src_y;
  int dst_x;
  int dst_y;
  int count;

  /* [ WIDTH  = 320 ] x 1.5 -> 480 */
  /* [ HEIGHT = 223 ] x 1.5 -> 334  */

  for (dst_y = 0; dst_y < 270; dst_y++) {

    src_y = (dst_y * height) / 270;

    src_vram_line = &src_vram[(src_y * width)];
    dst_vram_line = dst_vram;

    count = 320;
    while (count > 0) {
      render16bpp_X15_pixel(dst_vram_line, src_vram_line);
      src_vram_line += 4;
      dst_vram_line += 6;
      count -= 4;
    }

    dst_vram += PSP_LINE_SIZE;
  }
}

void
psp_atari_put_image_normal()
{
  uint height = maria_visibleArea.GetHeight( );
  uint length = maria_visibleArea.GetLength( );

  const byte* buffer = maria_surface + 
      ((maria_visibleArea.top - maria_displayArea.top) * maria_visibleArea.GetLength( ));

  word* surface = (word*)psp_sdl_get_vram_addr(80, 25);
  uint pitch = PSP_LINE_SIZE;

  for(uint indexY = 0; indexY < height; indexY++) {
    for(uint indexX = 0; indexX < length; indexX += 4) {
      surface[indexX + 0] = display_palette16[buffer[indexX + 0]];
      surface[indexX + 1] = display_palette16[buffer[indexX + 1]];
      surface[indexX + 2] = display_palette16[buffer[indexX + 2]];
      surface[indexX + 3] = display_palette16[buffer[indexX + 3]];
    }
    surface += pitch;
    buffer += length;
  }
}

void
psp_atari_refresh_screen()
{
  if (ATARI.psp_skip_cur_frame <= 0) {

    ATARI.psp_skip_cur_frame = ATARI.psp_skip_max_frame;

    if (ATARI.atari_render_mode == ATARI_RENDER_NORMAL) psp_atari_put_image_normal(); 
    else
    if (ATARI.atari_render_mode == ATARI_RENDER_X125  ) psp_atari_put_image_x125(); 
    else
    if (ATARI.atari_render_mode == ATARI_RENDER_MAX   ) psp_atari_put_image_x15(); 

    if (ATARI.atari_speed_limiter) {
      atari_synchronize();
    }

    if (psp_kbd_is_danzeff_mode()) {
      danzeff_moveTo(-165, -50);
      danzeff_render();
    }

    psp_sdl_flip();
  
    if (psp_screenshot_mode) {
      psp_screenshot_mode--;
      if (psp_screenshot_mode <= 0) {
        psp_sdl_save_screenshot();
        psp_screenshot_mode = 0;
      }
    }
  } else if (ATARI.psp_skip_max_frame) {
    ATARI.psp_skip_cur_frame--;
  }
}

void
psp_atari_main_loop()
{
  if (bios_Load("./7800.rom")) {
    bios_enabled = true;
  }
  /* Open default rom */
  if (psp_atari_load_rom("./default.a78")) {
    psp_sdl_exit(1);
  }

  psp_atari_init_palette16();

  while (1) {
    psp_update_keys();

    if(prosystem_active && !prosystem_paused) {
      prosystem_ExecuteFrame(ATARI.keyboard_data);
      sound_Store();
      psp_atari_refresh_screen();
    }
  }
}

};
