/*
  Image class
  for loading and saving images
  
  Not tested
  Compiles with:  g++ -c Image.cpp -o Image.o -I /usr/X11/include
  Curerntly saving isn't implemented yet
  Only PNG and JPG supported at the moment
  Baseline implementation using system libjpeg and libpng
  Currently not using standalone implementations of these so not slimmed down for tiny embedded use
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <png.h>
#include "Image.h"

namespace details
{
  enum ImageExt
  {
    EXT_NONE,
    EXT_PNG,
    EXT_JPG
  };
  ImageExt extension(const char* a_fileName)
  {
    int i = strlen(a_fileName) - 1;
    while (i >= 0 && a_fileName[i] != '.')
      i--;
    if (!strcmp(a_fileName + i, ".png"))
      return EXT_PNG;
    if (!strcmp(a_fileName + i, ".jpg"))
      return EXT_JPG;
    return EXT_NONE;
  }
}

Image::Image()
{
  init(0, 0, 0);
}

Image::Image(const char* a_fileName)
{
  if (a_fileName)
  {
    FILE *file = fopen(a_fileName, "r");
    if ( !file ) {
      printf("Error opening file %s\n", a_fileName);
    } else {
      bool okay = false;
      if (details::extension(a_fileName) == details::EXT_PNG)
        okay = initFromPng(file);
      else if (details::extension(a_fileName) == details::EXT_JPG)
        okay = initFromJpg(file);
      fclose(file);
      if (okay)
        return;
    }
  }
  init(0, 0, 0);
}

Image::Image(int a_width, int a_height, int a_bytesPerPixel)
{
  init(a_width, a_height, a_bytesPerPixel);
}

Image::~Image()
{
  for (int i = m_height - 1; i >= 0; i--)
    free(m_scanLines[i]);
  free(m_scanLines);
}

void Image::init(int a_width, int a_height, int a_bytesPerPixel)
{
  m_width = a_width;
  m_height = a_height;
  m_scanLines = (uint32_t**)malloc(sizeof(char*)*m_height);
  for (int i = m_height - 1; i >= 0; i--)
    m_scanLines[i] = (uint32_t*)malloc(4*m_width);
}

bool Image::initFromJpg(FILE* a_file)
{
  struct jpeg_decompress_struct cinfo;
  struct jpeg_error_mgr err;

  cinfo.err = jpeg_std_error(&err);
  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, a_file);
  jpeg_read_header(&cinfo, TRUE);
  jpeg_calc_output_dimensions(&cinfo);
  jpeg_start_decompress(&cinfo);

  // Setup image
  init(cinfo.output_width, cinfo.output_height, 3);

  // Decode image
  for (int i = 0; i < m_height; i++)
    jpeg_read_scanlines(&cinfo, (JSAMPLE**)&m_scanLines[i], 1);

  // Clean up
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);
  return true;
}

bool Image::initFromPng(FILE* a_file)
{
  int width, height;
  png_structp png;
  png_infop cinfo;
  png_infop cinfo_end;
  struct png_read_struct *reads;

  // Initialise
  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
  cinfo = png_create_info_struct(png);
  cinfo_end = png_create_info_struct(png);

  png_init_io(png, a_file);
  png_read_info(png, cinfo);
  width = png_get_image_width(png, cinfo);
  height = png_get_image_height(png, cinfo);

  // Setup image
  init(width, height, 4);

  // Decode image
  png_start_read_image(png);
  png_read_image(png, (png_bytepp)m_scanLines);

  // Clean up
  png_read_end(png, cinfo_end);
  png_destroy_read_struct(&png, &cinfo, &cinfo_end);
  return true;
}

bool Image::save(const char* a_fileName)
{
  bool okay = false;
  if (a_fileName)
  {
    FILE *file = fopen(a_fileName, "w");
    if ( !file ) {
      printf("Error opening file %s for saving\n", a_fileName);
    } else {
      if (details::extension(a_fileName) == details::EXT_PNG)
        okay = saveToPng(file);
      else if (details::extension(a_fileName) == details::EXT_JPG)
        okay = saveToJpg(file);
      fclose(file);
    }
  }
  return false;
}

bool Image::saveToPng(FILE* a_file)
{
  return false;
}

bool Image::saveToJpg(FILE* a_file)
{
  return false;
}

