/*
  DNG file format class based on LibTIFF.
 */

#include <stdint.h>
#include <stdlib.h>
#include <time.h>
#include <tiffio.h>
#include "dng.h"

DNG::DNG() {
  /* initialize public data */
  ImageWidth = 0;
  ImageHeight = 0;
  BitsPerSample = 0;
  SamplesPerPixel = 0;
  ColorID = 0;

  /* initialize internal data */
  tif = NULL;
}

DNG::~DNG() {
  /* close file if necessary */
  if(tif) {
    TIFFClose(tif);
  }
}

/*
  Run-time detection if we are little- or big-endian.
 */
int DNG::IsBigEndian(void) {
  int i = 1;
  return ! *((char *)&i);
}

/*
  Sets the file name of the DNG file to be written.  Returns -1 on
  failure, 0 on success.
 */
int DNG::setFile(char * file) {
  /*
    w = write
    l = little endian
    b = big endian
    -> if neither l or b is given, data is written in native CPU format
   */
  if (!(tif = TIFFOpen(file, "w"))) {
    return -1;
  }
  return 0;
}

/*
  Sets the color ID.  Only few formats are supported right now.
 */
void DNG::setColorID(int id) {

  switch(id) {

  case DNG_COLORID_RGB:
    BitsPerSample = 8;
    SamplesPerPixel = 3;
    ColorID = id;
    break;
    
  case DNG_COLORID_RAW16:
    BitsPerSample = 16;
    SamplesPerPixel = 1;
    ColorID = id;
    break;
    
  case DNG_COLORID_RAW8:
    BitsPerSample = 8;
    SamplesPerPixel = 1;
    ColorID = id;
    break;
    
  default:
    fprintf(stderr, "Error: DNG color ID #%d not supported\n", id);
    break;
  }
}

/*
  Sets the image width.
 */
int DNG::setWidth(int width) {
  ImageWidth = width;
  return 0;
}

/*
  Gets the image width.
 */
int DNG::getWidth(void) {
  return ImageWidth;
}

/*
  Sets the image height.
 */
int DNG::setHeight(int height) {
  ImageHeight = height;
  return 0;
}

/*
  Gets the image height.
 */
int DNG::getHeight(void) {
  return ImageHeight;
}

/*
  Saves given sample data into the DNG file according to the color ID
  and image size specifications.
 */
int DNG::writeFile(void * data) {

  /* create timestamp */
  time_t abs_ts;
  struct tm tm;
  char timestamp[64];

  /* get time stamp */
  time (&abs_ts);
  gmtime_r (&abs_ts, &tm);
  sprintf(timestamp, "%04d:%02d:%02d %02d:%02d:%02d",
	  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);

  /* set general meta data */
  TIFFSetField(tif, TIFFTAG_MAKE, "Svbony");
  TIFFSetField(tif, TIFFTAG_MODEL, "Svbony SV305");
  TIFFSetField(tif, TIFFTAG_SOFTWARE, "SimpleSkyCam v0.0.1");
  TIFFSetField(tif, TIFFTAG_ARTIST, "Steffen Pohle");
  TIFFSetField(tif, TIFFTAG_DATETIME, timestamp);
  TIFFSetField(tif, TIFFTAG_DNGVERSION, "\001\001\0\0");
  TIFFSetField(tif, TIFFTAG_DNGBACKWARDVERSION, "\001\0\0\0");

  /* set image meta data */
  TIFFSetField(tif, TIFFTAG_SUBFILETYPE, 0);               // image
  TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, ImageWidth);       // in pixels
  TIFFSetField(tif, TIFFTAG_IMAGELENGTH, ImageHeight);     // in pixels
  TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, BitsPerSample); // int
  TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT);
  TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT);
  TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
  TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, SamplesPerPixel);
  TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);

  if(ColorID == DNG_COLORID_RAW8 || ColorID == DNG_COLORID_RAW16) {
    static const short CFARepeatPattern[] = { 2,2 }; // 2x2 CFA
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_CFA);
    TIFFSetField(tif, TIFFTAG_CFAREPEATPATTERNDIM, CFARepeatPattern);
    TIFFSetField(tif, TIFFTAG_CFALAYOUT, 1);
    TIFFSetField(tif, TIFFTAG_CFAPLANECOLOR, 3, "\000\001\002");  // RGB
    // 0 = Red, 1 = Green, 2 = Blue
    TIFFSetField(tif, TIFFTAG_CFAPATTERN, "\001\000\002\001"); // GRBG
  }
  else if(ColorID == DNG_COLORID_RGB) {
    TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
  }
  
  // go through all lines
  for(int y = 0; y < ImageHeight; y++) {
    
    // calculate offset into source image based on color ID settings
    int offset = y * (ImageWidth * BitsPerSample/8 * SamplesPerPixel);

    // write complete scanline
    TIFFWriteScanline(tif, &((uint8_t *)data)[offset], y, 0);
  }

  return 0;
}
