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

  \author Satofumi KAMIMURA

  $Id: ChatMessage.cpp 1811 2010-04-30 16:12:05Z 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;
}


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


void ChatMessage::setRotateAngle(const Angle& angle)
{
    (void)angle;
    // !!!
}


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");
}
