/*------------------------------------------------------------------------------
 src/bin/djview/app.cxx

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

//#define DJ_DBG
#include "app.h"
#include "widget.h"
#include "window.h"
#include "magnifyTool.h"
#include "colorSampleTool.h"
#include "imageInfoTool.h"
#include "imageOpTool.h"
#include "prefsDialog.h"
#include "aboutDialog.h"
#include <djImageOpStackWidget.h>
#include <FL/Fl.H>

namespace djview {

App::App(int & argc, char ** argv) :
  dj::App("djview", argc, argv),
  _imageCacheEnabled(true),
  _filePixelFormat(0), _filePixelType(1 + PIXEL_U8),
  _windowFileFit(true), _windowFullscreenFit(true),
  _viewBg(Color(0.0)),
  _playbackFile(true), _playbackFileSpeed(true), _playbackEveryFrame(true),
  _magnifyTool(0), _colorSampleTool(0), _imageInfoTool(0),
    _imageOpTool(0),
  _prefsDialog(0), _aboutDialog(0),
  _callbacks(true)
{
  DJ_DBG_FNC("App::App");
  
  // Command line.
  //
  StrList argList;
  for (int i = 1; i < argc; ++i) {
    const Str it = argv[i];
    if (Str("-help") == it || Str("-h") == it) { help(); return; }
    else if (Str("-about") == it) { about(); return; }
    else argList += it;
  }
  const Str err = cmdln(argList);
  if (! err.isNull()) {
    error(Str("Error parsing command line \"%1\".").arg(err));
    exitValue(1);
    return;
  }
  valid(true);
  
  // Image operations.
  //
  for (ConstListIt<ImageOp *> it = imageOp().pluginList(); it.isValid(); ++it)
    if (1 == it()->inputNameList().size())
      _imageOpList += it()->name();
  
  // Widgets.
  //
  _magnifyTool = new MagnifyTool;
  _colorSampleTool = new ColorSampleTool;
  _imageInfoTool = new ImageInfoTool;
  _imageOpTool = new ImageOpTool;
  _prefsDialog = new PrefsDialog;
  _aboutDialog = new AboutDialog;

  // Initialize.
  //
  prefsGet();
  
  if (_inputList.size())
    for (StrListIt it = _inputList; it.isValid(); ++it)
      windowNew(it());
  else
    windowNew();
    
  // Callbacks.
  //
  _imageOpTool->updateSig().set(this, imageOpToolCallback);
  _imageOpTool->stackUpdateSig().set(this, imageOpToolCallback);
}

App::~App()
{
  DJ_DBG_FNC("App::~App");
  
  if (isValid()) prefsSet();

  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    delete it();
  if (_magnifyTool) delete _magnifyTool;
  if (_colorSampleTool) delete _colorSampleTool;
  if (_imageInfoTool) delete _imageInfoTool;
  if (_imageOpTool) delete _imageOpTool;
  if (_prefsDialog) delete _prefsDialog;
  if (_aboutDialog) delete _aboutDialog;
}

void App::imageCacheEnabled(bool in)
{
  _imageCacheEnabled = in;
  if (! _imageCacheEnabled) _imageCache.del();
}

void App::fileReload(const Str & in)
{
  DJ_DBG_FNC("App::fileReload");

  _imageCache.del(in);

  DJ_DBG_PRN("windows = " << _windowList.size());
  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    if (in == it()->fileName())
      it()->fileReload();
}

void App::fileRecentList(const Str & in)
{
  DJ_DBG_FNC("App::fileRecentList");
  
  const int i = find(_fileRecentList, in);
  if (i < 0) {
    _fileRecentList.push(in);
    while (_fileRecentList.size() > 10)
      _fileRecentList.shift();
  } else {
    _fileRecentList.push(_fileRecentList.del(i));
  }
  
  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    it()->fileRecentListUpdate();
}

void App::filePixelFormat(int in)
{
  _filePixelFormat = in;
}

void App::filePixelType(int in)
{
  _filePixelType = in;
}

void App::windowNew(const Str & fileName)
{
  DJ_DBG_FNC("App::windowNew");
  DJ_DBG_PRN("file name = " << fileName);

  Window * window = new Window;
  _windowList += window;
  window->show();
  window->fileOpen(fileName);
}

void App::windowCopy(Window * in)
{
  DJ_DBG_FNC("App::windowCopy");
  
  Window * window = new Window;
  _windowList += window;
  window->set(in);
  window->show();
}

void App::windowDel(Window * in)
{
  DJ_DBG_FNC("App::windowDel");
  
  // XXX Messy.
  //
  in->hide();
  in->die();
  _windowList.del(find(_windowList, in));
  dj::App::windowDel(in);
  Fl::delete_widget(in);
  
  if (! _windowList.size()) exit();
}

void App::windowFileFit(bool in)
{
  _windowFileFit = in;
}

void App::windowFullscreenFit(bool in)
{
  _windowFullscreenFit = in;
}

void App::viewBg(const Color & in)
{
  _viewBg = in;
  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    it()->viewBg(_viewBg);
}

void App::playbackFile(bool in)
{
  _playbackFile = in;
}

void App::playbackFileSpeed(bool in)
{
  _playbackFileSpeed = in;
}

void App::playbackEveryFrame(bool in)
{
  _playbackEveryFrame = in;
}

void App::imageOpToolCallback(bool)
{
  if (! _callbacks) return;
  imageOpToolUpdate();
}

void App::imageOpToolUpdate()
{
  DJ_DBG_FNC("App::imageOpToolUpdate");

  _callbacks = false;
  
  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    it()->imageOpUpdate();
    
  _callbacks = true;
}

void App::imageOpToolCallback(int in)
{
  if (! _callbacks) return;
  imageOpToolUpdate(in);
}

void App::imageOpToolUpdate(int in)
{
  DJ_DBG_FNC("App::imageOpToolUpdate");
  DJ_DBG_PRN("in = " << in);

  _callbacks = false;
  
  for (ListIt<Window *> it = _windowList; it.isValid(); ++it)
    it()->imageOpUpdate(in);
    
  _callbacks = true;
}

void App::prefsDialog()
{
  _prefsDialog->show();
}

void App::prefsSet()
{
  DJ_DBG_FNC("App::prefsSet");
  
  Prefs prefsThis(prefs(), "djview");
  
  dj::prefs(_imageOpStack, "imageOpStack", &prefsThis);
  prefsThis.set("imageOpActive", _imageOpTool->stackActive());
  prefsThis.set("imageCacheSize", _imageCache.max());
  prefsThis.set("imageCacheEnabled", _imageCacheEnabled);
  
  prefsThis.set("fileRecentList", _fileRecentList);
  prefsThis.set("filePixelFormat", _filePixelFormat);
  prefsThis.set("filePixelType", _filePixelType);
  
  prefsThis.set("windowFileFit", _windowFileFit);
  prefsThis.set("windowFullscreenFit", _windowFullscreenFit);
  
  prefsThis.set("viewBg", _viewBg);
  
  prefsThis.set("playbackFile", _playbackFile);
  prefsThis.set("playbackFileSpeed", _playbackFileSpeed);
  prefsThis.set("playbackEveryFrame", _playbackEveryFrame);
    
  _magnifyTool->prefs(&prefsThis);
  _colorSampleTool->prefs(&prefsThis);
}

void App::prefsGet()
{
  DJ_DBG_FNC("App::prefsGet");
  
  Prefs prefsThis(prefs(), "djview");
  
  if (! prefsInit()) imageOpDefault();
  dj::prefs(&_imageOpStack, "imageOpStack", prefsThis);
  int active = -1;
  prefsThis.get("active", &active, active);
  _imageOpTool->stackActive(active);
  int imageCacheSize = 0;
  prefsThis.get("imageCacheSize", &imageCacheSize, _imageCache.max());
  _imageCache.max(imageCacheSize);
  prefsThis.get("imageCacheEnabled", &_imageCacheEnabled, _imageCacheEnabled);
  
  prefsThis.get("fileRecentList", &_fileRecentList);
  prefsThis.get("filePixelFormat", &_filePixelFormat, _filePixelFormat);
  prefsThis.get("filePixelType", &_filePixelType, _filePixelType);
  
  prefsThis.get("windowFileFit", &_windowFileFit, _windowFileFit);
  prefsThis.get(
    "windowFullscreenFit", &_windowFullscreenFit, _windowFullscreenFit
  );
  
  prefsThis.get("viewBg", &_viewBg, _viewBg);
  
  prefsThis.get("playbackFile", &_playbackFile, _playbackFile);
  prefsThis.get("playbackFileSpeed", &_playbackFileSpeed, _playbackFileSpeed);
  prefsThis.get(
    "playbackEveryFrame", &_playbackEveryFrame, _playbackEveryFrame
  );
  
  _magnifyTool->prefs(prefsThis);
  _colorSampleTool->prefs(prefsThis);
  
  _imageOpTool->widgetUpdate();
  _prefsDialog->widgetUpdate();
}

void App::aboutDialog()
{
  _aboutDialog->show();
}

const Str App::cmdln(StrList & in)
{
  // Options.
  //
  StrList argList;
  while (in.size()) {
    const Str it = in[0];
    in.pop();
    
    if (0) {
      
    } else argList += it;
  }
  
  // Arguments.
  //
  _inputList = argList;
  
  return Str();
}

static const char helpStr [] =
"\n"
"djview\n"
"------\n"
"\n"
"Image and movie viewer.\n"
"\n"
"Usage\n"
"-----\n"
"\n"
"djview [input]... [option]...\n"
"\n"
"Arguments\n"
"---------\n"
"\n"
"input - Input image. Multiple inputs may be given.\n"
"\n"
"General Options\n"
"---------------\n"
"\n"
"-help, -h - Display help message.\n"
"\n"
"-about - Display about message.\n";

void App::help()
{
  printFormatted(Str(helpStr));
}

}

int main(int argc, char ** argv)
{
  DJ_DBG_LOG();
  return djview::App(argc, argv).exec();
}
