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

//#define DJ_DBG
#include "djImageIo.h"
#include <djFileInfo.h>
#include <djImageOpCore.h>
#include <djImageApp.h>
#include <dlfcn.h>

namespace dj {

ImageIoInfo::ImageIoInfo() :
  _timeStart(0), _timeEnd(0), _timeSpeed(24.0)
{}

void ImageIoInfo::tagList(const ImageTagList & in)
{
  _tagList = in;
}

void ImageIoInfo::time(int start, int end, double speed)
{
  _timeStart = start;
  _timeEnd = end;
  _timeSpeed = speed;
}

void ImageIoInfo::time(const Seq & in)
{
  _timeStart = in.start();
  _timeEnd = in.end();
}

void ImageIoInfo::timeStart(int in) { _timeStart = in; }
void ImageIoInfo::timeEnd(int in) { _timeEnd = in; }
void ImageIoInfo::timeSpeed(double in) { _timeSpeed = in; }

ImageIo::~ImageIo() {}

ImageLoad::~ImageLoad() {}

bool ImageLoad::get(Image * out, int proxy, int format, int type, int frame)
{
  DJ_DBG_FNC("dj::ImageLoad::get");
  DJ_DBG_PRN("proxy = " << proxy);
  DJ_DBG_PRN("format = " << format);
  DJ_DBG_PRN("type = " << type);
  DJ_DBG_PRN("frame = " << frame);

  const int _proxy = clamp(proxy, 0, imageIoProxyMax);
  const int proxyScale = imageProxyScale(_proxy);

  // Open.
  //
  ImageIoInfo info;
  if (! imageBegin(frame, &info)) return false;
  
  // Setup.
  //
  out->set(
    Vec2i(
      ceil(info.size().x() / double(proxyScale)),
      ceil(info.size().y() / double(proxyScale))
    ),
    Pixel(info.format(), type != -1 ? PIXEL_TYPE(type) : info.type())
  );
  out->proxy(_proxy);
  out->tagList(info.tagList());
  DJ_DBG_PRN("proxy scale = " << proxyScale);
  DJ_DBG_PRN("image = " << *out);
  
  // Read.
  //
  if (! imageGet(out, proxyScale)) return false;
  
  // Convert.
  //
  if (format != -1 && format != out->format())
    *out = ImageOpConvert::work(*out, Pixel(PIXEL_FORMAT(format), out->type()));
    
  return true;
}

ImageSave::~ImageSave() {}

bool ImageSave::set(const Image & in, int frame)
{
  DJ_DBG_FNC("dj::ImageSave::set");
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("frame = " << frame);

  // Open.
  //
  if (! imageBegin(in, frame)) return false;

  // Write.
  //
  if (! imageSet(in)) return false;
    
  return true;
}

ImageLoadFactory::~ImageLoadFactory()
{
  del();
}

ImageLoad * ImageLoadFactory::pluginFind(const Str & in)
{
  for (ListIt<ImageLoad *> it = _pluginList; it.isValid(); ++it)
    if (lowercase(in) == lowercase(it()->name())) return it();
  return 0;
}

ImageLoad * ImageLoadFactory::pluginCopy(const Str & in)
{
  for (ListIt<ImageLoad *> it = _pluginList; it.isValid(); ++it)
    if (lowercase(in) == lowercase(it()->name()))
      return static_cast<ImageLoad *>(it()->copy());
  return 0;
}

void ImageLoadFactory::pluginAdd(const List<ImageLoad *> & in)
{
  _pluginList += in;
}

bool ImageLoadFactory::init(void * in)
{
  ImageLoadFnc * fnc = (ImageLoadFnc *)dlsym(in, "djImageLoad");
  if (! fnc) return false;
  _pluginList += fnc();
  return true;
}

void ImageLoadFactory::del()
{
  for (ListIt<ImageLoad *> it = _pluginList; it.isValid(); ++it) delete it();
  _pluginList.del();
  PluginFactory::del();
}

ImageSaveFactory::~ImageSaveFactory()
{
  del();
}

ImageSave * ImageSaveFactory::pluginFind(const Str & in)
{
  for (ListIt<ImageSave *> it = _pluginList; it.isValid(); ++it)
    if (lowercase(in) == lowercase(it()->name())) return it();
  return 0;
}

ImageSave * ImageSaveFactory::pluginCopy(const Str & in)
{
  for (ListIt<ImageSave *> it = _pluginList; it.isValid(); ++it)
    if (lowercase(in) == lowercase(it()->name()))
      return static_cast<ImageSave *>(it()->copy());
  return 0;
}

void ImageSaveFactory::pluginAdd(const List<ImageSave *> & in)
{
  _pluginList += in;
}

bool ImageSaveFactory::init(void * in)
{
  ImageSaveFnc * fnc = (ImageSaveFnc *)dlsym(in, "djImageSave");
  if (! fnc) return false;
  _pluginList += fnc();
  return true;
}

void ImageSaveFactory::del()
{
  for (ListIt<ImageSave *> it = _pluginList; it.isValid(); ++it) delete it();
  _pluginList.del();
  PluginFactory::del();
}
  
ImageLoad * imageLoad(
  const Str & fileName, ImageIoInfo * info,
  const Str & pluginName, const PluginAttr & pluginAttr
) {
  DJ_DBG_FNC("dj::imageLoad");
  DJ_DBG_PRN("file name = " << fileName);
  DJ_DBG_PRN("plugin name = " << pluginName);
  DJ_DBG_PRN("plugin attributes = " << pluginAttr);
  
  const Str extension = lowercase(FileInfo(fileName).extension());
  for (
    ConstListIt<ImageLoad *> it = djImageApp->imageLoad().pluginList();
    it.isValid();
    ++it
  ) {
    
    if (! pluginName.isNull()) {
      if (lowercase(pluginName) != lowercase(it()->name())) continue;
    } else {
      if (find(it()->extensionList(), extension) == -1) continue;
    }
    
    ImageLoad * plugin = static_cast<ImageLoad *>(it()->copy());
    plugin->attrSet(pluginAttr);
    ImageIoInfo infoTmp;
    if (plugin->open(fileName, info ? info : &infoTmp)) return plugin;
    delete plugin;
  }

  return 0;
}

ImageSave * imageSave(
  const Str & fileName,
  const ImageIoInfo & info,
  const Str & pluginName, const PluginAttr & pluginAttr
) {
  DJ_DBG_FNC("dj::imageSave");
  DJ_DBG_PRN("file name = " << fileName);
  DJ_DBG_PRN("plugin name = " << pluginName);
  DJ_DBG_PRN("plugin attributes = " << pluginAttr);
  
  const Str extension = lowercase(FileInfo(fileName).extension());
  for (
    ConstListIt<ImageSave *> it = djImageApp->imageSave().pluginList();
    it.isValid();
    ++it
  ) {
    
    if (! pluginName.isNull()) {
      if (lowercase(pluginName) != lowercase(it()->name())) continue;
    } else {
      if (find(it()->extensionList(), extension) == -1) continue;
    }
    
    ImageSave * plugin = static_cast<ImageSave *>(it()->copy());
    plugin->attrSet(pluginAttr);
    if (plugin->open(fileName, info)) return plugin;
    delete plugin;
  }

  return 0;
}

bool imageLoadAttr(
  const Str & pluginName, const Str & in, const StrList & data
) {
  DJ_DBG_FNC("dj::imageLoadAttr");
  DJ_DBG_PRN("plugin name = " << pluginName);
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("data = " << data);

  ImageLoad * plugin = djImageApp->imageLoad().pluginFind(pluginName);
  if (! plugin) return false;
  return plugin->attr(in, data);
}

bool imageSaveAttr(
  const Str & pluginName, const Str & in, const StrList & data
) {
  DJ_DBG_FNC("dj::imageSaveAttr");
  DJ_DBG_PRN("plugin name = " << pluginName);
  DJ_DBG_PRN("in = " << in);
  DJ_DBG_PRN("data = " << data);

  ImageSave * plugin = djImageApp->imageSave().pluginFind(pluginName);
  if (! plugin) return false;
  return plugin->attr(in, data);
}

const StrList pluginNameList(const List<ImageLoad *> & in)
{
  StrList out;
  for (ConstListIt<ImageLoad *> it = in; it.isValid(); ++it)
    out += it()->name();
  return out;
}

const StrList pluginNameList(const List<ImageSave *> & in)
{
  StrList out;
  for (ConstListIt<ImageSave *> it = in; it.isValid(); ++it)
    out += it()->name();
  return out;
}

}
