/*------------------------------------------------------------------------------
 src/lib/djImage/imageIoTgaSave.cxx
 
 Copyright (c) 2004-2006 Darby Johnston.
 All rights reserved.
 Use is subject to license terms.
------------------------------------------------------------------------------*/

//#define DJ_DBG
#include "djImageIoTga.h"

namespace dj {
namespace ImageIoTga {

Save::Save() :
  _compress(COMPRESS_NONE)
{}

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

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

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

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

bool Save::imageOpen(const Str & fileName, const Image & in)
{
  DJ_DBG_FNC("dj::ImageIoTga::Save::imageOpen");
  DJ_DBG_PRN("file name = " << fileName);
  
  // Open file.
  //
  _f.endian(endian() != LSB);
  if (! _f.open(fileName, FileIo::WRITE)) return false;
  
  // Setup header.
  //
  _header.idSize = 0;
  _header.cmapType = 0;
  switch (_info.format()) {
    case PIXEL_L:
    case PIXEL_LA: _header.imageType = 3; break;
    case PIXEL_RGB:
    case PIXEL_RGBA: _header.imageType = 2; break;
  }
  if (COMPRESS_RLE == _compress) _header.imageType += 8;
  _header.width = _info.width();
  _header.height = _info.height();
  _header.pixelDepth = pixelSize(_info.format()) * 8;
  int alphaBits = 0;
  switch (_info.format()) {
    case PIXEL_L:
    case PIXEL_RGB: break;
    case PIXEL_LA:
    case PIXEL_RGBA: alphaBits = 8; break;
  }
  int orient = 0;
  _header.descriptor = (alphaBits & 15) | ((orient & 3) << 4);
  
  // Write header.
  //
  if (! _f.setU8(_header.idSize)) return false;
  if (! _f.setU8(_header.cmapType)) return false;
  if (! _f.setU8(_header.imageType)) return false;
  if (! _f.setU16(_header.cmapStart)) return false;
  if (! _f.setU16(_header.cmapSize)) return false;
  if (! _f.setU8(_header.cmapDepth)) return false;
  if (! _f.setU16(_header.x)) return false;
  if (! _f.setU16(_header.y)) return false;
  if (! _f.setU16(_header.width)) return false;
  if (! _f.setU16(_header.height)) return false;
  if (! _f.setU8(_header.pixelDepth)) return false;
  if (! _f.setU8(_header.descriptor)) return false;
  
  return true;
}

bool Save::imageBegin(const Image & in, int frame)
{
  DJ_DBG_FNC("dj::ImageIoTga::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::ImageIoDpx::Save::imageSet");
  DJ_DBG_PRN("in = " << in);

  const int channels = pixelSize(_info.format());
  const bool compress = 10 == _header.imageType || 11 == _header.imageType;
  
  // Convert.
  //
  PixelData framebuf(_info);
  framebuf.zero();
  pixelCopy(in, &framebuf);
  
  // Write.
  //
  int y = 0;
  for (; y < _header.height; ++y) {
    //DJ_DBG_PRN("y = " << y);
    uint8_t * const imageP = reinterpret_cast<uint8_t *>(framebuf.data(0, y));

    // Convert.
    //
    if (channels >= 3) {
      PixelU8 * p = reinterpret_cast<PixelU8 *>(imageP);
      const PixelU8 * const end = p + _header.width * channels;
      for (; p < end; p += channels) {
        const PixelU8 tmp = p[0];
        p[0] = p[2];
        p[2] = tmp;
      }
    }
    
    // Write.
    //
    if (! compress) {
    
      if (! _f.set(imageP, _header.width * channels)) break;
    
    } else {
      
      //DJ_DBG_PRN("rle");
      
      const PixelU8 * p = reinterpret_cast<PixelU8 *>(imageP);
      int count = 0;
      int i = 0;
      for (; i < _header.width; i += count, p += count * channels) {
        //DJ_DBG_PRN("index = " << i);
        const int max = min(127, _header.width - i);
        count = 0;
        const PixelU8 * tmpP = p;
        for (; count < max; ++count, tmpP += channels) {
          int c = 0;
          for (; c < channels; ++c)
            if (tmpP[c] != p[c]) break;
          if (c < channels) break;
        }
        if (count > 1) {
          if (! _f.setU8(((count - 1) & 127) | (1 << 7))) break;
          if (! _f.set(p, channels)) break;
        } else {
          for (; count < max; ++count, ++tmpP) {
            int c = 0;
            for (; c < channels; ++c)
              if (tmpP[c] != p[c]) break;
            if (c == channels) break;
          }
          if (! _f.setU8((count - 1) & 127)) break;
          if (! _f.set(p, count * channels)) break;
        }
      }
      if (i < _header.width) break;
    }
  }
  
  _f.close();
  return _header.height == y;
}

}
}
