/*
 * ViewerApp.cpp
 *
 * (c) 2002 by Thorsten Scheuermann <tscheuermann@gmx.net>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include "ViewerApp.h"
#include "ViewerGlobals.h"
#include "ViewerSettings.h"

#include <qwidgetstack.h>
#include <qpe/applnk.h>
#include <qpe/qpeapplication.h>
#include <qpe/qpetoolbar.h>
#include <qpe/resource.h>
#include <qaction.h>
#include <qstring.h>
#include <qlistview.h>
#include <qpopupmenu.h>
#include <qtoolbutton.h>
#include <qlabel.h>
#include <qstatusbar.h>
#include <qlist.h>
#include <qcopchannel_qws.h>

#include "ImageInfo.h"
#include "ImageSet.h"
#include "Bookmark.h"

#include "Display.h"
#include "Image.h"
#include "ImageReader.h"
#include "ImageReaderFactory.h"
#include "JPEGReader.h"
#include "ImageScaler.h"
#include "ImageRotator.h"

#include "ImageCanvas.h"
#include "SettingsDialog.h"
#include "ImageListView.h"
#include "BookmarkMenu.h"
#include "AboutDialog.h"

// ---------------------------------------------------------------------------
ViewerApp::ViewerApp()
: QMainWindow()
{
  QAction *a;
  Bookmark *currBm;

  setCaption("MooView");
  setToolBarsMovable(false);

  startedFromDocTab = false;
  imgSet = NULL;
  currentInfo = NULL;
  currentIndex = -1;
  currentImage = NULL;
  imgCanvas = NULL;
  settings = ViewerSettings::getInstance();
  imgSet = new ImageSet("", this);
  channel = new QCopChannel("QPE/Card", this);

  // initialize tool bar
  toolBar = new QPEToolBar(this);
  bookmarkButton = new QToolButton(Resource::loadPixmap("mooview/Bookmarks"),
                                   "Bookmarks", "", this, SLOT(showBookmarkMenu()),
                                   toolBar);
  toolBar->addSeparator();
  a = new QAction("Settings", Resource::loadPixmap("mooview/SettingsIcon"),
                  "Settings", 0, this);
  connect(a, SIGNAL(activated()), this, SLOT(showSettingsDialog()));
  a->addTo(toolBar);
  QWidget *stretchWidget = new QWidget(toolBar);
  toolBar->setStretchableWidget(stretchWidget);
  a = new QAction("About...", Resource::loadPixmap("mooview/AboutIcon"),
                  "About...", 0, this);
  connect(a, SIGNAL(activated()), this, SLOT(showAboutDialog()));
  a->addTo(toolBar);

  bookmarkMenu = new BookmarkMenu(this);

  // initialize status bar
  statusLabel = new QLabel(statusBar());
  statusBar()->addWidget(statusLabel, 1);

  stack = new QWidgetStack(this);
  imgListView = new ImageListView(stack);
//  imgListView->setImageSet(imgSet);

  setCentralWidget(stack);
  stack->raiseWidget(imgListView);
  stack->show();

  currBm = settings->currentBookmark();
  if(currBm)
  {
    if(currBm->available())
      changeDirectory(*currBm);
    else
      changeToDocuments();
  }
  else
  {
    // default to Documents tab
    changeToDocuments();
  }

  connect(channel, SIGNAL(received(const QCString&, const QByteArray&)),
          this, SLOT(cardMessage(const QCString &, const QByteArray &)));
  connect(this, SIGNAL(cardStatusChanged()), bookmarkMenu, SLOT(update()));
  connect(this, SIGNAL(cardStatusChanged()), imgSet, SLOT(update()));
  connect(imgSet, SIGNAL(changed()), imgListView, SLOT(updateList()));
  connect(this, SIGNAL(settingsChanged()), bookmarkMenu, SLOT(update()));
  connect(bookmarkMenu, SIGNAL(documentsSelected()), this,
          SLOT(changeToDocuments()));
  connect(bookmarkMenu, SIGNAL(bookmarkSelected(const Bookmark&)), this,
          SLOT(changeDirectory(const Bookmark&)));
  connect(imgListView, SIGNAL(fileSelected(ImageInfo*)),
          this, SLOT(showImage(ImageInfo*)));
  show();
  imgListView->setImageSet(imgSet);
}
// ---------------------------------------------------------------------------
ViewerApp::~ViewerApp()
{
  delete imgCanvas;

  if(currentImage)
    delete currentImage;

  // save current directory/bookmark
  settings->save();

  // kind of a hack here...
  delete settings;
}
// ---------------------------------------------------------------------------
void ViewerApp::setDocument(const QString& appLnkFileName)
{
  DocLnk dl(appLnkFileName);
  ImageInfo iInfo(dl.file());

  startedFromDocTab = true;
  changeToDocuments();
  currentIndex = -1;
  showImage(&iInfo);
}
// ---------------------------------------------------------------------------
void ViewerApp::loadImage(const QString& fileName)
{
  ImageReader *reader = NULL;
  ImageScaler *scaler = NULL;
  ImageRotator *rotator = NULL;
  int imgWidth, imgHeight;
  int rotation;
  bool doScale, doRotate;

  if(currentImage)
    delete currentImage;    // make sure that no ImageCanvas is displaying it anymore

  reader = ImageReaderFactory::getReader(fileName);
  if(!reader)
    return;

  // figure out optimal image size
  optImageSize(Display::width(), Display::height(), reader,
               settings->allowRotation(), imgWidth, imgHeight,
               doScale, doRotate);

  // set up pipeline
  if(doScale)
  {
    scaler = new ImageScaler;
    scaler->setOutputDimension(imgWidth, imgHeight);
    reader->setConsumer(scaler);
  }

  // compensate for hardware display rotation (e.g. on Zaurus)
  rotation = (4 - Display::rotation()) % 4;
  if(doRotate)
  {
    // add rotation for optimal screen space utilization
    rotation += settings->rotation();
    rotation %= 4;
  }
  if(rotation)
  {
    // add rotation element do processing chain
    rotator = new ImageRotator;
    rotator->setRotation((RotationMode)rotation);
    if(doScale)
      scaler->setConsumer(rotator);
    else
     reader->setConsumer(rotator);
  }

  currentImage = new Image;
  if(rotator)
    rotator->setDestImage(currentImage);
  else if(scaler)
    scaler->setConsumer(currentImage);
  else
    reader->setConsumer(currentImage);

  // load the image
  reader->readImageData();

  // clean up
  delete reader;
  if(scaler)
    delete scaler;
  if(rotator)
    delete rotator;
}
// ---------------------------------------------------------------------------
void ViewerApp::showImage(ImageInfo *imgInfo)
{
  // load image
  loadImage(imgInfo->fullFileName());
  currentInfo = imgInfo;
  currentIndex = imgSet->children().findRef(currentInfo);
  imgCanvas = new ImageCanvas(NULL);
  imgCanvas->installEventFilter(this);
  imgCanvas->setImage(currentImage);
  imgCanvas->showFullScreen();
}
// ---------------------------------------------------------------------------
void ViewerApp::showNewImage()
{
  if(!currentInfo)
    return;
  loadImage(currentInfo->fullFileName());
  imgCanvas->setImage(currentImage);
}
// ---------------------------------------------------------------------------
bool ViewerApp::eventFilter(QObject *o, QEvent *e)
{
  if(o != imgCanvas)
    return false;

  switch(e->type())
  {
  case QEvent::KeyPress:
    switch(((QKeyEvent *)e)->key())
    {
    case Qt::Key_Escape:
      if(startedFromDocTab)
      {
        // exit application
        imgCanvas->hide();
        close();
        return true;
      }
      updateListViewSelection();
      imgCanvas->hide();
      delete imgCanvas;
      imgCanvas = NULL;
      delete currentImage;
      currentImage = NULL;
      return true;

    case Qt::Key_Up:
      if(currentIndex <= 0)
        return true;
      currentIndex--;
      currentInfo = imgSet->children().at(currentIndex);
      showNewImage();
      return true;

    case Qt::Key_Down:
      if(currentIndex >= (int)imgSet->children().count()-1)
        return true;
      currentIndex++;
      currentInfo = imgSet->children().at(currentIndex);
      showNewImage();
      return true;

    default:
      return false;
    }
    break;

  default:
    break;
  }
  return QWidget::eventFilter(o, e);
}
// ---------------------------------------------------------------------------
void ViewerApp::updateListViewSelection()
{
  QListViewItemIterator lvit(imgListView);

  for( ; lvit.current(); ++lvit)
  {
    if(((ImageListViewItem *)lvit.current())->imageInfo() == currentInfo)
    {
      imgListView->setSelected(lvit.current(), true);
      imgListView->ensureItemVisible(lvit.current());
      return;
    }
  }
}
// ---------------------------------------------------------------------------
void ViewerApp::showSettingsDialog()
{
  SettingsDialog sd(this);
  sd.initialize(settings);
  if(sd.exec())
  {
    sd.getSettings(settings);
    settings->save();
    emit settingsChanged();
  }
}
// ---------------------------------------------------------------------------
void ViewerApp::showAboutDialog()
{
  AboutDialog ad(this, "aw", true);

  ad.mooIconLabel->setPixmap(Resource::loadPixmap("MooView"));
  ad.showMaximized();
  ad.exec();
}
// ---------------------------------------------------------------------------
void ViewerApp::showBookmarkMenu()
{
  bookmarkMenu->popupMenu(bookmarkButton->mapToGlobal(QPoint(0,
                          bookmarkButton->height())));
}
// ---------------------------------------------------------------------------
void ViewerApp::changeDirectory(const Bookmark& bm)
{
  QString s("Browsing '%1'");

  imgSet->setBaseDirectory(bm.fullPath());
  statusLabel->setText(s.arg(bm.name()));
  settings->setCurrentBookmark(&bm);
}
// ---------------------------------------------------------------------------
void ViewerApp::changeToDocuments()
{
  imgSet->setBaseDirectory("");
  statusLabel->setText("Browsing 'Documents'");
  settings->setCurrentBookmark(NULL);
}
// ---------------------------------------------------------------------------
void ViewerApp::cardMessage(const QCString &msg, const QByteArray &)
{
  if(msg == "mtabChanged()")
    emit cardStatusChanged();
}
// ---------------------------------------------------------------------------
void ViewerApp::optImageSize(int bbWidth, int bbHeight, ImageReader *reader,
                             bool allowRot, int &imgWidth, int &imgHeight,
                             bool &scale, bool &rotate)
{
  int rotWidth, rotHeight;
  static int jpegScaleFactors[4] = {1, 2, 4, 8};

  rotate = false;

  if((reader->imageWidth() <= bbWidth) && (reader->imageHeight() <= bbHeight))
  {
    // no scaling necessary
    imgWidth = reader->imageWidth();
    imgHeight = reader->imageHeight();
    scale = false;
  }
  else
  {
    // determine max. dimensions without rotation
    maxBBFit(bbWidth, bbHeight, reader->imageWidth(), reader->imageHeight(),
            imgWidth, imgHeight);

    if(!allowRot)
      scale = true;
    else
    {
      // determine max. dimensions with rotation
      maxBBFit(bbHeight, bbWidth, reader->imageWidth(), reader->imageHeight(),
               rotWidth, rotHeight);

      if((imgWidth*imgHeight) < (rotWidth * rotHeight))
      {
        // rotated image will provide better utilization of bounding box
        imgWidth = rotWidth;
        imgHeight = rotHeight;
        rotate = true;
        if((imgWidth < reader->imageWidth()) || (imgHeight < reader->imageHeight()))
          scale = true;
        else
          scale = false;
      }
    }
  }

  // if we have a JPEGReader configure proper pre-scaling to increase performance
  if(reader->type() == QString("JPEG"))
  {
    int scaledWidth;
    int scaledHeight;

    for(int i=0; i<4; i++)
    {
      scaledWidth = reader->imageWidth()/jpegScaleFactors[i];
      scaledHeight = reader->imageHeight()/jpegScaleFactors[i];
      scaledWidth += (reader->imageWidth() % jpegScaleFactors[i]) ? 1 : 0;
      scaledHeight += (reader->imageHeight() % jpegScaleFactors[i]) ? 1 : 0;

      if((scaledWidth >= imgWidth) && (scaledHeight >= imgHeight))
      {
        ((JPEGReader *)reader)->setScaling(jpegScaleFactors[i]);
        if((scaledWidth == imgWidth) && (scaledHeight == imgHeight))
          scale = false;
      }
      else
        break;
    }
  }
}
// ---------------------------------------------------------------------------
void ViewerApp::maxBBFit(int bWidth, int bHeight, int oWidth, int oHeight,
                         int &maxWidth, int &maxHeight)
{
  float oRatio, bRatio;

  oRatio = oWidth/(float)oHeight;
  bRatio = bWidth/(float)bHeight;

  if(oRatio >= bRatio)
  {
    maxWidth = bWidth;
    maxHeight = (int)((bWidth / oRatio) + 0.5);
    if(maxHeight < 1)
      maxHeight = 1;
    else if(maxHeight > bHeight)
      maxHeight = bHeight;
  }
  else
  {
    maxWidth = (int)((bHeight * oRatio) + 0.5);
    maxHeight = bHeight;
    if(maxWidth < 1)
      maxWidth = 1;
    else if(maxWidth > bWidth)
      maxWidth = bWidth;
  }
}
// ---------------------------------------------------------------------------
