/*!
  \file
  \brief チャット等のメッセージ表示用

  \author Satofumi KAMIMURA

  $Id: ChatMessage.cpp 1053 2009-06-20 22:52:51Z satofumi $
*/

#include "ChatMessage.h"
#include "Font.h"
#include "TextSurface.h"
#include "StopWatch.h"
#include <deque>
#include <string>

using namespace qrk;
using namespace std;


namespace
{
  class Line
  {
  public:
    Surface* surface_;
    Point<long> draw_offset_;
    int first_time_;

    Line(Surface* surface, const Point<long>& offset, int first_time)
      : surface_(surface), draw_offset_(offset), first_time_(first_time)
    {
    }


    ~Line(void)
    {
      delete surface_;
    }
  };

  typedef deque<Line*> Lines;
}


struct ChatMessage::pImpl
{
  enum {
    DefaultDrawMsec = 6000,
    DefaultFadeoutMsec = 1000,
  };

  Font& font_;
  Rect<long> rect_;
  float alpha_;
  int draw_timeout_;
  int fadeout_time_;
  Point<long> offset_;

  StopWatch timer_;
  deque<string> buffer_;
  Lines lines_;


  pImpl(Font& font, const Rect<long>& rect)
    : font_(font), rect_(rect), alpha_(1.0),
      draw_timeout_(DefaultDrawMsec), fadeout_time_(DefaultFadeoutMsec)
  {
    timer_.start();
  }


  void draw(const Point<long>& own_position, const Rect<long>& area)
  {
    static_cast<void>(area);
    //Rect<long> updated_area = drawingArea(area, own_position, rect_);
    //Screen::setClipArea(updated_area);

    // 登録済みメッセージの追加
    while (! buffer_.empty()) {
      int h = addLine();
      if (h <= 0) {
        break;
      }

      // 追加した幅だけ、既存の描画位置に加算する
      for (Lines::iterator it = lines_.begin(); it != lines_.end(); ++it) {
        (*it)->draw_offset_.y += h;
      }
    }

    // スクロールアウト、またはタイムアウトしたメッセージの削除
    int ticks = timer_.ticks();
    size_t n = lines_.size();
    for (size_t i = 0; i < n; ++i) {
      Line* line = lines_.front();
      if (((ticks - line->first_time_) > draw_timeout_) ||
          (line->draw_offset_.y > rect_.h)) {
        delete line;
        lines_.pop_front();
        continue;
      }
      break;
    }

    // メッセージの描画
    for (Lines::iterator it = lines_.begin(); it != lines_.end(); ++it) {
      Line* line = *it;

      // アルファ値の更新
      int left_time = draw_timeout_ - (ticks - line->first_time_);
      float alpha =
        (left_time > fadeout_time_) ? 1.0f : alpha_ * left_time / fadeout_time_;

      // 描画
      Surface* surface = line->surface_;
      surface->setAlpha(alpha);
      Rect<long> dest = surface->rect();
      dest.x = own_position.x;
      dest.y = own_position.y + rect_.h - line->draw_offset_.y;
      surface->draw(NULL, &dest);
    }
    //Screen::disableClipArea();
  }


  int addLine(void)
  {
    string line = buffer_.front();
    size_t n = line.find("\n");
    if (n == string::npos) {
      // 改行がなければ、処理しない
      return 0;
    }

    // 描画範囲に収まる文字列を生成する
    int text_size = static_cast<int>(n);
    for (; text_size >= 0; --text_size) {
      Rect<long> surface_size =
        TextSurface::surfaceSize(font_, line.substr(0, text_size).c_str());
      if (surface_size.w < rect_.w) {
        break;
      }
    }
    if (text_size <= 0) {
      return 0;
    }

    buffer_.pop_front();

    // サーフェスの作成
    Surface* surface =
      new TextSurface(font_, line.substr(0, text_size).c_str());
    lines_.push_back(new Line(surface, offset_, timer_.ticks()));

    if (text_size < static_cast<long>(n)) {
      // 余った文字は、次の行に加える
      buffer_.front() = line.substr(text_size) + buffer_.front();
    }

    return surface->rect().h;
  }


  void print(const char* text)
  {
    // 終端が改行文字でなければ、既存の行に続けて連結する
    if (! buffer_.empty()) {
      string text_line = buffer_.back();
      if (text_line[text_line.size() - 1] != '\n') {
        buffer_.back() += text;
        return;
      }
    }
    buffer_.push_back(text);
  }
};


ChatMessage::ChatMessage(Font& font, const Rect<long>& rect)
  : pimpl(new pImpl(font, rect))
{
}


ChatMessage::~ChatMessage(void)
{
}


Rect<long> ChatMessage::rect(void) const
{
  return pimpl->rect_;
}


void ChatMessage::setEventAcceptable(bool acceptable)
{
  static_cast<void>(acceptable);
  // !!!
}


void ChatMessage::setAlpha(float alpha)
{
  pimpl->alpha_ = alpha;
}


void ChatMessage::draw(const Rect<long>& area)
{
  pimpl->draw(position(), area);
}


void ChatMessage::setDrawTimeout(size_t msec)
{
  pimpl->draw_timeout_ = static_cast<int>(msec);
}


void ChatMessage::setFadeoutTime(size_t msec)
{
  pimpl->fadeout_time_ = static_cast<int>(msec);
}


void ChatMessage::setOffset(const Point<long>& offset)
{
  pimpl->offset_ = offset;
}


void ChatMessage::clear(void)
{
  // 描画中の文字列と、登録済みの文字列を削除
  pimpl->lines_.clear();
  pimpl->buffer_.clear();
}


void ChatMessage::print(const char* text)
{
  pimpl->print(text);
}


void ChatMessage::newline(void)
{
  pimpl->print("\n");
}
