/*------------------------------------------------------------------------------
 src/bin/djinfo/djinfo.cxx

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

//#define DJ_DBG
#include <djDir.h>
#include <djImageIo.h>
#include <djImageApp.h>

namespace djinfo {
using namespace dj;

class App : public ImageApp {
public:

  App(int &, char **);

private:

  bool printItem(const FileInfo &, bool path = false);
  bool printDir(const FileInfo &, bool label);
  Str cmdln(StrList &);
  void help();

  StrList _inputList;
  bool _seq;
  bool _recurse;
  bool _verbose;
  int _columns;
  Str _load;
  Map<Str, PluginAttr> _loadAttr;
};

App::App(int & argc, char ** argv) :
  ImageApp("djinfo", argc, argv),
  _seq(true), _recurse(false),
  _verbose(false), _columns(terminalWidth())
{
  DJ_DBG_FNC("djinfo::App::App");
    
  // Command line.
  //
  if (1 == argc) { help(); return; }
  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;
  }
  
  // Setup input.
  //
  if (! _inputList.size()) _inputList += ".";
  DJ_DBG_PRN("inputs = " << _inputList);
  List<FileInfo> itemList;
  for (StrListIt it = _inputList; it.isValid(); ++it) {
    FileInfo fileInfo(it());
    fileInfo.stat();
    if (! _seq && fileInfo.isSeq())
      for (int i = fileInfo.seq().start(); i <= fileInfo.seq().end(); ++i)
        itemList += fileInfo.get(i);
    else
      itemList += fileInfo;
  }
  if (_seq) itemList = seq(itemList);

  // Set image I/O attributes.
  //
  const StrList keyList = _loadAttr.keyList();
  for (ConstStrListIt it = keyList; it.isValid(); ++it) {
    const StrList attrList = _loadAttr[it()].keyList();
    for (ConstStrListIt name = attrList; name.isValid(); ++name)  
      if (! imageLoadAttr(it(), name(), _loadAttr[it()].get(name()))) {
        error(Str("Error parsing image load attribute \"%1\".").arg(name()));
        exitValue(1);
        return;
      }
  }
  
  // Work.
  //
  bool r = true;
  for (ListIt<FileInfo> it = itemList; it.isValid(); ++it)
    if (FileInfo::DIR == it().type())
      r &= printDir(it(), (itemList.size() > 1) || _recurse);
    else {
      if (! printItem(it(), true)) {
        error(Str("Cannot open \"%1\".").arg(it()));
        r = false;
      }
    }
  if (! r) exitValue(1);
}

bool App::printItem(const FileInfo & in, bool path)
{
  DJ_DBG_FNC("djinfo::App::printItem");
  DJ_DBG_PRN("in = " << in);
  
  // Open image.
  //
  ImageIoInfo info;
  ImageLoad * load = dj::imageLoad(in, &info, _load);
  if (! load) return false;
  delete load;
  
  // Print information.
  //
  const Str fileName = in.get(-1, path);
  const Vec2i size = info.size();
  const Pixel pixel = info.pixel();
  if (! _verbose) {
  
    Str tmp = fileName;
    
    int columns [] = { 0, 0, 0, 0, 0 };
    if (_columns) {
      columns[0] = _columns - 44;
      if (tmp.size() > columns[0]) {
        tmp = cut(tmp, 0, columns[0]);
        int i = tmp.size(), j = 3;
        while (i-- > 0 && j-- > 0) tmp[i] = '.';
      }
      columns[1] = 14;
      columns[2] = 4;
      columns[3] = 3;
      columns[4] = 18;
    }
    
    const Str sizeStr = Str("%1x%2:%3").
      arg(size.x()).arg(size.y()).arg(aspect(size), 0, 2);
    const Str timeStr = Str("%1-%2:%3").
      arg(info.timeStart()).arg(info.timeEnd()).arg(info.timeSpeed(), 0, 2);
    print(
      Str("%1 %2 %3 %4 %5").
        arg(tmp, -columns[0]).
        arg(sizeStr, columns[1]).
        arg(pixelFormatStr()[pixel.format()], columns[2]).
        arg(pixelTypeStr()[pixel.type()], columns[3]).
        arg(timeStr, columns[4])
    );
    
  } else {
  
    print(fileName);
    printSeparator();
    
    print(Str("  size = %1 %2").arg(size.x()).arg(size.y()));
    print(Str("  aspect = %1").arg(aspect(size), 0, 2));
    print(
      Str("  pixel = %1 %2").
        arg(pixelFormatStr()[pixel.format()]).
        arg(pixelTypeStr()[pixel.type()])
    );
    print(Str("  time = %1 %2").arg(info.timeStart()).arg(info.timeEnd()));
    print(Str("  speed = %1").arg(info.timeSpeed(), 0, 2));
    printSeparator();
    
    const ImageTagList & tagList = info.tagList();
    const StrList keyList = tagList.keyList();
    for (ConstStrListIt it = keyList; it.isValid(); ++it)
      print(Str("  %1 = %2").arg(it()).arg(tagList[it()]));
    printSeparator();
  }
  
  return true;
}

bool App::printDir(const FileInfo & in, bool label)
{
  DJ_DBG_FNC("djinfo::App::printDir");
  DJ_DBG_PRN("in = " << in);
  
  // Setup directory.
  //
  Dir dir(in);
  if (! dir.isValid()) {
    error(Str("Cannot open \"%1\".").arg(in));
    return false;
  }
  List<FileInfo> itemList = dir.itemList();
  if (_seq) itemList = seq(itemList);

  // Print items.
  //
  if (label) print(Str("%1:").arg(in));
  for (ListIt<FileInfo> it = itemList; it.isValid(); ++it)
    printItem(it());
  printSeparator();
  
  // Recurse.
  //
  bool r = true;
  if (_recurse) {
    Dir dir(in);
    const List<FileInfo> itemList = filter(dir.itemList(), FILE_FILTER_DIRS);
    for (ConstListIt<FileInfo> it = itemList; it.isValid(); ++it)
      r &= printDir(it(), label);
  }
  return r;
}

Str App::cmdln(StrList & in)
{
  StrList argList;
  
  // Options.
  //
  while (in.size()) {
    const Str it = in.pop();
    if (Str("-sequence") == it || Str("-q") == it) {
      if (! inflateStr(in, &_seq)) return it;
    } else if (Str("-recurse") == it || Str("-r") == it) {
      _recurse = true;
    } else if (it == Str("-verbose") || it == Str("-v")) {
      _verbose = true;
    } else if (Str("-columns") == it || Str("-c") == it) {
      if (! inflateStr(in, &_columns)) return it;
    } else if (Str("-load") == it || Str("-l") == it) {
      if (! inflateStr(in, &_load)) return it;
    } else if (Str("-loadAttr") == it || Str("-la") == it) {
      Str plugin, attr, value;
      if (! inflateStr(in, &plugin)) return it;
      if (! inflateStr(in, &attr)) return it;
      if (! inflateStr(in, &value)) return it;
      _loadAttr[plugin].set(attr, deflateStr(value));
    } else
      argList += it;
  }
  
  // Arguments.
  //
  _inputList = argList;
  
  return Str();
}

static const char helpStr [] =
"\n"
"djinfo\n"
"------\n"
"\n"
"Image information utility.\n"
"\n"
"Usage\n"
"-----\n"
"\n"
"djinfo (input)... [option]...\n"
"\n"
"Arguments\n"
"---------\n"
"\n"
"input - Input image or directory. Multiple inputs may be given.\n"
"\n"
"Options\n"
"-------\n"
"\n"
"-sequence, -q (value) - File sequencing. Values: %1. Default: %2.\n"
"\n"
"-recurse, -r - Descend into sub-directories.\n"
"\n"
"Display Options\n"
"---------------\n"
"\n"
"-verbose, -v - Display verbose information including image tags.\n"
"\n"
"-columns, -c (value) - Number of output columns. A value of zero disables "
"formatting. Default: %3.\n"
"\n"
"Load Options\n"
"------------\n"
"\n"
"-load, -l (plugin) - Specify image load plugin. Plugins: %4.\n"
"\n"
"-loadAttr, -la (plugin) (attr) (value) - Set an image load attribute.\n"
"\n"
"General Options\n"
"---------------\n"
"\n"
"-help, -h - Display help message.\n"
"\n"
"-about - Display about message.\n"
"\n"
"Example Output\n"
"--------------\n"
"\n"
"bart.134-250.jpeg                 320x240:1.33   RGB  U8        134-250:0.00\n"
"train.152-500.jpeg                640x480:1.33   RGB  U8        152-500:0.00\n"
"waterfall.167-199.jpeg            640x480:1.33   RGB  U8        167-199:0.00\n"
"(1)                               (2)            (3)            (4)\n"
"\n"
"1. Image name.\n"
"2. Width, height, and aspect ratio.\n"
"3. Pixel format and type.\n"
"4. First frame, last frame, and frames per second.\n"
"\n"
"Examples\n"
"--------\n"
"\n"
"Image information:\n"
"> djinfo image1.sgi image2.sgi\n"
"\n"
"Image sequence information:\n"
"> djinfo image.1-100.sgi\n"
"\n"
"Verbose information:\n"
"> djinfo -v image1-100.sgi\n"
"\n"
"Video lookup table information:\n"
"> djinfo $DISPLAY -load vlut\n";

void App::help()
{
  printFormatted(
    Str(helpStr).
      arg(join(boolStr(), Str(", "))).arg(boolStr()[_seq]).
      arg(_columns).
      arg(join(pluginNameList(imageLoad().pluginList()), Str(", ")))
  );
}

}

int main(int argc, char ** argv)
{
  DJ_DBG_LOG();
  int exitValue = 0;
do {
  djinfo::App app(argc, argv);
  exitValue = app.exitValue();
} while (0);
  return exitValue;
}
