#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//
#include "proto.h"
#include "bucket.h"
#include "main.h"
#include "flash.h"
#include "bitmap.h"
#include "utils.h"

#include "zlib.h"
#ifdef __riscos__
#include "os.h"
#include "osspriteop.h"

//#define SUPPORT_ALPHA_4_8BPP

static S32 flashbitmap_from_spritefile(char **in, U32 *insize, U16 *width, U16 *height, U8 *bpp, U32 *alpha);
#endif

static S32 zlib_compress(char *out, U32 *outsize, char *in, U32 insize, S32 level);


S32 bitmap_read(BITMAP *bitmap) {

  return 0;
}


S32 bitmap_write(BITMAP *bitmap) {

  S32 type, tag;
  U32 ptr, size, sizeword;
  U8 *buf;

  type = bitmap->type;

  if (flush_bucket())                             return 1;
  ptr = read_position(NULL);
  if (write_ushort(0))                            return 1;
  if (write_ushort(bitmap->id))                   return 1;

  switch (type) {
  case BITMAP_JPEG:
    if (bitmap->alphafile)      type |= BITMAP_HAS_ALPHA;
    if (type == BITMAP_JPEG)
      tag = stagDefineBitsJPEG2;
    else {
      tag = stagDefineBitsJPEG3;
      // get offset of word that should eventually contain the size of the jpeg data
      if (flush_bucket())                         return 1;
      sizeword = read_position(&buf);
      // write dummy word
      if (write_uint(0))                          return 1;
    }
    // write jpeg encoding
    if (write_ushort(0xd9ff))                     return 1;
    if (write_ushort(0xd8ff))                     return 1;
    // write jpeg data
    size = bitmap->sizeinmemory;
    if (write_file(bitmap->file, &size))          return 1;
    size += 4;
    if (type == BITMAP_JPEGALPHA) {
      char *file;
      U32 size;

      // first, go back to just before jpeg data, and write size of jpeg data
      buf[sizeword+0] =  size       & 0xff;
      buf[sizeword+1] = (size>>  8) & 0xff;
      buf[sizeword+2] = (size>> 16) & 0xff;
      buf[sizeword+3] = (size>> 24) & 0xff;

      size = bitmap->alphasizeinmemory;
      file = bitmap->alphafile;
      // make sure file is loaded into memory
      if (size == NOT_INLINE_FILE)
        if (load_file_into_memory(file, &file, &size))  return 1;
      // compress alphachannel if not already compressed
      if (bitmap->alphatype == BITMAP_UNCOMPRESSED) {
        U32 outsize;
        char *out;

        outsize = size+size/100+16;
        out = malloc(outsize);
        if (!out)                                 return 1;
        if (zlib_compress(out, &outsize, file, size, 7))  return 1;
        file = out;
        size = outsize;
      }

      if (write_file(file, &size))                return 1;
    }
    break;

  case BITMAP_LOSSLESSALPHA:
    return 1;
    break;

  case BITMAP_LOSSLESS:
    {
      U32 size;
      char *file;

      tag = stagDefineBitsLossless;
      size = bitmap->sizeinmemory;
      file = bitmap->file;
      // make sure file is loaded into memory
      if (size == NOT_INLINE_FILE)
        if (load_file_into_memory(file, &file, &size))      return 1;

#ifdef __riscos__
      if (bitmap->bits == BITMAP_SPRITEFILE) {
        // extract palette and pixels
        U8 bits;
        U32 hasalpha = 0;

        if (flashbitmap_from_spritefile(&file, &size,
            &bitmap->width, &bitmap->height, &bits, &hasalpha))   return 1;
        if (hasalpha)      tag = stagDefineBitsLossless2;
        switch (bits) {
        case 8:  bitmap->bits = BITMAP_8BPP;
          break;
        case 16: bitmap->bits = BITMAP_16BPP;
          break;
        case 32: bitmap->bits = BITMAP_32BPP;
          break;
        }
        bitmap->bits |= BITMAP_UNCOMPRESSED;
      }
#endif
      // write bitmap info
      switch (bitmap->bits) {
      case BITMAP_8BPP:
      case BITMAP_8BPP | BITMAP_UNCOMPRESSED:
        if (write_ubyte(3))                       return 1;
        if (write_ushort(bitmap->width))          return 1;
        if (write_ushort(bitmap->height))         return 1;
        if (write_ubyte(255))                     return 1;
        break;
      case BITMAP_16BPP:
      case BITMAP_16BPP | BITMAP_UNCOMPRESSED:
        if (write_ubyte(4))                       return 1;
        if (write_ushort(bitmap->width))          return 1;
        if (write_ushort(bitmap->height))         return 1;
        break;
      case BITMAP_32BPP:
      case BITMAP_32BPP | BITMAP_UNCOMPRESSED:
        if (write_ubyte(5))                       return 1;
        if (write_ushort(bitmap->width))          return 1;
        if (write_ushort(bitmap->height))         return 1;
        break;
      default:
        return 1;
        break;
      }

      // compress bitmap if not already compressed
      if (bitmap->bits & BITMAP_UNCOMPRESSED) {
        U32 outsize;
        char *out;
        outsize = size+size/50+100;
        out = malloc(outsize);
        if (!out)                                 return 1;
        if (zlib_compress(out, &outsize, file, size, 7))    return 1;
        file = out;
        size = outsize;
      }

      if (write_file(file, &size))                return 1;
    }
    break;
  }

  return update_record_header(tag, ptr);
}



S32 bitmap_create(BITMAP **bitmap) {

  *bitmap = malloc(sizeof(BITMAP));
  if (!*bitmap)               return 1;
  memset(*bitmap, 0, sizeof(BITMAP));

  return 0;
}


// ---------------------------------------------------------------------

#ifdef __riscos__
S32 flashbitmap_from_spritefile(char **in, U32 *insize, U16 *width, U16 *height, U8 *bits, U32 *hasalpha) {

  S32 *area, bpp;
  U8 *palette;
  osspriteop_header *header;
  char *inp;

  inp = *in;
  *hasalpha = 0;

  area = (S32 *)inp;
  if (area[0] != 1) {
    fprintf(stderr, "The spritefile must contain only one sprite\n");
    return 1;
  }
  header = (osspriteop_header *)(inp + area[1] - 4);
  if (xos_read_mode_variable(header->mode, os_MODEVAR_LOG2_BPP, &bpp, NULL)) {
    fprintf(stderr, "Unknown mode\n");
    return 1;
  }
  *bits = 1<<bpp;
  *height = header->height+1;
  if (*bits == 4) {
    S32 i, pixels;
    char *img, *out, *outp;

    if ((header->left_bit != 0) || (header->right_bit != 31)) {
      fprintf(stderr, "4bpp sprites must be a multiple of 8 pixels wide\n");
      return 1;
    }
    *width = 8*(header->width+1);
    if (header->image != 2*4*16+sizeof(osspriteop_header)) {
      fprintf(stderr, "The sprite does not have a fullcolour palette\n");
      return 1;
    }
    pixels = (*width)*(*height);
    out = malloc(pixels+4*256);
    outp = out;
    if (!out) {
      fprintf(stderr, "No room\n");
      return 1;
    }
    palette = (U8 *)(inp + area[1] + sizeof(osspriteop_header));
    img = (char *)header + header->image;
#ifdef SUPPORT_ALPHA_4_8BPP
    // check palette for alpha values
    for (i = 0; i < 16; i++)
      if (palette[8*i+0])        *hasalpha = 1;
#endif
    // make palette
    for (i = 0; i < 16; i++) {
      *outp++ = palette[8*i+1];
      *outp++ = palette[8*i+2];
      *outp++ = palette[8*i+3];
      if (*hasalpha)  *outp++ = 255-palette[8*i+0];
    }
    for (i = 16; i < 255; i++) {
      *outp++ = 0;
      *outp++ = 0;
      *outp++ = 0;
      if (*hasalpha)  *outp++ = 0;
    }
    // copy and convert the pixels
    for (i = 0; i < pixels; i++)
      if (i &1)
        *outp++ = (img[i>>1]>>4) &15;
      else
        *outp++ = img[i>>1] &15;

    *insize = pixels + 3*256;
    if (*hasalpha)   *insize += 256;
    *in = out;
    *bits = 8;

  } else if (*bits == 8) {
    S32 i;
    char *img;

    if ((header->left_bit != 0) || (header->right_bit != 31)) {
      fprintf(stderr, "8bpp sprites must be a multiple of 4 pixels wide\n");
      return 1;
    }
    *width = 4*(header->width+1);
    if (header->image != 2*4*256+sizeof(osspriteop_header)) {
      fprintf(stderr, "The sprite does not have a 256-entry palette\n");
      return 1;
    }
    *insize = (*width)*(*height);
    palette = (U8 *)(inp + area[1] + sizeof(osspriteop_header));
    img = (char *)header + header->image;
#ifdef SUPPORT_ALPHA_4_8BPP
    // check palette for alpha
    for (i = 0; i < 256; i++)
      if (palette[8*i+0])        *hasalpha = 1;
#endif
    // convert palette
    for (i = 0; i < 256; i++) {
      *inp++ = palette[8*i+1];
      *inp++ = palette[8*i+2];
      *inp++ = palette[8*i+3];
      if (*hasalpha)      *inp++ = 255-palette[8*i+0];
    }
    memcpy(inp, img, *insize);
    *insize += 3*256;
    if (*hasalpha)   *insize += 256;

  } else if (*bits == 16) {
    if ((header->left_bit != 0) || (header->right_bit != 31)) {
      fprintf(stderr, "16bpp sprite must be a multiple of 2 pixels wide\n");
      return 1;
    }
    *width = 2*(header->width+1);
    *insize = 2*(*width)*(*height);
    memcpy(*in, (char *)header + header->image, *insize);

  } else if (*bits == 32) {
    U8 *data, *inp;
    U32 i, n;

    *width = header->width+1;
    *insize = 4*(*width)*(*height);

    n = (*width)*(*height);
    data = (U8 *)*in;
    inp = (U8 *)header + header->image;
    for (i = 0; i < n; i++) {
      *data++ = 255 - inp[3];
      *data++ = inp[0];
      *data++ = inp[1];
      *data++ = inp[2];
      inp += 4;
    }
    *hasalpha = 1;

  } else {
    fprintf(stderr, "Illegal no. of bits per pixel (%d)\n", bpp);
    return 1;
  }

  return 0;
}
#endif


S32 zlib_compress(char *out, U32 *outsize, char *in, U32 insize, S32 level) {

  if (compress2((Bytef *)out, (uLongf *)outsize, (Bytef *)in, insize, level) != Z_OK) {
    fprintf(stderr, "Failed to compress file\n");
    return 1;
  }

  return 0;
}
