/*!
  \file
  \brief UTF-8 文字列の操作クラス

  \author Satofumi KAMIMURA

  $Id: Utf8.cpp 1823 2010-05-11 22:11:47Z satofumi $
*/

#include "Utf8.h"
#include <vector>

using namespace qrk;
using namespace std;


namespace
{
    void createIndex(vector<size_t>& index, const string& utf8_text)
    {
        index.clear();

        size_t n = utf8_text.size();
        for (size_t i = 0; i < n;) {
            index.push_back(i);
            unsigned char ch = utf8_text[i];
            if ((ch & 0xf0) == 0xe0) {
                i += 3;
            } else {
                ++i;
            }
        }
    }
}


struct Utf8::pImpl
{
    string utf8_text_;
    vector<size_t> index_;


    pImpl(const string& utf8_text) : utf8_text_(utf8_text)
    {
        createIndex(index_, utf8_text);
    }


    size_t utf8_index(size_t index)
    {
        size_t max_index = index_.size();
        if (index >= max_index) {
            return string::npos;
        }
        return index_[index];
    }


    string toStdString(void)
    {
        string c_string;

        size_t n = index_.size();
        for (size_t i = 0; i < n; ++i) {
            size_t index = utf8_index(i);
            const unsigned short ch = utf8_text_[index];
            if (ch & 0xff00) {
                c_string.push_back(ch);
                c_string.push_back(utf8_text_[index + 1]);
                c_string.push_back(utf8_text_[index + 2]);
            } else {
                c_string.push_back(ch);
            }
        }
        return c_string;
    }
};


Utf8::Utf8(const string& utf8_text) : pimpl(new pImpl(utf8_text))
{
}


Utf8::Utf8(const Utf8& rhs) : pimpl(new pImpl(rhs.pimpl->utf8_text_))
{
}


Utf8& Utf8::operator = (const Utf8& rhs)
{
    this->pimpl->utf8_text_ = rhs.pimpl->utf8_text_;
    this->pimpl->index_ = rhs.pimpl->index_;

    return *this;
}


Utf8::~Utf8(void)
{
}


bool Utf8::empty(void)
{
    return pimpl->index_.empty();
}


void Utf8::clear(void)
{
    pimpl->utf8_text_ = "";
    pimpl->index_.clear();
}


size_t Utf8::size(void) const
{
    return pimpl->index_.size();
}


unsigned long Utf8::ch(size_t index) const
{
    size_t first_index = pimpl->utf8_index(index);
    size_t last_index = pimpl->utf8_index(index + 1);
    size_t length = (last_index == string::npos)
        ? (pimpl->utf8_text_.size() - first_index) : (last_index - first_index);

    unsigned long ch = 0;
    if (length == 1) {
        ch = pimpl->utf8_text_[first_index];
    } else {
        for (size_t i = 0; i < 3; ++i) {
            ch <<= 8;
            ch |= pimpl->utf8_text_[first_index + i] & 0x00ff;
        }
    }
    return ch;
}


unsigned short Utf8::operator[](size_t index)
{
    return ch(index);
}


std::string Utf8::toStdString(void)
{
    return pimpl->toStdString();
}


Utf8 Utf8::substr(size_t first_index, size_t n)
{
    if (first_index >= pimpl->index_.size()) {
        // 指定した範囲に文字が存在しないときには、"" を返す
        return Utf8("");
    }

    // 指定された文字の位置から指定されたサイズだけ文字を格納していく
    size_t raw_first_index = pimpl->index_[first_index];

    // 末尾に格納する次の文字の先頭位置を計算し、その１つ前の文字までを格納する
    size_t last_index = first_index + n;
    size_t raw_last_index;
    if (last_index >= pimpl->index_.size()) {
        raw_last_index = pimpl->utf8_text_.size();
    } else {
        raw_last_index = pimpl->index_[last_index] - 1;
    }
    return Utf8(pimpl->utf8_text_.substr(raw_first_index,
                                         raw_last_index + 1 - raw_first_index));
}


void Utf8::pop_back(void)
{
    if (! pimpl->index_.empty()) {
        pimpl->index_.pop_back();
    }
}
