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

  \author Satofumi KAMIMURA

  $Id: Font.cpp 888 2009-05-14 14:13:11Z 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_;

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


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


    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
{
  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)
    : 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;
}


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)
{
}


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::setFontSize(size_t font_size)
{
  pimpl->font_size_ = font_size;
}


size_t Font::fontSize(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_;
}
