/* This file is part of the wvWare 2 project
   SPDX-FileCopyrightText: 2003 Werner Trobin <trobin@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the Library GNU General Public
   version 2 of the License, or (at your option) version 3 or,
   at the discretion of KDE e.V (which shall act as a proxy as in
   section 14 of the GPLv3), any later version..

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA 02110-1301, USA.
*/

#include "word_helper.h"
#include "olestream.h"
#include "textconverter.h"
#include "ustring.h"

namespace wvWare
{

STTBF::STTBF(U16 lid, OLEStreamReader *reader, bool preservePos)
{
    if (preservePos)
        reader->push();
    init(lid, reader, nullptr);
    if (preservePos)
        reader->pop();
}

STTBF::STTBF(U16 lid, const U8 *ptr)
{
    init(lid, nullptr, ptr);
}

STTBF::STTBF(const STTBF &rhs)
    : m_strings(rhs.m_strings)
    , m_extraDataLength(rhs.m_extraDataLength)
{
    std::vector<U8 *>::const_iterator it = rhs.m_extraData.begin();
    std::vector<U8 *>::const_iterator end = rhs.m_extraData.end();
    for (; it != end; ++it) {
        U8 *tmp = new U8[m_extraDataLength];
        memcpy(tmp, *it, m_extraDataLength);
        m_extraData.push_back(tmp);
    }
}

STTBF::~STTBF()
{
    std::vector<U8 *>::const_iterator it = m_extraData.begin();
    for (; it != m_extraData.end(); ++it)
        delete[] *it;
}

unsigned int STTBF::count() const
{
    return m_strings.size();
}

UString STTBF::firstString() const
{
    m_stringIt = m_strings.begin();
    if (m_stringIt != m_strings.end())
        return *m_stringIt;
    return UString::null;
}

UString STTBF::nextString() const
{
    if (m_stringIt == m_strings.end())
        return UString::null;
    ++m_stringIt;
    if (m_stringIt != m_strings.end())
        return *m_stringIt;
    return UString::null;
}

UString STTBF::prevString() const
{
    if (m_strings.size() == 0)
        return UString::null;
    if (m_stringIt != m_strings.begin())
        --m_stringIt;
    return *m_stringIt;
}

UString STTBF::lastString() const
{
    m_stringIt = m_strings.end();
    if (m_stringIt == m_strings.begin())
        return UString::null;
    --m_stringIt;
    return *m_stringIt;
}

UString STTBF::stringAt(unsigned int index) const
{
    if (index < m_strings.size())
        return m_strings[index];
    return UString::null;
}

const U8 *STTBF::firstExtra() const
{
    m_extraIt = m_extraData.begin();
    if (m_extraIt != m_extraData.end())
        return *m_extraIt;
    return nullptr;
}

const U8 *STTBF::nextExtra() const
{
    if (m_extraIt == m_extraData.end())
        return nullptr;
    ++m_extraIt;
    if (m_extraIt != m_extraData.end())
        return *m_extraIt;
    return nullptr;
}

const U8 *STTBF::prevExtra() const
{
    if (m_extraData.size() == 0)
        return nullptr;
    if (m_extraIt != m_extraData.begin())
        --m_extraIt;
    return *m_extraIt;
}

const U8 *STTBF::lastExtra() const
{
    m_extraIt = m_extraData.end();
    if (m_extraIt == m_extraData.begin())
        return nullptr;
    --m_extraIt;
    return *m_extraIt;
}

const U8 *STTBF::extraAt(unsigned int index) const
{
    if (index < m_extraData.size())
        return m_extraData[index];
    return nullptr;
}

void STTBF::dumpStrings() const
{
    wvlog << "STTBF::dumpStrings(): count=" << count() << " extraDataLength=" << extraDataLength() << Qt::endl;
    std::vector<UString>::const_iterator it = m_strings.begin();
    std::vector<UString>::const_iterator end = m_strings.end();
    for (; it != end; ++it)
        wvlog << "   '" << (*it).ascii() << "'" << Qt::endl;
}

void STTBF::init(U16 lid, OLEStreamReader *reader, const U8 *ptr)
{
    bool extended = false;
    U16 count = readU16(reader, &ptr);
    // "extended" characters?
    if (count == 0xffff) {
        extended = true;
        // read the real size
        count = readU16(reader, &ptr);
    }
    m_extraDataLength = readU16(reader, &ptr);

    // If we don't read unicode strings we have to set up a text converter
    TextConverter *textconverter = nullptr;
    if (!extended)
        textconverter = new TextConverter(lid);

    // read one string after the other
    for (U16 i = 0; i < count; ++i) {
        U16 len = 0;
        if (extended) // double byte count!
            len = readU16(reader, &ptr);
        else
            len = readU8(reader, &ptr);

        if (len != 0) {
            if (extended) {
                XCHAR *string = new XCHAR[len];
                for (U16 j = 0; j < len; ++j)
                    string[j] = readU16(reader, &ptr);
                UString ustring(reinterpret_cast<const wvWare::UChar *>(string), len);
                delete[] string;
                m_strings.push_back(ustring);
            } else {
                U8 *string = new U8[len];
                read(reader, &ptr, string, len);
                UString ustring(textconverter->convert(reinterpret_cast<char *>(string), static_cast<unsigned int>(len)));
                delete[] string;
                m_strings.push_back(ustring);
            }
        } else
            m_strings.push_back(UString(""));
        if (m_extraDataLength != 0) {
            U8 *extra = new U8[m_extraDataLength];
            read(reader, &ptr, extra, m_extraDataLength);
            m_extraData.push_back(extra);
        }
    }
    delete textconverter;
}

U16 STTBF::readU16(OLEStreamReader *reader, const U8 **ptr) const
{
    if (reader)
        return reader->readU16();
    else if (*ptr) {
        U16 ret = wvWare::readU16(*ptr);
        *ptr += 2;
        return ret;
    } else {
        wvlog << "Warning: Internal error while reading STTBF" << Qt::endl;
        return 0;
    }
}

U8 STTBF::readU8(OLEStreamReader *reader, const U8 **ptr) const
{
    if (reader)
        return reader->readU8();
    else if (*ptr) {
        U8 ret = **ptr;
        *ptr += 1;
        return ret;
    } else {
        wvlog << "Warning: Internal error while reading STTBF" << Qt::endl;
        return 0;
    }
}

bool STTBF::read(OLEStreamReader *reader, const U8 **ptr, U8 *buffer, size_t length) const
{
    if (reader)
        return reader->read(buffer, length);
    else if (*ptr) {
        memcpy(buffer, *ptr, length);
        *ptr += length;
        return true;
    } else {
        wvlog << "Warning: Internal error while reading STTBF" << Qt::endl;
        return false;
    }
}

CHPFKP_BX::CHPFKP_BX()
{
    clear();
}

CHPFKP_BX::CHPFKP_BX(OLEStreamReader *stream, bool preservePos)
{
    clear();
    read(stream, preservePos);
}

CHPFKP_BX::CHPFKP_BX(const U8 *ptr)
{
    clear();
    readPtr(ptr);
}

bool CHPFKP_BX::read(OLEStreamReader *stream, bool preservePos)
{
    if (preservePos)
        stream->push();
    offset = stream->readU8();
    if (preservePos)
        stream->pop();
    return true;
}

void CHPFKP_BX::readPtr(const U8 *ptr)
{
    offset = *ptr;
}

bool CHPFKP_BX::write(OLEStreamWriter *stream, bool preservePos) const
{
    if (preservePos)
        stream->push();
    stream->write(offset);
    if (preservePos)
        stream->pop();
    return true;
}

void CHPFKP_BX::clear()
{
    offset = 0;
}

bool operator==(const CHPFKP_BX &lhs, const CHPFKP_BX &rhs)
{
    return lhs.offset == rhs.offset;
}

bool operator!=(const CHPFKP_BX &lhs, const CHPFKP_BX &rhs)
{
    return !(lhs == rhs);
}

const unsigned int CHPFKP_BX::sizeOf = 1;

} // namespace wvWare
