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

//#define DJ_DBG
#include "djImageIoPpm.h"
#include <stdio.h>

namespace dj {
namespace ImageIoPpm {

Save::Save() :
  _mode(ASCII)
{}

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

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

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

bool Save::open(const Str & fileName, const ImageIoInfo & info)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Save::open");
  DJ_DBG_PRN("file name = " << fileName);
  
  _fileInfo.set(fileName);
  _info = info;
  _info.size(info.size());
  switch (info.format()) {
    case PIXEL_L:
    case PIXEL_LA: _info.format(PIXEL_L); break;
    case PIXEL_RGB:
    case PIXEL_RGBA: _info.format(PIXEL_RGB); break;
  }
  switch (info.type()) {
    case PIXEL_U8: _info.type(PIXEL_U8); break;
    case PIXEL_U16:
    case PIXEL_F32: _info.type(PIXEL_U16); break;
  }

  return true;
}

bool Save::imageOpen(const Str & fileName, const Image & in)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Save::imageOpen");
  DJ_DBG_PRN("file name = " << fileName);
  
  // Setup header.
  //
  if (PIXEL_L == _info.format()) {
    switch (_mode) {
      case ASCII: _header.type = 2; break;
      case BINARY: _header.type = 5; break;
    }
  } else if (PIXEL_RGB == _info.format()) {
    switch (_mode) {
      case ASCII: _header.type = 3; break;
      case BINARY: _header.type = 6; break;
    }
  }
  _header.width = _info.width();
  _header.height = _info.height();
  switch (_info.type()) {
    case PIXEL_U8: _header.maxValue = pixelU8Max; break;
    case PIXEL_U16: _header.maxValue = pixelU16Max; break;
    case PIXEL_F32: break;
  }
  
  // Open file.
  //
  _f.endian(endian() != MSB);
  if (! _f.open(fileName, FileIo::WRITE)) return false;
  
  // Write header.
  //
  char magic [] = "P \n";
  magic[1] = '0' + _header.type;
  if (! _f.set(magic, 3)) return false;
  char tmp [DJ_CSTR_SIZE];
  int size = snprintf(
    tmp, DJ_CSTR_SIZE, "%d %d\n", _header.width, _header.height
  );
  if (! _f.set(tmp, size)) return false;
  size = snprintf(tmp, DJ_CSTR_SIZE, "%d\n", _header.maxValue);
  if (! _f.set(tmp, size)) return false;
  
  return true;
}

bool Save::imageBegin(const Image & in, int frame)
{
  DJ_DBG_FNC("dj::ImageIoPpm::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::ImageIoPpm::Save::imageSet");
  DJ_DBG_PRN("in = " << in);
  
  const int channels = pixelSize(_info.format());
  const int bytes = pixelSize(_info.type());
  const bool ascii = 2 == _header.type || 3 == _header.type;
  
  // Convert.
  //
  PixelData framebuf(_info);
  framebuf.zero();
  pixelCopy(in, &framebuf);
  
  // Write.
  //
  int y = 0;
  for (; y < _header.height; ++y) {
    //DJ_DBG_PRN("y = " << y);
    const uint8_t * const imageP = reinterpret_cast<const uint8_t *>(
      framebuf.data(0, _header.height - 1 - y)
    );
    
    // Write.
    //
    if (! ascii) {
    
      if (! _f.set(imageP, _header.width * channels, bytes))
        break;
      
    } else {
    
      char tmp [DJ_CSTR_SIZE];
      int size;
      
      if (1 == bytes) {

        const PixelU8 * p = reinterpret_cast<const PixelU8 *>(imageP);
        int i = 0;
        for (; i < _header.width; ++i, p += channels) {
          int c = 0;
          for (; c < channels; ++c) {
            size = snprintf(tmp, DJ_CSTR_SIZE, "%d ", p[c]);
            if (! _f.set(tmp, size)) break;
          }
          if (c < channels) break;
        }
        if (i < _header.width) break;
        tmp[0] = '\n';
        if (! _f.set(tmp, 1)) break;

      } else if (2 == bytes) {

        const PixelU16 * p = reinterpret_cast<const PixelU16 *>(imageP);
        int i = 0;
        for (; i < _header.width; ++i, p += channels) {
          int c = 0;
          for (; c < channels; ++c) {
            size = snprintf(tmp, DJ_CSTR_SIZE, "%d ", p[c]);
            if (! _f.set(tmp, size)) break;
          }
          if (c < channels) break;
        }
        if (i < _header.width) break;
        tmp[0] = '\n';
        if (! _f.set(tmp, 1)) break;
      }
    }
  }
  
  _f.close();
  return _header.height == y;
}

}
}
