/*
 * This file is part of the KFTPGrabber project
 *
 * Copyright (C) 2003-2005 by the KFTPGrabber developers
 * Copyright (C) 2003-2005 Jernej Kos <kostko@jweb-network.net>
 * Copyright (C) 2004-2005 Max Howell <max.howell@methylblue.com>
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful, but
 * is provided AS IS, WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, and
 * NON-INFRINGEMENT.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations
 * including the two.
 * You must obey the GNU General Public License in all respects
 * for all of the code used other than OpenSSL.  If you modify
 * file(s) with this exception, you may extend this exception to your
 * version of the file(s), but you are not obligated to do so.  If you
 * do not wish to do so, delete this exception statement from your
 * version.  If you delete this exception statement from all source
 * files in the program, then also delete it here.
 */

#include "misc.h"
#include "sidebar.h"
#include "multitabbar.h"

#include <kapplication.h>
#include <kconfig.h>
#include <kiconloader.h>
#include <klocale.h>

#include <qcursor.h>
#include <qpainter.h>
#include <qsignalmapper.h>
#include <qstyle.h>

namespace KFTPWidgets {

class Splitter : public QWidget {
public:
    Splitter(Sidebar *w)
      : QWidget(w, "divider"),
        m_position(w->m_position)
    {
      if (m_position == Sidebar::Left)
        setCursor(QCursor(SplitHCursor));
      else
        setCursor(QCursor(SplitVCursor));
        
      styleChange(style());
    }

    virtual void paintEvent(QPaintEvent*)
    {
      QPainter p(this);
      parentWidget()->style().drawPrimitive(QStyle::PE_Splitter, &p, rect(), colorGroup(), m_position == Sidebar::Left ? QStyle::Style_Horizontal : 0);
    }

    virtual void styleChange(QStyle&)
    {
      if (m_position == Sidebar::Left)
        setFixedWidth(style().pixelMetric(QStyle::PM_SplitterWidth, this));
      else
        setFixedHeight(style().pixelMetric(QStyle::PM_SplitterWidth, this));
    }

    virtual void mouseMoveEvent(QMouseEvent *e)
    {
      static_cast<Sidebar*>(parent())->mouseMovedOverSplitter(e);
    }
private:
    Sidebar::Position m_position;
};

Sidebar::Sidebar(QWidget *parent, Position position)
  : QWidget(parent, "Sidebar"),
    m_position(position),
    m_divider(new KFTPWidgets::Splitter(this)),
    m_content(new QVBox(this)),
    m_tabBar(new MultiTabBar(position == Left ? MultiTabBar::Vertical : MultiTabBar::Horizontal, this)),
    m_sidebarBox(new QWidget(this)),
    m_currentIndex(-1),
    m_lastIndex(-1),
    m_mapper(new QSignalMapper(this))
{
  m_tabBar->setStyle(MultiTabBar::AMAROK);
  m_tabBar->setPosition(m_position == Left ? MultiTabBar::Left : MultiTabBar::Bottom);
  m_tabBar->showActiveTabTexts(true);
    
  if (m_position == Left) {
    m_pos = m_tabBar->sizeHint().width() + 5;

    m_tabBar->setFixedWidth(m_pos);
    m_tabBar->move(0, 3);
  
    QVBoxLayout *layout = new QVBoxLayout(m_sidebarBox);
    layout->addSpacing(3);
    layout->setAutoAdd(true);

    m_sidebarBox->move(m_pos, 0);
    m_sidebarBox->hide();
    m_divider->hide();
    m_content->setSpacing(1);
  } else {
    m_pos = m_tabBar->sizeHint().height() + 5;
    m_tabBar->setFixedHeight(m_pos);
    
    QVBoxLayout *layout = new QVBoxLayout(m_sidebarBox);
    layout->setAutoAdd(true);
    
    m_sidebarBox->hide();
    m_divider->hide();
    m_content->setSpacing(1);
  }

  connect(m_mapper, SIGNAL(mapped(int)), SLOT(showHideSidebar(int)));
}

void Sidebar::setVisible(bool visible)
{
  if (m_position == Left) {
    m_pos = m_tabBar->sizeHint().width() + 5;
    m_tabBar->setFixedWidth(visible ? m_pos : 0);
  } else {
    m_pos = m_tabBar->sizeHint().height() + 5;
    m_tabBar->setFixedHeight(visible ? m_pos : 0);
  }
  
  if (m_currentIndex != -1)
    showHideSidebar(m_currentIndex);
  
  if (visible)
    m_tabBar->show();
  else
    m_tabBar->hide();
  
  adjustWidgetSizes();
}

Sidebar::~Sidebar()
{
  KConfig *config;
  // Save the currently selected sidebar
  if (m_position == Left) {
	config = KFTPGrabberBase::config(QString("Sidebar_%1").arg("Left"));
  } else {
        config = KFTPGrabberBase::config(QString("Sidebar_%1").arg("Bottom"));
  }
  if (m_currentIndex != -1) {
  	config->writeEntry("CurrentSidebar", currentSidebar()->name());
  } else {
	config->writeEntry("CurrentSidebar", QString::null);
  }
  if (m_position == Left) {
  	config->writeEntry("Size", m_sidebarBox->width());
  } else {
  	config->writeEntry("Size", m_sidebarBox->height());
  }
}

void Sidebar::polish()
{
  QWidget::polish();
  
  KConfig *config = KFTPGrabberBase::config(QString("Sidebar_%1").arg(m_position == Left ? "Left" : "Bottom"));
  const int index = indexForName(config->readEntry("CurrentSidebar"));

  if (m_position == Left) {
    uint M = 0;
    for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
      const uint m = (*it)->minimumWidth();
      if (m > M) M = m;
    }
  
    const int width = config->readNumEntry("Size", sidebar(index)->sizeHint().width());
  
    if (M > 250) {
      M = 250;
    }
  
    m_sidebarBox->setMinimumWidth(M);
    m_sidebarBox->resize(width, height());
  } else {
    uint M = 0;
    for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
      const uint m = (*it)->minimumHeight();
      if (m > M) M = m;
    }
    
    const int height = config->readNumEntry("Size", sidebar(index)->height());
    
    if (M > 250) {
      M = 250;
    }
    
    m_sidebarBox->setMinimumHeight(M);
    m_sidebarBox->resize(width(), height);
  }

  // If any sidebar should be open, open it
  if (index != -1)
    showHideSidebar(index);
}

void Sidebar::adjustWidgetSizes()
{
  if (m_position == Left) {
    const uint w = width();
    const uint h = height();
    const uint mxW = maxSidebarWidth();
    const uint p = (m_pos < mxW) ? m_pos : mxW;
    const uint ppw = p + m_divider->width();
    const uint tbw = m_tabBar->width();
  
    m_divider->move(p, 0);
  
    const uint offset = !m_divider->isHidden() ? ppw : tbw;
  
    m_sidebarBox->resize(p - tbw, h);
    m_content->setGeometry(offset, 0, w - offset, h);
  } else {
    const uint w = width();
    const uint h = height();
    const uint mxH = maxSidebarHeight();
    const uint p = (m_pos < mxH) ? m_pos : mxH;
    const uint pph = p + m_divider->height();
    const uint tbh = m_tabBar->height();
  
    m_divider->move(0, h - pph);
  
    const uint offset = !m_divider->isHidden() ? pph : tbh;

    m_sidebarBox->setGeometry(0, h - p, w, p - tbh);
    m_content->setGeometry(0, 0, w, h - offset);
    m_tabBar->move(0, h - tbh);
  }
}

void Sidebar::mouseMovedOverSplitter(QMouseEvent *e)
{
  const uint oldPos = m_pos;
  uint newPos;
  uint minPos;
  uint maxPos;
    
  if (m_position == Left) {
    newPos = mapFromGlobal(e->globalPos()).x();
    minPos = m_tabBar->width() + m_sidebarBox->minimumWidth();
    maxPos = maxSidebarWidth();
  } else {
    newPos = height() - mapFromGlobal(e->globalPos()).y();
    minPos = m_tabBar->height() + m_sidebarBox->minimumHeight();
    maxPos = maxSidebarHeight();
  }
  
  if (newPos < minPos)
    m_pos = minPos;
  else if (newPos > maxPos)
    m_pos = maxPos;
  else
    m_pos = newPos;

  if (m_pos != oldPos)
    adjustWidgetSizes();
}

bool Sidebar::event(QEvent *e)
{
  switch (e->type()) {
    case QEvent::LayoutHint: {
      if (m_position == Left) {
        setMinimumWidth(m_tabBar->minimumWidth() + m_divider->minimumWidth() + m_sidebarBox->width() + m_content->minimumWidth());
      } else {
        setMinimumHeight(m_tabBar->minimumHeight() + m_divider->minimumHeight() + m_sidebarBox->height() + m_content->minimumHeight());
      }
      break;
    }
    case QEvent::Resize: {
      if (m_position == Left) {
        m_divider->resize(0, height());
        m_tabBar->resize(0, height());
      } else {
        m_divider->resize(width(), 0);
        m_tabBar->resize(width(), 0);
      }

      adjustWidgetSizes();
      return true;
    }
    default: break;
  }

  return QWidget::event(e);
}

void Sidebar::addSidebar(QWidget *widget, const QString &title, const QString &icon)
{
  const int id = m_tabBar->tabs()->count(); // the next available id
  const QString name(widget->name());
  QWidget *tab;

  widget->reparent(m_sidebarBox, QPoint());
  widget->hide();

  m_tabBar->appendTab(SmallIcon(icon), id, title);
  tab = m_tabBar->tab(id);
  tab->setFocusPolicy(QWidget::NoFocus);

  // We use a SignalMapper to show/hide the corresponding browser when tabs are clicked
  connect(tab, SIGNAL(clicked()), m_mapper, SLOT(map()));
  m_mapper->setMapping(tab, id);

  m_sidebars.push_back(widget);
}

void Sidebar::showHideSidebar(int index)
{
  const int prevIndex = m_currentIndex;

  if (m_currentIndex != -1) {
    // First we need to hide the currentBrowser
    m_currentIndex = -1;

    m_sidebars[prevIndex]->hide();
    m_tabBar->setTab(prevIndex, false);
  }

  if (index == prevIndex) {
    // Close the sidebar
    m_sidebarBox->hide();
    m_divider->hide();

    adjustWidgetSizes();
  } else if ((uint) index < m_sidebars.count()) {
    // Open up target
    QWidget* const target = m_sidebars[index];
    m_currentIndex = index;

    m_divider->show();
    target->show();
    target->setFocus();
    m_sidebarBox->show();
    m_tabBar->setTab(index, true);

    if (prevIndex == -1) {
      if (m_position == Left) {
        m_pos = m_sidebarBox->width() + m_tabBar->width();
      } else {
        m_pos = currentSidebar()->height() + m_tabBar->height();
      }
      
      adjustWidgetSizes();
    }
  }
}

QWidget *Sidebar::sidebar(const QString &name) const
{
  for (SidebarList::ConstIterator it = m_sidebars.begin(), end = m_sidebars.end(); it != end; ++it) {
    if (name == (*it)->name())
      return *it;
  }

  return 0;
}

int Sidebar::indexForName(const QString &name) const
{
  for (uint x = 0; x < m_sidebars.count(); ++x) {
    if (name == m_sidebars[x]->name())
      return x;
  }

  return -1;
}

}

#include "sidebar.moc"
