#include "auction.h"

Auction::Auction(QWidget* parent, const int timeOffseteBayToLocal) : QTreeWidgetItem(0), QObject(parent)
{
  m_parent = parent;
  m_timeOffseteBayToLocal = 0;
  m_previousNotification = 0;
  m_pixmap = QPixmap(100, 100);
  m_pixmap.fill();
  m_bidCount = 0;
  m_openBrowserOnNextAlarm = false;

  m_timeOffseteBayToLocal = timeOffseteBayToLocal;

  m_notificationTimer = new QTimer(this);
  m_notificationTimer->setSingleShot(true);
  connect(m_notificationTimer, SIGNAL(timeout()), SLOT(slotAlarm()));
}

Auction::~Auction()
{
  qDebug() << "Auction::~Auction()";
  if(m_previousNotification)
  {
    qDebug() << "Auction::~Auction(): deleting previous notification";
    m_previousNotification->close();
  }
}

QString Auction::formatDuration(int secondsToEnd) const
{
  if(secondsToEnd < 0) return i18n("auction\nended");
  
  QString result("%1d %2h\n%3m %4s");

  //86400 + 3663 seconds = 90063s = 01d 01h 01m 03s
  const int days = secondsToEnd / 86400;
  const int hours = (secondsToEnd % 86400) / 3600;
  const int minutes = (secondsToEnd % 3600) / 60;
  const int seconds = secondsToEnd % 60;

  return result.arg(QString::number(days), 2, '0').arg(QString::number(hours), 2, '0').arg(QString::number(minutes), 2, '0').arg(QString::number(seconds), 2, '0');
}

int Auction::getTimeToEnd(bool includeOffset) const
{
  if(includeOffset)
    return QDateTime::currentDateTime().secsTo(m_end.addSecs(m_timeOffseteBayToLocal));
  else
    return QDateTime::currentDateTime().secsTo(m_end);
}

void Auction::update(const bool translateEndTimes)
{
  if(m_priceBuyNow.isEmpty())
    setText(1, m_title + "\n" + i18n("No BuyNow Option"));
  else
    setText(1, m_title + "\n" + i18n("BuyNow for") + " " + m_currency + " " + m_priceBuyNow);
  
  setText(2, m_currency + " " + m_priceCurrent + "\n" + QString::number(m_bidCount) + " " + i18n("bids"));
  
  if(translateEndTimes)
    setText(3, KGlobal::locale()->formatDate(m_end.addSecs(m_timeOffseteBayToLocal).date(), KLocale::ShortDate) + "\n" + KGlobal::locale()->formatTime(m_end.addSecs(m_timeOffseteBayToLocal).time(), true));
  else
    setText(3, KGlobal::locale()->formatDate(m_end.date(), KLocale::ShortDate) + "\n" + KGlobal::locale()->formatTime(m_end.time(), true));

  setText(4, formatDuration(getTimeToEnd(true)));

  setIcon(0, QIcon(m_pixmap.scaled(62, 62, Qt::KeepAspectRatio, Qt::SmoothTransformation)));
}

void Auction::setItemId(const QString &itemId)
{
  m_itemId = itemId;
}

QString Auction::itemId(void) const
{
  return m_itemId;
}

void Auction::setTitle(const QString &title)
{
  m_title = title;
}

QString Auction::title(void) const
{
  return m_title;
}

void Auction::setUrl(const QUrl &url)
{
  m_url = url;
}

QUrl Auction::url(void) const
{
  return m_url;
}

void Auction::setCurrency(const QString &currency)
{
  m_currency = currency;
}

void Auction::setBidCount(const int bidCount)
{
  m_bidCount = bidCount;
}

void Auction::setPriceCurrent(const QString &priceCurrent)
{
  m_priceCurrent = priceCurrent;
}

void Auction::setPriceBuyNow(const QString &priceBuyNow)
{
  m_priceBuyNow = priceBuyNow;
}

void Auction::setEnd(const QDateTime &end)
{
  m_end = end;
  slotSetNextAlarm();
}

void Auction::setPixmap(const QPixmap &pixmap)
{
//   qDebug() << "Auction::setPixmap():" << m_title;
  m_pixmap = pixmap;
}

QPixmap Auction::pixmap(void) const
{
  return m_pixmap;
}

bool auctionAlarmsLessThan(const QString &s1, const QString &s2)
{
  const int t1 = s1.right(1) == "!" ? s1.left(s1.length()-1).toInt() : s1.toInt();
  const int t2 = s2.right(1) == "!" ? s2.left(s2.length()-1).toInt() : s2.toInt();
  return t1 < t2;
}

void Auction::slotSetNextAlarm()
{
  KConfigGroup settings = KGlobal::config().data()->group("Settings");
  QStringList notificationsMinutes = settings.readEntry("alarms", "30,15,5!,4,3,2").split(",");
  qSort(notificationsMinutes.begin(), notificationsMinutes.end(), auctionAlarmsLessThan);

  qDebug() << "\n" << __FILE__ << __FUNCTION__ << "setting up alarms" << notificationsMinutes.join(",") << "for item" << m_itemId;

  // Go backwards and set the first alarm that is in the future.
  QListIterator<QString> iterator(notificationsMinutes);
  iterator.toBack();
  while(iterator.hasPrevious())
  {
    bool ok = false;
    const QString minutes = iterator.previous();
    int secondsBeforeEnd = 0;

    // A "!" at the end of the alarm-value tells us to open the item in the browser on alarm.
    if(minutes.right(1) == "!")
      secondsBeforeEnd = 60 * minutes.left(minutes.length()-1).toInt(&ok, 10);
    else
      secondsBeforeEnd = 60 * minutes.toInt(&ok, 10);

    Q_ASSERT(ok && qDebug() << minutes);

    const int secondsToAlarm = getTimeToEnd() - secondsBeforeEnd;

    if(secondsToAlarm >= 0)
    {
      qDebug() << __FILE__ << __FUNCTION__ << "alarm" << minutes << secondsBeforeEnd << "(min/sec) before end is the next as getTimeToEnd() =" << getTimeToEnd();

      // If the next alarm is more than 24 days away, we get an integer
      // overflow as QTimer is using signed INTs for its interval setting.
      if(secondsToAlarm < (INT_MAX/1000))
      {
	qDebug() << __FILE__ << __FUNCTION__ << "setting alarm to" << secondsToAlarm << "s," << secondsBeforeEnd << "seconds / " << minutes << "minutes before end.";
	
	m_notificationTimer->setInterval(secondsToAlarm * 1000);
	m_notificationTimer->start();

        // open the browser on the next alarm
	m_openBrowserOnNextAlarm = minutes.right(1) == "!";

	// The next alarm was set, we're done.
	return;
      }
      else
      {
        // Since the watchlist is updated at least once a day, this shouldn't matter. The alarm will be set correctly as soon as the watchlist is updated
	// and the remaining time becomes smaller than INT_MAX
	qDebug() << __FILE__ << __FUNCTION__ << "setting alarm failed, value exceeds integer range. Setting pseudo-alarm.";
	m_notificationTimer->setInterval(INT_MAX);
	m_notificationTimer->start();
	return;
      }
    }
    else
    {
      qDebug() << __FILE__ << __FUNCTION__ << "alarm" << minutes << "has passed" << secondsToAlarm << "seconds ago.";
    }
  }
  qDebug() << __FILE__ << __FUNCTION__ << "all alarms have passed, nothing to do.";
}

void Auction::slotAlarm()
{
  qDebug() << __FILE__ << __FUNCTION__ << "item" << m_itemId << "caused alarm";
  // http://api.kde.org/4.x-api/kdelibs-apidocs/kdeui/html/classKNotification.html#_details
  if(m_previousNotification)
  {
    m_previousNotification->close();
    m_previousNotification->deleteLater();
  }

  m_previousNotification = new KNotification("AuctionEnding", m_parent, KNotification::Persistent | KNotification::RaiseWidgetOnActivation);
  m_previousNotification->setText(i18n("Item is ending in %2:<br><br><b>%1</b><br>", m_title, formatDuration(getTimeToEnd())));
  m_previousNotification->setPixmap(m_pixmap.scaled(QSize(160, 120), Qt::KeepAspectRatio, Qt::SmoothTransformation));
  connect(m_previousNotification, SIGNAL(activated(unsigned int)), SLOT(slotNotificationActivated(unsigned int)));
  connect(m_previousNotification, SIGNAL(activated(unsigned int)), SLOT(slotNotificationClosed()));
  connect(m_previousNotification, SIGNAL(closed()), SLOT(slotNotificationClosed()));

  if(m_openBrowserOnNextAlarm) emit openBrowserForAlarm((QTreeWidgetItem*)this);
  
  QStringList actions;
  actions << "Open Item";
  actions << "Show List";
  m_previousNotification->setActions(actions);
  
  m_previousNotification->sendEvent();

  // Now that this alarm has fired, set the next one
  qDebug() << __FILE__ << __FUNCTION__ << "alarm fired, setting up next alarm in two seconds.";
  QTimer::singleShot(2000, this, SLOT(slotSetNextAlarm()));
}

void Auction::slotNotificationActivated(unsigned int action)
{
  if(action == 1) QDesktopServices::openUrl(m_url);
  if(action == 2) m_parent->show();
}

void Auction::slotNotificationClosed()
{
  qDebug() << __FILE__ << __FUNCTION__;
  // A notification has been closed and deleted. Fortunately, we only
  // use one at a time so we know which one we have to unset.
  m_previousNotification = 0;
}
