#include <stdio.h>
#include "swis.h"
#include <kernel.h>

// SWI Registers
extern _kernel_swi_regs inreg;
extern _kernel_swi_regs outreg;

extern unsigned char *buffer;
extern unsigned char *fontbuffer;
extern unsigned char *tilebuffer;

int screen = 1;

extern void screen_nobuffer();
// Loads sprite file into buffer
void load_sprites(char* filename, unsigned char **buffername)
{
  int length;

  // Attempt to get file info
  inreg.r[0] = 5;
  inreg.r[1] = (int) filename;
  _kernel_swi(OS_File,&inreg,&outreg);

  // Length will be in R4 if it exists
  length = outreg.r[4];

  // If it's <1 it's fil not found
  if(outreg.r[0] < 1)
  {         
    printf("Sprite file %s not found, object type is %i and size is %i\n",filename,outreg.r[0],outreg.r[4]);
    exit(0);
  }

  // Stops us trying to mallocsomething mad if file is too big.
  if(length > 200000)
  {
    screen_nobuffer();
    printf("Sprite file %s seems unreasonably large at %i bytes, object type is %i\n",filename, length, outreg.r[0]);
    exit(0);
  }

  // Attempt malloc, die if we cant
  *buffername = (unsigned char *) malloc(length + 4);

  if(buffername==NULL)
  {
    screen_nobuffer();
    printf("Couldn't malloc %i bytes for sprite buffer\n",length);
    exit(0);
  }

  // Store size and other info as required for SpriteOp 9 to init sprite area
  *(unsigned int *)*buffername = length + 4;
  *(unsigned int *)(*buffername + 4) = 16;
  inreg.r[0] = 256+9;
  inreg.r[1] = (unsigned int) *buffername;
  _kernel_swi(OS_SpriteOp,&inreg,&outreg);

  // Load sprite file into buffer
  inreg.r[0] = 256+10;
  inreg.r[1] = (int) *buffername;
  inreg.r[2] = (int) filename;
  _kernel_swi(OS_SpriteOp,&inreg,&outreg);
}

void display_mode(int mode)
{
  int pitch;
  int height=256; // TODO: This shouldn't be here!

  // OS_ScreenMode doesn't seem to work in 3.10
  inreg.r[0] = 22;
  _kernel_swi(OS_WriteC,&inreg,&outreg);
  inreg.r[0] = mode;
  _kernel_swi(OS_WriteC,&inreg,&outreg);

  // The SDL library does this if double buffering is enabled - seems
  // to be what makes it work - although other examples dont have this
  inreg.r[0] = -1;
  inreg.r[1] = 6;
  _kernel_swi(OS_ReadModeVariable, &inreg, &outreg);
  pitch = outreg.r[2];

  inreg.r[0] = 2; /* Screen area */
  _kernel_swi(OS_ReadDynamicArea, &inreg, &outreg);
  inreg.r[1] = (pitch * height * 2) - outreg.r[1];
  if (_kernel_swi(OS_ChangeDynamicArea, &inreg, &outreg) != NULL)
  {
    screen_nobuffer();
    while(1)
    printf("Couldn't OS_ChangeDynamicArea\n");
    exit(0);
  }
}

void draw_dotted_line(int x1,int y1,int x2,int y2)
{
  inreg.r[0] = 4 + 16;
  inreg.r[1] = x1;
  inreg.r[2] = y1;
  _kernel_swi(OS_Plot,&inreg,&outreg);
  inreg.r[0] = 5 + 16;
  inreg.r[1] = x2;
  inreg.r[2] = y2;
  _kernel_swi(OS_Plot,&inreg,&outreg);
}

void draw_line(int x1,int y1,int x2,int y2)
{
  inreg.r[0] = 4;
  inreg.r[1] = x1;
  inreg.r[2] = y1;
  _kernel_swi(OS_Plot,&inreg,&outreg);
  inreg.r[0] = 5;
  inreg.r[1] = x2;
  inreg.r[2] = y2;
  _kernel_swi(OS_Plot,&inreg,&outreg);
}

void draw_rectangle(int x1,int y1,int x2,int y2)
{
  draw_line(x1,y1,x1,y2);
  draw_line(x1,y1,x2,y1);
  draw_line(x2,y2,x2,y1);
  draw_line(x2,y2,x1,y2);
}

void draw_sprite(char* spritename,int x, int y)
{
  // SpriteOp 34 to put sprite at a location
  inreg.r[0] = 256+34;
  inreg.r[1] = (int) buffer;
  inreg.r[2] = (int) spritename;
  inreg.r[3] = x;
  inreg.r[4] = y;
  inreg.r[5] = 8; // GCOL dest=source and sprite mask
  _kernel_swi(OS_SpriteOp,&inreg,&outreg);
}

void draw_letter(char* spritename,int x, int y)
{
  // SpriteOp 34 to put sprite at a location
  inreg.r[0] = 256+34;
  inreg.r[1] = (int) fontbuffer;
  inreg.r[2] = (int) spritename;
  inreg.r[3] = x;
  inreg.r[4] = y;
  inreg.r[5] = 8;
  _kernel_swi(OS_SpriteOp,&inreg,&outreg);
}

void draw_tile(char* spritename,int x, int y)
{
  // SpriteOp 34 to put sprite at a location
  inreg.r[0] = 256+34;
  inreg.r[1] = (int) tilebuffer;
  inreg.r[2] = (int) spritename;
  inreg.r[3] = x;
  inreg.r[4] = y;
  inreg.r[5] = 0;
  _kernel_swi(OS_SpriteOp,&inreg,&outreg);
}

void draw_spritetext(char* text, int x, int y)
{
  int currentx;
  int currenty;
  int i;
  char str[2] = "\0";
  currentx = x;
  currenty = y;
  for(i = 0; i < strlen(text); i++)
  {
    str[0] = text[i];

    if((text[i] != ' ') && (text[i] != '\n') && (text[i] != '~'))
      draw_letter(str,currentx,currenty);

    switch (text[i])
    {
      case 't':
      case 'i':
      case '.':
      case ',':
      case 'y':
        currentx += 12 + 4;
        break;
      case '\'':
        currentx += 8 + 4;
        break;
      case 'm':
        currentx += 22 + 4;
        break;
      case 'q':
      case 'w':
        currentx += 20 + 4;
        break;
      case ' ':
        currentx += 8 + 4;
        break;
      case '=':
        currentx += 22 + 2;
        break;
      case '{':
      case '}':
        currentx += 26;
        break;
      case '~':
        currentx -= 2;
        break;
      default:
        currentx += 16 + 4;
    }

    if(text[i] == '\n')
    {
      currentx = x;
      currenty -= 36;
    }
  }
}

void draw_text(char* text, int x, int y, int fonthandle)
{
  inreg.r[0] = fonthandle;
  inreg.r[1] = (int) text;
  inreg.r[2] = 1 << 4;
  inreg.r[3] = x;
  inreg.r[4] = y;
  inreg.r[5] = 0;
  inreg.r[6] = 0;
  inreg.r[7] = 0;
  _kernel_swi(Font_Paint,&inreg,&outreg);
}

int font_find(char* font, int height, int width)
{
  inreg.r[0] = 0;
  inreg.r[1] = (int) font;
  inreg.r[2] = height*16;
  inreg.r[3] = width*16;
  inreg.r[4] = 0;
  inreg.r[5] = 0;
  _kernel_swi(Font_FindFont,&inreg,&outreg);

  return outreg.r[0];
}

void font_colour(int fg, int bg, int fonthandle)
{
  inreg.r[0] = fonthandle;
  inreg.r[1] = bg;
  inreg.r[2] = fg;
  inreg.r[3] = 14;
  _kernel_swi(ColourTrans_SetFontColours, &inreg, &outreg);
}

void graphics_colour(int setcolour)
{
  inreg.r[0] = setcolour;
  inreg.r[1] = -1;
  inreg.r[2] = 0;
  _kernel_swi(ColourTrans_ReturnColourNumberForMode,&inreg,&outreg);
  inreg.r[0] = 0;
  inreg.r[1] = outreg.r[0];
  _kernel_swi(OS_SetColour,&inreg,&outreg);
}
void screen_flipbuffer()
{
   // Hardware
   inreg.r[0] = 113;
   inreg.r[1] = screen+1;
  _kernel_swi(OS_Byte,&inreg,&outreg);

  screen ^= 1;

  // Drivers
  inreg.r[0] = 112;
  inreg.r[1] = screen+1;
  _kernel_swi(OS_Byte,&inreg,&outreg);

  inreg.r[0] = 19;
  _kernel_swi(OS_Byte,&inreg,&outreg);
  
}

void screen_nobuffer()
{
   // Hardware
   inreg.r[0] = 113;
   inreg.r[1] = 1;
  _kernel_swi(OS_Byte,&inreg,&outreg);

  // Drivers
  inreg.r[0] = 112;
  inreg.r[1] = 1;
  _kernel_swi(OS_Byte,&inreg,&outreg);

  inreg.r[0] = 19;
  _kernel_swi(OS_Byte,&inreg,&outreg);
  
}

void screen_clear()
{
    inreg.r[0] = 12;
  _kernel_swi(OS_WriteC,&inreg,&outreg);
}
