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

//#define DJ_DBG
#include "djFileBrowser.h"
#include <djUser.h>
#include <djImageOpCore.h>
#include <djIcon.h>
#include <FL/Fl.H>
#include <FL/fl_draw.H>

namespace dj {

static const Str _sizeToStr(uint64_t in)
{
  return Str("%1MB").arg(in / double(megabyte), 0, 2);
}

FileBrowserWidget::FileBrowserWidget() :
  Fl_Browser_(0, 0, 0, 0),
  _items(0), _itemsSize(0),
  _info(false),
  _textHeight(0),
  _nameWidth(0), _typeWidth(0), _sizeWidth(0), _userWidth(0), _permWidth(0),
    _timeWidth(0),
  _images(FILE_BROWSER_IMAGES_OFF)
{
  //Fl_Widget::type(FL_SELECT_BROWSER);
  Fl_Widget::type(FL_HOLD_BROWSER);
  //has_scrollbar(VERTICAL_ALWAYS);
  box(FL_FLAT_BOX);
  textfont(dj::font());
  textsize(dj::fontSize());
  end();

  fl_font(textfont(), textsize());
  _textHeight = textsize();
  _typeWidth = ceil(fl_width("0000"));
  _sizeWidth = ceil(fl_width("0000000000"));
  _userWidth = ceil(fl_width("00000000"));
  _permWidth = ceil(fl_width("000"));
  _timeWidth = ceil(fl_width("000000000000000000000000"));

  callback(widgetCallback, this);
}

FileBrowserWidget::~FileBrowserWidget()
{
  if (_items) delete [] _items;
}

void FileBrowserWidget::set(
  const List<FileInfo> & in,
  bool info, FILE_BROWSER_IMAGES images
) {
  DJ_DBG_FNC("dj::FileBrowserWidget::set");
  DJ_DBG_PRN("info = " << info);
  DJ_DBG_PRN("images = " << images);
  
  fl_font(textfont(), textsize());
  _nameWidth = 0;
  _typeWidth = 0;
  _sizeWidth = 0;
  _userWidth = 0;
  _permWidth = 0;
  _timeWidth = 0;

  if (_items) {
    delete [] _items;
    _items = 0;
  }
  _itemsSize = in.size();
  if (_itemsSize) {
    _items = new FileBrowserItem [_itemsSize];
    for (ConstListIt<FileInfo> it = in; it.isValid(); ++it) {
      _items[it.count()].set(it());
      _nameWidth = max(
        ceil(fl_width(it().get(-1, false).toAscii())), _nameWidth
      );
      _typeWidth = max(
        ceil(fl_width(fileTypeStr()[it().type()].toAscii())), _typeWidth
      );
      _sizeWidth = max(
        ceil(fl_width(_sizeToStr(it().size()).toAscii())), _sizeWidth
      );
      _userWidth = max(
        ceil(fl_width(userToStr(it().user()).toAscii())), _userWidth
      );
      _permWidth = max(
        ceil(fl_width(filePermStr(it().perm()).toAscii())), _permWidth
      );
      _timeWidth = max(
        ceil(fl_width(timeToStr(it().time()).toAscii())), _timeWidth
      );
    }
  }
  DJ_DBG_PRN("items = " << _itemsSize);

  _info = info;
  _images = images;
  _imagesWorkList.del();
  
  new_list();
}

const FileBrowserItem * FileBrowserWidget::get() const
{
  return static_cast<FileBrowserItem *>(Fl_Browser_::selection());
}

void FileBrowserWidget::resize(int x, int y, int w, int h)
{
  Fl_Browser_::resize(x, y, w, h);
  WidgetBase::resize(x, y, w, h);
}

int FileBrowserWidget::visible() const { return Fl_Browser_::visible(); }

int FileBrowserWidget::handle(int in)
{
  switch (in) {
  
    case FL_KEYBOARD:
      switch (Fl::event_key()) {
        case FL_Left: return 0;
        case FL_Right:
        case FL_Enter:
        case FL_KP_Enter: _actionSig.emit(get()); return 1;
      }
      break;
  }
  
  return Fl_Browser_::handle(in);
}
  
void * FileBrowserWidget::item_first() const
{
  return _itemsSize ? &_items[0] : 0;
}

void * FileBrowserWidget::item_next(void * in) const
{
  for (int i = 0; i < _itemsSize - 1; ++i)
    if (in == &_items[i]) return &_items[i + 1];
  return 0;
}

void * FileBrowserWidget::item_prev(void * in) const
{
  for (int i = 1; i < _itemsSize; ++i)
    if (in == &_items[i]) return &_items[i - 1];
  return 0;
}

int FileBrowserWidget::item_height(void * in) const
{
  FileBrowserItem * item = static_cast<FileBrowserItem *>(in);

  int out = layoutIconSize;
  Vec2i imageSize;
  if (_images && item) {
    imageSize = item->imageSize(FILE_BROWSER_IMAGES_LOW == _images);
    out = max(imageSize.y(), out);
  }
  out = max(_textHeight, out);
  return out + layoutWidgetMargin() * 2;
}

int FileBrowserWidget::item_quick_height(void * in) const
{
  return item_height(in);
}

int FileBrowserWidget::item_width(void * in) const
{
  const int margin = layoutWidgetMargin();
  
  int out =
    (_images ? max(layoutThumbnailSize(), layoutIconSize) : layoutIconSize) +
    margin + _nameWidth;
  
  if (_info)
    out +=
      layoutWidgetMargin() +
      _typeWidth + margin * 2 +
      _sizeWidth + margin * 2 +
      _userWidth + margin * 2 +
      _permWidth + margin * 2 +
      _timeWidth + margin;

  return out + margin * 2;
}

void FileBrowserWidget::item_draw(void * in, int x, int y, int w, int h) const
{
  //DJ_DBG_FNC("dj::FileBrowserWidget::item_draw");
  
  FileBrowserWidget * that = const_cast<FileBrowserWidget *>(this);
  FileBrowserItem * item = static_cast<FileBrowserItem *>(in);
  if (! item) return;

  fl_font(textfont(), textsize());
  //fl_color(FL_RED);
  //fl_rect(x, y, w, h);

  const FileInfo & fileInfo = item->get();
  const int visibleWidth =
    this->w() -
    (Fl_Browser_::scrollbar.visible() ? Fl_Browser_::scrollbar.w() : 0) -
    2 * 2;
  const int itemWidth =
    _nameWidth + max(0, (visibleWidth - item_width(0)));
  const int margin = layoutWidgetMargin();

  // Icon/image.
  //
  int _x = x + margin, _y = y + margin;
  Fl_Image * image = 0;
  if (_images) {
    //image = item->image(FILE_BROWSER_IMAGES_LOW == _images);
    //if (image) image->draw(_x, y + h / 2 - image->h() / 2);
    if (! item->imageIsValid()) {
      that->_imagesWorkList += item;
      Fl::remove_idle(imageCallback, that);
      Fl::add_idle(imageCallback, that);
    } else {
      image = item->image(FILE_BROWSER_IMAGES_LOW == _images);
      if (image)
        image->draw(_x, y + h / 2 - image->h() / 2);
    }
  }
  if (! image) {
    Fl_Image * image = 0;
    switch (fileInfo.type()) {
      case FileInfo::SEQ: image = icon("fileSeq"); break;
      case FileInfo::DIR: image = icon("dir"); break;
      default: image = icon("file"); break;
    }
    if (image)
      image->draw(_x, y + h / 2 - image->h() / 2);
  }
  _x +=
    (_images ? max(layoutThumbnailSize(), layoutIconSize) : layoutIconSize) +
    margin;
  
  // Name.
  //
  _y = y - fl_descent() + _textHeight + (h - _textHeight) / 2;
  Str str = item->get().get(-1, false);
  fl_color(textcolor());
  fl_draw(str.toAscii(), _x, _y);
  _x += itemWidth + margin;
  
  // Information.
  //
  if (_info) {

    _x += margin;
    str = fileTypeStr()[fileInfo.type()];
    fl_color(textcolor());
    fl_draw(str.toAscii(), _x + _typeWidth - ceil(fl_width(str.toAscii())), _y);
    _x += _typeWidth + margin * 2;
    
    str = _sizeToStr(fileInfo.size());
    fl_color(textcolor());
    fl_draw(str.toAscii(), _x + _sizeWidth - ceil(fl_width(str.toAscii())), _y);
    _x += _sizeWidth + margin * 2;
    
    str = userToStr(fileInfo.user());
    fl_color(textcolor());
    fl_draw(str.toAscii(), _x + _userWidth - ceil(fl_width(str.toAscii())), _y);
    _x += _userWidth + margin * 2;
    
    str = filePermStr(fileInfo.perm());
    fl_color(textcolor());
    fl_draw(str.toAscii(), _x + _permWidth - ceil(fl_width(str.toAscii())), _y);
    _x += _permWidth + margin * 2;
    
    str = timeToStr(fileInfo.time());
    fl_color(textcolor());
    fl_draw(str.toAscii(), _x + _timeWidth - ceil(fl_width(str.toAscii())), _y);
    _x += _timeWidth + margin;
  }
}

void FileBrowserWidget::widgetCallback()
{
  const FileBrowserItem * item = get();
  if (! item) return;
  
  if (Fl::event_clicks()) {
    Fl::event_clicks(0);
    _actionSig.emit(item);
  } else {
    _sig.emit(item);
  }
}

void FileBrowserWidget::imageCallback()
{
  DJ_DBG_FNC("dj::FileBrowserWidget::imageCallback");
  DJ_DBG_PRN("images = " << _imagesWork.size());
  
  if (_imagesWorkList.size()) {
    FileBrowserItem * item = _imagesWorkList.pop();
    item->image(FILE_BROWSER_IMAGES_LOW == _images);
    redraw_line(item);
  }
  if (! _imagesWorkList.size())
    Fl::remove_idle(imageCallback, this);
}

}
