/*------------------------------------------------------------------------------
 src/plugin/djImageIoTiff/save.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "io.h"
#include <djMath.h>

extern "C" {
const dj::List<dj::ImageSave *> djImageSave()
{
  TIFFSetErrorHandler(0);
  TIFFSetWarningHandler(0);
  
  dj::List<dj::ImageSave *> out;
  out += new djImageIoTiff::Save;
  return out;
}
}

namespace djImageIoTiff {

Save::Save() :
  _compress(COMPRESS_NONE),
  _channels(INTERLEAVE),
  _f(0)
{}

Save::~Save()
{
  if (_f) TIFFClose(_f);
}

const StrList Save::attrNameList() const { return saveAttrStr(); }

bool Save::attr(const Str & in, const StrList & data)
{
  StrList tmp = data;
  if (compareCase(saveAttrStr()[ATTR_COMPRESS], in)) {
    if (! inflateStr(tmp, &_compress, compressStr())) return false;
  } else if (compareCase(saveAttrStr()[ATTR_CHANNELS], in)) {
    if (! inflateStr(tmp, &_channels, channelsStr())) return false;
  } else
    return false;
  return true;
}

const StrList Save::attr(const Str & in) const
{
  StrList out;
  if (compareCase(saveAttrStr()[ATTR_COMPRESS], in))
    out += deflateStr(_compress, compressStr());
  else if (compareCase(saveAttrStr()[ATTR_CHANNELS], in))
    out += deflateStr(_channels, channelsStr());
  return out;
}

bool Save::open(const Str & fileName, const ImageIoInfo & info)
{
  DJ_DBG_FNC("djImageIoTiff::Save::open");
  DJ_DBG_PRN("file name = " << fileName);
  
  _fileInfo.set(fileName);
  _info = info;
  
  return true;
}

bool Save::imageOpen(const Str & fileName, const Image & in)
{
  DJ_DBG_FNC("djImageIoTiff::Save::imageOpen");
  DJ_DBG_PRN("file name = " << fileName);
  
  // Open file.
  //
  if (_f) TIFFClose(_f);
  _f = TIFFOpen(fileName.toAscii(), "w");
  if (! _f) return false;
  
  // Setup header.
  //
  _header.width = _info.width();
  _header.height = _info.height();
  
  uint16 extraSamples [] = { EXTRASAMPLE_ASSOCALPHA };
  switch (_info.format()) {
    case PIXEL_L:
      _header.photometric = PHOTOMETRIC_MINISBLACK;
      _header.samples = 1;
      break;
    case PIXEL_LA:
      _header.photometric = PHOTOMETRIC_MINISBLACK;
      _header.samples = 2;
      _header.extraSamples = extraSamples;
      _header.extraSamplesSize = 1;
      break;
    case PIXEL_RGB:
      _header.photometric = PHOTOMETRIC_RGB;
      _header.samples = 3;
      break;
    case PIXEL_RGBA:
      _header.photometric = PHOTOMETRIC_RGB;
      _header.samples = 4;
      _header.extraSamples = extraSamples;
      _header.extraSamplesSize = 1;
      break;
  }
  switch (_info.type()) {
    case PIXEL_U8:
      _header.sampleDepth = 8;
      _header.sampleFormat = SAMPLEFORMAT_UINT;
      break;
    case PIXEL_U16:
      _header.sampleDepth = 16;
      _header.sampleFormat = SAMPLEFORMAT_UINT;
      break;
    case PIXEL_F32:
      _header.sampleDepth = 32;
      _header.sampleFormat = SAMPLEFORMAT_IEEEFP;
      break;
  }
  
  _header.orient = ORIENTATION_BOTLEFT;
  
  switch (_compress) {
    case COMPRESS_NONE: _header.compress = COMPRESSION_NONE; break;
    case COMPRESS_RLE: _header.compress = COMPRESSION_PACKBITS; break;
    case COMPRESS_LZW: _header.compress = COMPRESSION_LZW; break;
  }
  
  _header.channels =
    _channels == INTERLEAVE ? PLANARCONFIG_CONTIG : PLANARCONFIG_SEPARATE;
  
  // Write header.
  //
  dbg(_header);
  TIFFSetField(_f, TIFFTAG_IMAGEWIDTH, _header.width);
  TIFFSetField(_f, TIFFTAG_IMAGELENGTH, _header.height);
  TIFFSetField(_f, TIFFTAG_PHOTOMETRIC, _header.photometric);
  TIFFSetField(_f, TIFFTAG_SAMPLESPERPIXEL, _header.samples);
  TIFFSetField(_f, TIFFTAG_BITSPERSAMPLE, _header.sampleDepth);
  TIFFSetField(_f, TIFFTAG_SAMPLEFORMAT, _header.sampleFormat);
  TIFFSetField(
    _f, TIFFTAG_EXTRASAMPLES, _header.extraSamplesSize, _header.extraSamples
  );
  TIFFSetField(_f, TIFFTAG_ORIENTATION, _header.orient);
  TIFFSetField(_f, TIFFTAG_COMPRESSION, _header.compress);
  TIFFSetField(_f, TIFFTAG_PLANARCONFIG, _header.channels);

  // Tags.
  //
  const ImageTagList tagList = in.tagList();
  Str str;
  StrList data;
  data = tagList[imageTagStr()[IMAGE_TAG_CREATOR]];
  if (inflateStr(data, &str))
    TIFFSetField(_f, TIFFTAG_ARTIST, str.toAscii());
  data = tagList[imageTagStr()[IMAGE_TAG_COPYRIGHT]];
  if (inflateStr(data, &str))
    TIFFSetField(_f, TIFFTAG_COPYRIGHT, str.toAscii());
  data = tagList[imageTagStr()[IMAGE_TAG_TIME_STAMP]];
  if (inflateStr(data, &str))
    TIFFSetField(_f, TIFFTAG_DATETIME, str.toAscii());
  data = tagList[imageTagStr()[IMAGE_TAG_DESCRIPTION]];
  if (inflateStr(data, &str))
    TIFFSetField(_f, TIFFTAG_IMAGEDESCRIPTION, str.toAscii());
  
  return true;
}

bool Save::imageBegin(const Image & in, int frame)
{
  DJ_DBG_FNC("djImageIoTiff::Save::imageBegin");
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("frame = " << frame);
  
  if (! imageOpen(_fileInfo.get(frame == -1 ? _fileInfo.start() : frame), in))
    return false;

  return true;
}

bool Save::imageSet(const PixelData & in)
{
  DJ_DBG_FNC("dj::djImageIoTiff::Save::imageSet");
  DJ_DBG_PRN("in = " << in);

  const bool planar = _channels == PLANAR;
  const int planes = planar ? pixelSize(_info.format()) : 1;
  DJ_DBG_PRN("planar = " << planar);
  DJ_DBG_PRN("planes = " << planes);
  
  // Convert.
  //
  PixelData framebuf(_info);
  framebuf.zero();
  pixelCopy(in, &framebuf);
  DJ_DBG_PRN("framebuf = " << framebuf);

  // Write.
  //
  PixelData scanline(
    Vec2i(_info.width(), 1),
    Pixel(planar ? PIXEL_L : _info.format(), _info.type())
  );
  int c = 0;
  for (; c < planes; ++c) {
    DJ_DBG_PRN("c = " << c);
    
    int y = 0;
    for (; y < _info.height(); ++y) {
      //DJ_DBG_PRN("y = " << y);

      // Convert.
      //
      pixelCopy(
        framebuf.data(0, y) + c * pixelSize(framebuf.type()), scanline.data(),
        _info.width(), framebuf.pixel(), scanline.pixel(),
        1, false, planar ? -1 : 0
      );
      
      // Write.
      //
      if (TIFFWriteScanline(_f, (tdata_t *)scanline.data(), y, c) == -1)
        break;
    }
    if (y < _info.height()) break;
  }
  
  TIFFClose(_f);
  _f = 0;
  return planes == c;
}

}
