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

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

namespace dj {
namespace ImageIoPpm {

bool Load::open(const Str & fileName, ImageIoInfo * info)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Load::open");
  DJ_DBG_PRN("file name = " << fileName);
  
  _fileInfo.set(fileName);
  
  if (
    ! imageOpen(
      _fileInfo.get(_fileInfo.isSeq() ? _fileInfo.start() : -1), info
    )
  ) return false;
  
  if (_fileInfo.isSeq()) info->time(_fileInfo.seq());
  
  return true;
}

bool Load::imageOpen(const Str & fileName, ImageIoInfo * info)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Load::imageOpen");
  DJ_DBG_PRN("file name = " << fileName);
  
  // Open file.
  //
  _f.endian(endian() != MSB);
  if (! _f.open(fileName, FileIo::READ)) return false;
  
  // Read header.
  //
  char magic [] = { 0, 0, 0 };
  if (! _f.get(magic, 2)) return false;
  if (magic[0] != 'P') return false;
  switch (magic[1]) {
    case '2':
    case '3':
    case '5':
    case '6': break;
    default: return false;
  }
  _header.type = magic[1] - '0';
  char tmp [DJ_CSTR_SIZE];
  if (! getWord(&_f, tmp, DJ_CSTR_SIZE)) return false;
  _header.width = atoi(tmp, DJ_CSTR_SIZE);
  if (! getWord(&_f, tmp, DJ_CSTR_SIZE)) return false;
  _header.height = atoi(tmp, DJ_CSTR_SIZE);
  if (! getWord(&_f, tmp, DJ_CSTR_SIZE)) return false;
  _header.maxValue = atoi(tmp, DJ_CSTR_SIZE);

  // Setup information.
  //
  info->size(Vec2i(_header.width, _header.height));
  
  switch (_header.type) {
    case 2:
    case 5: _pixel.format(PIXEL_L); break;
    case 3:
    case 6: _pixel.format(PIXEL_RGB); break;
  }
  _pixel.type(_header.maxValue <= pixelU8Max ? PIXEL_U8 : PIXEL_U16);
  info->pixel(_pixel);
  
  return true;
}

bool Load::imageBegin(int frame, ImageIoInfo * info)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Load::imageBegin");
  DJ_DBG_PRN("frame = " << frame);
  
  if (
    ! imageOpen(_fileInfo.get(frame == -1 ? _fileInfo.start() : frame), info)
  ) return false;
  
  return true;
}

static bool _scanlineAscii(
  FileIo * f, int w, int channels, int bytes, uint8_t * out
) {
  char tmp [DJ_CSTR_SIZE];
  int i = 0;
  
  if (1 == bytes) {
  
    PixelU8 * p = reinterpret_cast<PixelU8 *>(out);
    for (; i < w; ++i, p += channels) {
      int c = 0;
      for (; c < channels; ++c) {
        if (! getWord(f, tmp, DJ_CSTR_SIZE)) break;
        p[c] = atoi(tmp, DJ_CSTR_SIZE);
      }
      if (c < channels) break;
    }

  } else if (2 == bytes) {

    PixelU16 * p = reinterpret_cast<PixelU16 *>(out);
    for (; i < w; ++i, p += channels) {
      int c = 0;
      for (; c < channels; ++c) {
        if (! getWord(f, tmp, DJ_CSTR_SIZE)) break;
        p[c] = atoi(tmp, DJ_CSTR_SIZE);
      }
      if (c < channels) break;
    }
  }
  
  return w == i;
}

bool Load::imageGet(PixelData * out, int proxyScale)
{
  DJ_DBG_FNC("dj::ImageIoPpm::Load::imageGet");
  DJ_DBG_PRN("out = " << *out);
  DJ_DBG_PRN("proxy = " << proxyScale);
  DJ_DBG_PRN("flip = " << flipX << " " << flipY);
  
  const int channels = pixelSize(_pixel.format());
  const int bytes = pixelSize(_pixel.type());
  const bool ascii = 2 == _header.type || 3 == _header.type;

  // Read.
  //
  PixelData scanline(Vec2i(_header.width, 1), _pixel);
  int y = 0, fileY = 0;
  for (; y < out->height(); ++y, fileY += proxyScale) {
    //DJ_DBG_PRN("y = " << y << " (" << fileY << ")");
    
    if (! ascii) {
      if (! _f.get(scanline.data(), _header.width * channels, bytes)) break;
    } else {
      if (
        ! _scanlineAscii(&_f, _header.width, channels, bytes, scanline.data())
      ) break;
    }

    // Convert.
    //
    pixelCopy(
      scanline.data(), out->data(0, out->height() - 1 - y),
      out->width(), scanline.pixel(), out->pixel(),
      proxyScale
    );
    
    // Skip scanlines.
    //
    const int skip = min(proxyScale - 1, _header.height - (fileY + 1));
    if (skip) {
      if (! ascii) {
        if (
          ! _f.seek(skip * _header.width * channels * bytes, FileIo::CURRENT)
        )
          break;
      } else {
        int i = 0;
        for (i = 0; i < skip; ++i)
          if (
            ! _scanlineAscii(
              &_f, _header.width, channels, bytes, scanline.data()
            )
          )
            break;
        if (i < skip) break;
      }
    }
  }
  
  _f.close();
  return out->height() == y;
}

}
}
