#include "gpulocal.h"

#include <exec/types.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <exec/memory.h>

#include <powerup/ppcproto/intuition.h>
#include <powerup/ppclib/interface.h>
#include <powerup/ppclib/time.h>
#include <powerup/gcclib/powerup_protos.h>
#include <powerup/ppcproto/exec.h>
#include <powerup/ppcinline/graphics.h>
#include <powerup/ppcproto/dos.h>
#include <powerup/ppcproto/asl.h>

#include <cybergraphx/cybergraphics.h>
#include <powerup/ppcinline/cybergraphics.h>

#define DEF_W 256
#define DEF_H 240

/* Misc variables */
struct IntuitionBase *IntuitionBase;
struct GfxBase *GfxBase;
struct Library *CyberGfxBase;
struct Window *window = NULL;
struct Screen *screen = NULL;
int libraries_is_open = 0;
char *disp_buf = NULL, *true_buf = NULL;
int amiga_width, amiga_height, amiga_depth;
int cur_w = DEF_W, cur_h = DEF_H;

int amiga_use_screen = 0;
int joy0_ret = 0xffff;

/* CGFX variables */
unsigned long pixfmt;
unsigned char *baseaddress;
int bpr;

void open_libraries() {
  IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 39);
  if (IntuitionBase == NULL) {
    printf("Could not open intuition.library v39 or later!\n");
    exit(0);
  }

  GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 39);
  if (GfxBase == NULL) {
    CloseLibrary(IntuitionBase);
    printf("Could not open graphics.library v39 or later!\n");
    exit(0);
  }

  CyberGfxBase = OpenLibrary("cybergraphics.library", 40);
  if (CyberGfxBase == NULL) {
    CloseLibrary(GfxBase);
    CloseLibrary(IntuitionBase);
    printf("Could not open cybergraphics.library v40 or later!\n");
    exit(0);
  }

  libraries_is_open = 1;
}

void close_libraries() {
  CloseLibrary(CyberGfxBase);
  CloseLibrary(GfxBase);
  CloseLibrary(IntuitionBase);
}

void amiga_exit() {
  if (window != NULL) {
    CloseWindow(window);
    window = NULL;
  }
  if (screen != NULL) {
    CloseScreen(screen);
    screen = NULL;
  }
  if (libraries_is_open) {
    close_libraries();
    libraries_is_open = 0;
  }
}

void write_shot_ppm()
{
  static int serial = 0;

  unsigned char *buf, *dst;
  unsigned short *src;
  FILE *fp = NULL;
  char filename[32];

  buf = malloc(cur_w*cur_h*3);
  if (buf != NULL) {
    int x, y;

    src = (unsigned short*)(disp_buf+(gpu.disp.x*2)+(gpu.disp.y*1024*2));
    dst = buf;

    for (y=0; y<cur_h; y++) {
      for (x=0; x<cur_w; x++) {
        unsigned short val = *src++;
        *dst++ = ((val & 0x7c00) >> 7);
        *dst++ = ((val & 0x03e0) >> 2);
        *dst++ = ((val & 0x001f) << 3);
      }
      src+=(1024-cur_w);
    }
  } else {
    return;
  }

  sprintf(filename, "shot%04d.ppm", serial);
  fp = fopen(filename, "w");
  if (fp != NULL) {
    // Write PPM header
    fprintf(fp, "P6\n%d %d\n255\n", cur_w, cur_h);
    // Write PPM data (24bit)
    fwrite(buf, cur_w*cur_h*3, 1, fp);
    fclose(fp);
  }

  free(buf);
  serial++;
}

void win_resize(int w,int h)
{
  if (amiga_use_screen) {
    if ((w == cur_w) && (h == cur_h)) {
      return;
    }

    cur_w = w;
    cur_h = h;

    /* Clear the screen */
    FillPixelArray(&screen->RastPort, 0, 0, amiga_width, amiga_height, 0x00000000);
    return;
  }

  if (true_buf == NULL) {
    true_buf = malloc(w*h*4);
  }

  if ((w == cur_w) && (h == cur_h)) {
    return;
  }

  cur_w = w;
  cur_h = h;

  free(true_buf);
  true_buf = malloc(cur_w*cur_h*4);

  ChangeWindowBox(window, 100, 100, window->BorderLeft+cur_w+window->BorderRight, window->BorderTop+cur_h+window->BorderBottom);
}

static int keybind[16] = {
     2,  /* '2' L2 */
     10, /* '0' R2 */
     1,  /* '1' L1 */
     9,  /* '9' R1 */
     52, /* 'V' /\ */
     49, /* 'Z'  O */
     50, /* 'X'  X */
     51, /* 'C' [] */
     66, /* SELECT (TAB) */
     0,
     0,
     68, /* START (ENTER) */
     76, /* UP */
     78, /* RIGHT */
     77, /* DOWN */
     79  /* LEFT */
};

void screen_update()
{
  struct IntuiMessage *imsg = NULL;
  ULONG imCode,imClass;
  int i;

  /* Check for IDCMP messages */
  if ((imsg = (struct IntuiMessage *)GetMsg(window->UserPort)))
  {
    imClass = imsg->Class;
    imCode = imsg->Code;

    ReplyMsg((struct Message *)imsg);

    if (imClass == IDCMP_REFRESHWINDOW)
    {
      BeginRefresh(window);
      EndRefresh(window, TRUE);
    }
    else if (imClass == IDCMP_CLOSEWINDOW)
    {
      exit(0);
    }
    else if (imClass == IDCMP_RAWKEY)
    {
      if (imCode == 69) {
        FPSE_Flags |= STOP;
      }

      if (imCode == 0xdf) {
        write_shot_ppm();
      }

      for(i=0;i<16;i++) {
        int keycode = keybind[i];
        if (keycode == (imCode & 0x7f)) {
          if (imCode < 128) {
            joy0_ret &= ~(1<<i);
          } else {
            joy0_ret |= (1<<i);
          }
        }
      }
    }
  }

  if (amiga_use_screen) {
    unsigned char *src, *dst;
    int y;

    dst = baseaddress+((amiga_height-cur_h)/2)*bpr+((amiga_width-cur_w)/2)*2;
    src = disp_buf+(gpu.disp.x*2)+(gpu.disp.y*1024*2);

    for (y=0; y<cur_h; y++) {
      memcpy(dst, src, cur_w*2);
      dst += bpr;
      src += 1024*2;
    }
  } else {
    unsigned long *dst;
    unsigned short *src;
    int x, y;

    dst = (unsigned long*)true_buf;
    src = (unsigned short*)(disp_buf+(gpu.disp.x*2)+(gpu.disp.y*1024*2));

    for (y=0; y<cur_h; y++) {
      for (x=0; x<cur_w; x++) {
        unsigned short val = *src++;
        *dst++ = ((val & 0x7c00) << 9) | ((val & 0x03e0) << 6) | ((val & 0x1f) << 3);
      }
      src+=(1024-cur_w);
    }
    WritePixelArray(true_buf, 0, 0, cur_w*4, window->RPort, window->BorderLeft, window->BorderTop, cur_w, cur_h, RECTFMT_ARGB);
  }
}

void *win_createbitmap(int w,int h,int bits)
{
  open_libraries();

  if (amiga_use_screen) {
    unsigned long VideoMode;
    VideoMode = BestCModeIDTags(CYBRBIDTG_NominalWidth, 640,
                                CYBRBIDTG_NominalHeight, 480,
                                CYBRBIDTG_Depth, 15,
                                TAG_DONE);

    if (VideoMode == INVALID_ID) {
      printf("BestCModeID failed, no usefull screenmode available!\n");
      close_libraries();
      exit(0);
    }

    /* Get VideoMode information */
    amiga_width = GetCyberIDAttr(CYBRIDATTR_WIDTH, VideoMode);
    amiga_height = GetCyberIDAttr(CYBRIDATTR_HEIGHT, VideoMode);
    amiga_depth = GetCyberIDAttr(CYBRIDATTR_DEPTH, VideoMode);

    if ((amiga_width != 640) || (amiga_height != 480) || (amiga_depth != 15)) {
      printf("BestCModeID failed, no usefull screenmode available!\n");
      close_libraries();
      exit(0);
    }

    /* Open screen */
    screen = OpenScreenTags(NULL,
             SA_Width,amiga_width,
             SA_Height,amiga_height,
             SA_Depth,amiga_depth,
             SA_Quiet,TRUE,
             SA_ShowTitle,FALSE,
             SA_Type,CUSTOMSCREEN,
             SA_DisplayID,VideoMode,
             TAG_DONE);

    /* Could the screen be opened? */
    if (screen == NULL) {
      close_libraries();
      printf("Could NOT open screen!\n");
      exit(0);
    }

    /* Open window */
    window = OpenWindowTags(NULL,
             WA_CustomScreen,(int)screen,
             WA_Width,screen->Width,
             WA_Height,screen->Height,
             WA_IDCMP,IDCMP_RAWKEY,
             WA_Backdrop,TRUE,
             WA_Borderless,TRUE,
             WA_Activate,TRUE,
             TAG_DONE);

    /* Could the window be opened? */
    if (window == NULL) {
      CloseScreen(screen);
      close_libraries();
      printf("Could NOT open window!\n");
      exit(0);
    }

    /* Get screenmode information */
    UnLockBitMap(LockBitMapTags(screen->RastPort.BitMap,
                                LBMI_PIXFMT,(ULONG)&pixfmt,
                                LBMI_BASEADDRESS,(ULONG)&baseaddress,
                                LBMI_BYTESPERROW,(ULONG)&bpr,
                                TAG_END));

    /* Clear the screen */
    FillPixelArray(&screen->RastPort, 0, 0, amiga_width, amiga_height, 0x00000000);
  } else {
    window = OpenWindowTags(NULL,
                            WA_Title,"FPSE 0.08, Amiga revision 1 (000802)",
                            WA_Flags,WFLG_SIMPLE_REFRESH|WFLG_DRAGBAR|WFLG_DEPTHGADGET|WFLG_CLOSEGADGET|WFLG_RMBTRAP,
                            WA_IDCMP,IDCMP_CLOSEWINDOW|IDCMP_REFRESHWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_RAWKEY,
                            WA_Left,100,
                            WA_Top,100,
                            WA_InnerWidth,DEF_W,
                            WA_InnerHeight,DEF_H,
                            TAG_DONE);

    if (window == NULL) {
      close_libraries();
      exit(0);
    }
  }

  disp_buf = (char *)malloc(w*h*(bits/8));
  if (disp_buf == NULL) {
    close_libraries();
    exit(0);
  }

  memset(disp_buf, 0, (w*h*(bits/8)));

  atexit(amiga_exit);

  return (void *)disp_buf;
}

void win_destroy(void)
{
  /* Do nothing */
}