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

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

namespace dj {
namespace ImageIoSgi {

bool Load::open(const Str & fileName, ImageIoInfo * info)
{
  DJ_DBG_FNC("dj::ImageIoSgi::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::ImageIoSgi::Load::imageOpen");
  DJ_DBG_PRN("file name = " << fileName);
  
  // Open file.
  //
  _f.endian(endian() != MSB);
  if (! _f.open(fileName, FileIo::READ)) return false;
  
  // Read header.
  //
  uint16_t magic;
  if (! _f.getU16(&magic)) return false;
  if (magic != 474) return false;
  if (! _f.getU8(&_header.storage)) return false;
  if (! _f.getU8(&_header.bytes)) return false;
  if (! _f.getU16(&_header.dimension)) return false;
  if (! _f.getU16(&_header.width)) return false;
  if (! _f.getU16(&_header.height)) return false;
  if (! _f.getU16(&_header.channels)) return false;
  if (! _f.getU32(&_header.pixelMin)) return false;
  if (! _f.getU32(&_header.pixelMax)) return false;
  if (! _f.seek(492, FileIo::CURRENT)) return false;
  dbg(_header);
  
  // Read RLE tables.
  //
  if (_header.storage) {
    const int tableSize = _header.height * _header.channels;
    if (tableSize) {
      _header.rleOffsets = new uint32_t [tableSize];
      if (! _f.getU32(_header.rleOffsets, tableSize)) return false;
      _header.rleSizes = new uint32_t [tableSize];
      if (! _f.getU32(_header.rleSizes, tableSize)) return false;
    }
  }
  
  // Setup information.
  //
  info->size(Vec2i(_header.width, _header.height));
  
  Pixel pixel;
  switch (_header.dimension) {
    case 1:
    case 2: pixel.format(PIXEL_L); break;
    case 3:
      switch (_header.channels) {
        case 1: pixel.format(PIXEL_L); break;
        case 2: pixel.format(PIXEL_LA); break;
        case 3: pixel.format(PIXEL_RGB); break;
        case 4: pixel.format(PIXEL_RGBA); break;
        default: return false;
      }
      break;
    default: return false;
  }
  switch (_header.bytes) {
    case 1: pixel.type(PIXEL_U8); break;
    case 2: pixel.type(PIXEL_U16); break;
    default: return false;
  }
  info->pixel(pixel);

  return true;
}

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

static bool _scanlineRle(
  FileIo * f, int w, int bytes, int offset, uint8_t * out
) {
  if (! f->seek(offset, FileIo::SET)) return false;

  int i = 0;
  
  if (1 == bytes) {

    PixelU8 * p = reinterpret_cast<PixelU8 *>(out);
    int count = 0;
    while (i < w) {
    
      PixelU8 tmp;
      if (! f->getU8(&tmp)) break;
      
      count = tmp & 127;
      if (! count || count > (w - i)) break;
      
      if (tmp >> 7) {
        int j = 0;
        for (; j < count; ++j) {
          if (! f->getU8(&tmp)) break;
          *p++ = tmp;
        }
        if (j < count) break;
      } else {
        if (! f->getU8(&tmp)) break;
        for (int j = 0; j < count; ++j)
          *p++ = tmp;
      }
      i += count;
    }
      
  } else if (2 == bytes) {

    PixelU16 * p = reinterpret_cast<PixelU16 *>(out);
    int count = 0;
    while (i < w) {
    
      PixelU16 tmp;
      if (! f->getU16(&tmp)) break;
      
      count = tmp & 127;
      if (! count || count > (w - i)) break;
      
      if (tmp >> 7) {
        int j = 0;
        for (; j < count; ++j) {
          if (! f->getU16(&tmp)) break;
          *p++ = tmp;
        }
        if (j < count) break;
      } else {
        if (! f->getU16(&tmp)) break;
        for (int j = 0; j < count; ++j)
          *p++ = tmp;
      }
      i += count;
    }
  }
  
  return w == i;
}

bool Load::imageGet(PixelData * out, int proxyScale)
{
  DJ_DBG_FNC("dj::ImageIoSgi::Load::imageGet");
  DJ_DBG_PRN("out = " << *out);
  DJ_DBG_PRN("proxy = " << proxyScale);

  const bool compress = 1 == _header.storage;

  // Read.
  //
  PixelData scanline(
    Vec2i(_header.width, 1),
    Pixel(PIXEL_L, _header.bytes == 1 ? PIXEL_U8 : PIXEL_U16)
  );
  int c = 0;
  for (; c < _header.channels; ++c) {
    DJ_DBG_PRN("c = " << c);

    int y = 0, fileY = 0;
    for (; y < out->height(); ++y, fileY += proxyScale) {
      //DJ_DBG_PRN("y = " << y << " (" << fileY << ")");
    
      if (! compress) {
        if (! _f.get(scanline.data(), _header.width, _header.bytes)) break;
      } else {
        if (
          ! _scanlineRle(
            &_f, _header.width, _header.bytes,
            _header.rleOffsets[fileY + c * _header.height],
            scanline.data()
          )
        ) break;
      }
      
      // Convert.
      //
      pixelCopy(
        scanline.data(),
        out->data(0, y) + c * pixelSize(out->type()),
        out->width(), scanline.pixel(), out->pixel(),
        proxyScale, false, 1
      );
    
      // Skip scanlines.
      //
      const int skip = min(proxyScale - 1, _header.height - (fileY + 1));
      if (skip)
        if (! compress)
          if (! _f.seek(skip * _header.width * _header.bytes, FileIo::CURRENT))
            break;
    }
    if (y < out->height()) break;
  }
  
  _f.close();
  return _header.channels == c;
}

}
}
