/***************************************************************************
                          chatstatusbar.cpp -  description
                          -------------------
    begin                : Wed Okt 8 2008
    copyright            : (C) 2008 by Ruben Vandamme
    email                : ruben@kmess.org
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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 "chatstatusbar.h"

#include <KColorScheme>
#include <KIconLoader>
#include <KLocale>

#include <QFontMetrics>
#include <QPainter>
#include <QPushButton>
#include <QPixmap>
#include <QTimer>



// The constructor
ChatStatusBar::ChatStatusBar( QWidget* parent )
 : QWidget( parent ),
   type_( DefaultType ),
   state_( DefaultState ),
   illumination_( 0 ),
   minTextHeight_( -1 ),
   timer_( 0 ),
   button_( 0 )
{
    setMinimumHeight( KIconLoader::SizeSmall );

    timer_ = new QTimer( this );
    connect( timer_, SIGNAL( timeout() ), this, SLOT( timerDone() ) );

    button_ = new QPushButton(i18nc("@action:button", "Reconnect"), this);
    button_->hide();
    connect(button_, SIGNAL(clicked()), this, SLOT(buttonClicked()));

    setMinimumTextHeight(KIconLoader::SizeSmall + 8);
}

// The destructor
ChatStatusBar::~ChatStatusBar()
{
}



// Shows a message in the status label.
void ChatStatusBar::setMessage( MessageType type, const QString& text )
{
  if ( ( text == text_ ) && ( type == type_ ) )
  {
    return;
  }

  text_ = text;
  type_ = type;

  timer_->stop();
  illumination_ = 0;
  state_ = DefaultState;

  const char* iconName = 0;

  switch ( type )
  {
  case Disconnected:
    iconName = "network-disconnect";

    timer_->start( 100 );
    state_ = Illuminate;

    updateCloseButtonPosition();
    button_->setEnabled( true );
    button_->show();
    break;

  case ContactTyping:
    iconName = "input-keyboard";

    button_->hide();
    break;

  case DefaultType:
  default:
    button_->hide();
    break;
  }

  pixmap_ = ( iconName == 0 ) ? QPixmap() : SmallIcon( iconName );
  QTimer::singleShot( GeometryTimeout, this, SLOT( assureVisibleText() ) );
  update();
}



// Set the minimum text height.
void ChatStatusBar::setMinimumTextHeight( int min )
{
  if ( min != minTextHeight_ )
  {
    minTextHeight_ = min;
    setMinimumHeight( min );
    if ( button_->height() > min )
    {
      button_->setFixedHeight( min );
    }
  }
}



// Enable or disable the button for the specified message type.
void ChatStatusBar::setButtonEnabled ( MessageType type, bool enable )
{
  if( type == type_ )
  {
    button_->setEnabled( enable );
  }
}



// Paint the statusbar.
void ChatStatusBar::paintEvent( QPaintEvent* /* event */ )
{
  QPainter painter( this );

  if ( illumination_ > 0 )
  {
    // At this point, a: we are a second label being drawn over the already
    // painted status area, so we can be translucent, and b: our palette's
    // window color (bg only) seems to be wrong (always black)

    KColorScheme scheme( palette().currentColorGroup(), KColorScheme::Window );
    QColor backgroundColor( scheme.background( KColorScheme::NegativeBackground ).color() );
    backgroundColor.setAlpha( qMin( 255, illumination_ * 2 ) );

    painter.setBrush( backgroundColor );
    painter.setPen  ( Qt::NoPen );
    painter.drawRect( QRect( 0, 0, width(), height() ) );
  }

  // Draw pixmap
  int x = BorderGap;
  const int y = ( minTextHeight_ - pixmap_.height() ) / 2;

  if ( !pixmap_.isNull() )
  {
    painter.drawPixmap( x, y, pixmap_ );
    x += pixmap_.width() + BorderGap;
  }

  // Draw text
  painter.setPen( palette().windowText().color() );
  int flags = Qt::AlignVCenter;

  if ( height() > minTextHeight_ )
  {
    flags = flags | Qt::TextWordWrap;
  }

  painter.drawText( QRect( x, 0, availableTextWidth(), height() ), flags, text_ );
  painter.end();
}



// Statusbar is resized, update it.
void ChatStatusBar::resizeEvent( QResizeEvent* event )
{
  QWidget::resizeEvent( event );
  updateCloseButtonPosition();
  QTimer::singleShot( GeometryTimeout, this, SLOT( assureVisibleText() ) );
}



// Update the background color.
void ChatStatusBar::timerDone()
{
  switch ( state_ )
  {
  case Illuminate:
  {
    // increase the illumination
    const int illumination_max = 128;
    if ( illumination_ < illumination_max )
    {
      illumination_ += 32;
      if ( illumination_ > illumination_max )
      {
        illumination_ = illumination_max;
      }
      update();
    }
    else
    {
      state_ = Illuminated;
      timer_->start( 5000 );
    }
    break;
  }
  case Illuminated:
    // start desaturation
    state_ = Desaturate;
    timer_->start( 100 );
    break;

  case Desaturate:
    // desaturate
    if ( illumination_ > 0 )
    {
      illumination_ -= 5;
      update();
    }
    else
    {
      state_ = DefaultState;
      timer_->stop();
    }
    break;

  default:
    break;
  }
}



// Increases the height of the message label so that the given text fits into given area.
void ChatStatusBar::assureVisibleText()
{
  if ( text_.isEmpty() )
  {
    return;
  }

  int requiredHeight = minTextHeight_;

  if ( type_ != DefaultType && type_ != ContactTyping )
  {
    // Calculate the required height of the widget thats
    // needed for having a fully visible text. Note that for certain
    // statusbar types increasing the text height is not wanted,
    // as this might rearrange the layout of items.

    const QFontMetrics fontMetrics( font() );
    const QRect bounds( fontMetrics.boundingRect( 0, 0, availableTextWidth(), height(),
                       Qt::AlignVCenter | Qt::TextWordWrap, text_ ) );
    requiredHeight = bounds.height();

    if ( requiredHeight < minTextHeight_ )
    {
      requiredHeight = minTextHeight_;
    }
  }

  // Increase/decrease the current height of the widget to the
  // required height. The increasing/decreasing is done in several
  // steps to have an animation if the height is modified
  // (see ChatStatusBar::resizeEvent())

  const int gap = minTextHeight_ / 2;
  int minHeight = minimumHeight();

  if ( minHeight < requiredHeight )
  {
    minHeight += gap;

    if ( minHeight > requiredHeight )
    {
      minHeight = requiredHeight;
    }

    setMinimumHeight(minHeight);
    updateGeometry();
  }
  else if ( minHeight > requiredHeight )
  {
    minHeight -= gap;

    if ( minHeight < requiredHeight )
    {
      minHeight = requiredHeight;
    }

    setMinimumHeight( minHeight );
    updateGeometry();
  }

  updateCloseButtonPosition();
}




// Returns the available width in pixels for the text.
int ChatStatusBar::availableTextWidth() const
{
  const int buttonWidth = ( button_->isVisible() ) ? button_->width() + BorderGap : 0;
  return width() - pixmap_.width() - ( BorderGap * 4 ) - buttonWidth;
}



// Moves the close button to the upper right corner of the message label.
void ChatStatusBar::updateCloseButtonPosition()
{
  const int x = width() - button_->width() - BorderGap;
  const int y = ( height() - button_->height() ) / 2;

  button_->move( x, y );
}



// User requested an action.
void ChatStatusBar::buttonClicked()
{
  emit reconnect();
}



// Resets the message label properties.
void ChatStatusBar::reset()
{
  text_.clear();
  button_->setEnabled( true );
  type_ = DefaultType;
}



// Returns the type of the status message.
inline ChatStatusBar::MessageType ChatStatusBar::type() const
{
  return type_;
}



// Returns the text in the statusbar.
inline const QString& ChatStatusBar::text() const
{
  return text_;
}



// Returns the minimum text height.
inline int ChatStatusBar::minimumTextHeight() const
{
  return minTextHeight_;
}



#include "chatstatusbar.moc"
