/*!
  \file
  \brief フォント設定

  \author Satofumi KAMIMURA

  $Id: Font.cpp 1961 2011-08-07 11:34:40Z satofumi $
*/

#include "DetectOS.h"
#include "Font.h"
#include "SdlTtfInit.h"
#include "log_printf.h"
#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <map>
#include <string>

#if defined(MSC)
#define snprintf _snprintf
#endif

using namespace qrk;
using namespace boost;
using namespace std;


namespace
{
    class TtfFont
    {
        TTF_Font* ttf_font_;
        string error_message_;

    public:
        TtfFont(const char* font_file, size_t font_size)
            : ttf_font_(TTF_OpenFont(font_file, static_cast<int>(font_size))),
              error_message_("no error")
        {
            if (!ttf_font_) {
                error_message_ = TTF_GetError();
                //log_printf("%s\n", error_message_.c_str());
            }
        }


        ~TtfFont(void)
        {
            if (ttf_font_) {
                TTF_CloseFont(ttf_font_);
            }
        }


        const char* what(void) const
        {
            return error_message_.c_str();
        }


        TTF_Font* resource(void)
        {
            return ttf_font_;
        }
    };


    class FontResource : private SdlTtfInit
    {
        typedef map<string, weak_ptr<TtfFont> > FontMap;
        FontMap ttf_fonts_;


    public:
        static FontResource* object(void)
        {
            static FontResource singleton_object;
            return &singleton_object;
        }


        shared_ptr<TtfFont> find(const char* file_name, size_t font_size)
        {
            char buffer[13];
            snprintf(buffer, sizeof(buffer), "%d", font_size);
            string key = string(buffer) + file_name;

            FontMap::iterator it = ttf_fonts_.find(key);
            if ((it != ttf_fonts_.end()) && !it->second.expired()) {
                // 見つかれば、その資源を返す
                return shared_ptr<TtfFont>(it->second);
            }

            // 見つからなければ、新規に作成して返す
            shared_ptr<TtfFont> ttf_font(new TtfFont(file_name, font_size));
            ttf_fonts_[key] = ttf_font;
            return ttf_font;
        }
    };
}


struct Font::pImpl
{
    string error_message_;
    FontResource* font_resources_;

    string font_file_;
    size_t font_size_;
    Color foreground_;
    Color background_;
    bool transparent_;
    bool is_valid_;

    shared_ptr<TtfFont> resource_;


    pImpl(const char* font_file, size_t font_size, bool transparent)
        : error_message_("no error."),
          font_resources_(FontResource::object()),
          font_file_(font_file), font_size_(font_size),
          foreground_(1.0, 1.0, 1.0), background_(0.0, 0.0, 0.0),
          transparent_(transparent), is_valid_(false)
    {
    }
};


Font::Font(const char* font_file, size_t size, bool transparent)
    : pimpl(new pImpl(font_file, size, transparent))
{
    // DefaultSize のフォントを生成し、フォントリソースが有効かを確認する
    pimpl->is_valid_ = (resource()) ? true : false;
    if (!pimpl->is_valid_) {
        pimpl->error_message_ = pimpl->resource_->what();
    }
}


Font::Font(const Font& rhs)
    : pimpl(new pImpl(rhs.pimpl->font_file_.c_str(),
                      rhs.pimpl->font_size_,
                      rhs.pimpl->transparent_))
{
    // pimpl 自体がコピーされないようにするため
    pimpl->foreground_ = rhs.pimpl->foreground_;
    pimpl->background_ = rhs.pimpl->background_;
}


Font& Font::operator = (const Font& rhs)
{
    // pimpl 自体がコピーされないようにするため
    this->pimpl->font_file_ = rhs.pimpl->font_file_;
    this->pimpl->font_size_ = rhs.pimpl->font_size_;
    this->pimpl->foreground_ = rhs.pimpl->foreground_;
    this->pimpl->background_ = rhs.pimpl->background_;
    this->pimpl->transparent_ = rhs.pimpl->transparent_;

    return *this;
}


Font::~Font(void)
{
}


const char* Font::what(void) const
{
    return pimpl->error_message_.c_str();
}


Font Font::duplicate(void)
{
    return Font(*this);
}


bool Font::isValid(void) const
{
    return pimpl->is_valid_;
}


TTF_Font* Font::resource(void) const
{
    // !!! スレッド環境で動作させる場合には、適切にロックすること
    // LockGuard guard(pimpl->font_resources_->mutex_);

    pimpl->resource_ = pimpl->font_resources_->
        find(pimpl->font_file_.c_str(), pimpl->font_size_);

    return pimpl->resource_.get()->resource();
}


void Font::setPixelSize(size_t font_size)
{
    pimpl->font_size_ = font_size;
}


size_t Font::pixelSize(void) const
{
    return pimpl->font_size_;
}


void Font::setForegroundColor(const Color& color)
{
    pimpl->foreground_ = color;
}


Color Font::foregroundColor(void) const
{
    return pimpl->foreground_;
}


void Font::setBackgroundColor(const Color& color)
{
    pimpl->background_ = color;
}


Color Font::backgroundColor(void) const
{
    return pimpl->background_;
}


void Font::setTransparent(bool on)
{
    pimpl->transparent_ = on;
}


bool Font::transparent(void) const
{
    return pimpl->transparent_;
}
