/* 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 GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   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 02111-1307, USA.
*/

#ifndef WORD_HELPER_H
#define WORD_HELPER_H

#include "olestream.h"
#include "wvlog.h"

#include "wv2_export.h"
#include <map>
#include <memory>
#include <string.h>
#include <vector>

namespace wvWare
{
namespace Word97
{
struct PHE;
struct BTE;
}
namespace Word95
{
struct PHE;
struct BTE;
Word97::PHE toWord97(const Word95::PHE &phe); // fake, to make gcc 3.4 happy :-(
Word97::BTE toWord97(const Word95::BTE &s); // fake, to make gcc 3.4 happy :-(
}

class OLEStreamReader;
class OLEStreamWriter;
class UString;

class STTBF
{
public:
    STTBF(U16 lid, OLEStreamReader *reader, bool preservePos = false);
    STTBF(U16 lid, const U8 *ptr);
    STTBF(const STTBF &rhs);
    ~STTBF();

    unsigned int count() const;
    unsigned int extraDataLength() const
    {
        return m_extraDataLength;
    }

    UString firstString() const;
    UString nextString() const;
    UString prevString() const;
    UString lastString() const;
    UString stringAt(unsigned int index) const;

    const U8 *firstExtra() const;
    const U8 *nextExtra() const;
    const U8 *prevExtra() const;
    const U8 *lastExtra() const;
    const U8 *extraAt(unsigned int index) const;

    void dumpStrings() const;

private:
    STTBF &operator=(const STTBF &rhs) = delete;

    // Internal helper methods to avoid code duplication in the CTORs
    void init(U16 lid, OLEStreamReader *reader, const U8 *ptr);
    U16 readU16(OLEStreamReader *reader, const U8 **ptr) const;
    U8 readU8(OLEStreamReader *reader, const U8 **ptr) const;
    bool read(OLEStreamReader *reader, const U8 **ptr, U8 *buffer, size_t length) const;

    std::vector<UString> m_strings;
    mutable std::vector<UString>::const_iterator m_stringIt;
    U16 m_extraDataLength;
    std::vector<U8 *> m_extraData;
    mutable std::vector<U8 *>::const_iterator m_extraIt;
};

// Attention: structs used as template parameters for this class need to
// enable the sizeOf field in the code generator (see comment (7)) in
// the header of generate.pl
// Use the short count for "broken" plfs like the LSTF one (surprisingly
// labeled plcflst(!) (see spec_defects for more information)
template<class T, bool shortCount = false>
class PLF
{
public:
    PLF(OLEStreamReader *reader, bool preservePos = false);
    PLF(const U8 *ptr);
    ~PLF();

    size_t count() const
    {
        return m_items.size();
    }

    const T *first() const
    {
        it = m_items.begin();
        if (it != m_items.end())
            return *it;
        return 0;
    }
    const T *next() const;
    const T *prev() const;
    const T *last() const
    {
        it = m_items.end();
        if (it == m_items.begin())
            return 0;
        --it;
        return *it;
    }
    const T *current() const
    {
        if (it != m_items.end())
            return *it;
        return 0;
    }
    const T *at(unsigned int index) const
    {
        if (index < m_items.size())
            return m_items[index];
        return 0;
    }

    // don't copy or assign it
    PLF(const PLF<T, shortCount> &rhs) = delete;
    PLF<T, shortCount> &operator=(const PLF<T, shortCount> &rhs) = delete;

private:
    std::vector<T *> m_items;
    mutable typename std::vector<T *>::const_iterator it;
};

template<class T, bool shortCount>
PLF<T, shortCount>::PLF(OLEStreamReader *reader, bool preservePos)
{
    if (preservePos)
        reader->push();

    U32 count = 0;
    if (shortCount) // work around a broken spec, e.g. for LSTF
        count = reader->readU16();
    else
        count = reader->readU32();

    for (U32 i = 0; i < count; ++i)
        m_items.push_back(new T(reader, false));
    if (preservePos)
        reader->pop();
    it = m_items.begin();
}

template<class T, bool shortCount>
PLF<T, shortCount>::PLF(const U8 *ptr)
{
    U32 count = 0;
    if (shortCount) { // work around a broken spec, e.g. for LSTF
        count = readU16(ptr);
        ptr += 2;
    } else {
        count = readU32(ptr);
        ptr += 4;
    }

    for (U32 i = 0; i < count; ++i, ptr += T::sizeOf)
        m_items.push_back(new T(ptr));
    it = m_items.begin();
}

template<class T, bool shortCount>
PLF<T, shortCount>::~PLF()
{
    for (it = m_items.begin(); it != m_items.end(); ++it)
        delete *it;
}

template<class T, bool shortCount>
const T *PLF<T, shortCount>::next() const
{
    if (it == m_items.end())
        return 0;
    ++it;
    if (it != m_items.end())
        return *it;
    return 0;
}

template<class T, bool shortCount>
const T *PLF<T, shortCount>::prev() const
{
    if (m_items.size() == 0)
        return 0;
    if (it != m_items.begin())
        --it;
    return *it;
}

template<class T>
class PLCF;
template<class T>
class PLCFIterator;
template<typename OldT, typename NewT>
PLCF<NewT> *convertPLCF(const PLCF<OldT> &old); // evil, eh? :-)

template<class T>
class PLCF
{
    friend class PLCFIterator<T>;
    template<typename OldT, typename NewT>
    friend PLCF<NewT> *convertPLCF(const PLCF<OldT> &old);

public:
    PLCF(U32 length, OLEStreamReader *reader, bool preservePos = false);
    PLCF(U32 length, const U8 *ptr);
    PLCF(const PLCF<T> &rhs);
    ~PLCF();

    PLCFIterator<T> at(unsigned int index) const;
    size_t count() const
    {
        return m_items.size();
    }
    bool isEmpty() const
    {
        return m_items.empty();
    }

    // Inserts the given index/item pair at the end of the PLCF (but before(!) the final n+1 index)
    // Normally you won't need that method. The ownership of the item is transferred.
    void insert(U32 index, T *item);

    void dumpCPs() const;

    // don't assign it
    PLCF<T> &operator=(const PLCF<T> &rhs) = delete;

private:
    // An empty default constructor for the convertPLCF friend. Don't use it
    // unless you know what you are doing :-)
    PLCF() = default;

    U32 calculateCount(U32 length);

    std::vector<U32> m_indices;
    std::vector<T *> m_items;
};

template<class T>
PLCF<T>::PLCF(U32 length, OLEStreamReader *reader, bool preservePos)
{
    if (preservePos)
        reader->push();
    U32 count = calculateCount(length);
    for (U32 i = 0; i < count + 1; ++i) // n+1 CPs/FCs
        m_indices.push_back(reader->readU32());
    for (U32 i = 0; i < count; ++i) // n "T"s
        m_items.push_back(new T(reader, false));
    if (preservePos)
        reader->pop();
}

template<class T>
PLCF<T>::PLCF(U32 length, const U8 *ptr)
{
    U32 count = calculateCount(length);
    for (U32 i = 0; i < count + 1; ++i, ptr += 4) // n+1 CPs/FCs
        m_indices.push_back(readU32(ptr));
    for (U32 i = 0; i < count; ++i, ptr += T::sizeOf) // n "T"s
        m_items.push_back(new T(ptr));
}

template<class T>
PLCF<T>::PLCF(const PLCF<T> &rhs)
    : m_indices(rhs.m_indices)
{
    typename std::vector<T *>::const_iterator it = rhs.m_items.begin();
    typename std::vector<T *>::const_iterator end = rhs.m_items.end();
    for (; it != end; ++it)
        m_items.push_back(new T(**it));
}

template<class T>
PLCF<T>::~PLCF()
{
    typename std::vector<T *>::const_iterator it = m_items.begin();
    for (; it != m_items.end(); ++it)
        delete *it;
}

template<class T>
PLCFIterator<T> PLCF<T>::at(unsigned int index) const
{
    PLCFIterator<T> it(*this);
    it.m_itemIt += index;
    it.m_indexIt += index;
    return it;
}

template<class T>
void PLCF<T>::insert(U32 index, T *item)
{
    if (m_indices.empty()) {
        delete item;
        return;
    }
    std::vector<U32>::iterator it(m_indices.end());
    --it;
    m_indices.insert(it, index);
    m_items.push_back(item);
}

template<class T>
void PLCF<T>::dumpCPs() const
{
    wvlog << "PLCF: count=" << count() << Qt::endl;
    std::vector<U32>::const_iterator it = m_indices.begin();
    std::vector<U32>::const_iterator end = m_indices.end();
    for (; it != end; ++it)
        wvlog << "    " << (*it) << Qt::endl;
    wvlog << "PLCF done." << Qt::endl;
}

template<class T>
U32 PLCF<T>::calculateCount(U32 length)
{
    if ((length - 4) % (T::sizeOf + 4)) {
        wvlog << "Warning: PLCF size seems to be screwed" << Qt::endl;
        wvlog << "Warning: length: " << length << ", size: " << T::sizeOf << ", mod: " << (length - 4) % (T::sizeOf + 4) << Qt::endl;
        return 0;
    }
    return (length - 4) / (T::sizeOf + 4);
}

// A method to "upgrade" the type of a PLCF from Word 6/7 to Word 8
// data structures, using the generated conversion code. Tricky :-)
// The ownership of the new PLCF is transferred to you!
template<typename OldT, typename NewT>
PLCF<NewT> *convertPLCF(const PLCF<OldT> &old)
{
    PLCF<NewT> *ret(new PLCF<NewT>);
    ret->m_indices = old.m_indices; // the indices remain the same

    typename std::vector<OldT *>::const_iterator oldIt(old.m_items.begin());
    typename std::vector<OldT *>::const_iterator oldEnd(old.m_items.end());
    for (; oldIt != oldEnd; ++oldIt)
        ret->m_items.push_back(new NewT(Word95::toWord97(**oldIt)));
    return ret;
}

template<class T>
class PLCFIterator
{
    friend PLCFIterator<T> PLCF<T>::at(unsigned int) const;

public:
    PLCFIterator(PLCFIterator &&other)
        : m_plcf(std::move(other.m_plcf))
        , m_itemIt(std::move(other.m_itemIt))
        , m_indexIt(std::move(other.m_indexIt))
    {
    }

    PLCFIterator(const PLCF<T> &plcf)
        : m_plcf(plcf)
    {
        m_itemIt = m_plcf.m_items.begin();
        m_indexIt = m_plcf.m_indices.begin();
    }

    unsigned int count() const
    {
        return m_plcf.m_items.count();
    }
    bool isEmpty() const
    {
        return m_plcf.m_items.empty();
    }

    T *toFirst();
    T *toLast();

    U32 currentStart() const
    {
        if (m_itemIt != m_plcf.m_items.end())
            return *m_indexIt;
        return 0;
    }
    U32 currentLim() const;
    T *current() const
    {
        if (m_itemIt != m_plcf.m_items.end())
            return *m_itemIt;
        return nullptr;
    }

    U32 currentRun() const
    {
        return currentLim() - currentStart();
    }

    PLCFIterator &operator++();
    PLCFIterator &operator--();

private:
    // don't assign it
    PLCFIterator(const PLCFIterator<T> &other) = delete;
    PLCFIterator<T> &operator=(const PLCFIterator<T> &rhs) = delete;

    const PLCF<T> &m_plcf;
    typename std::vector<T *>::const_iterator m_itemIt;
    std::vector<U32>::const_iterator m_indexIt;
};

template<class T>
T *PLCFIterator<T>::toFirst()
{
    m_itemIt = m_plcf.m_items.begin();
    m_indexIt = m_plcf.m_indices.begin();
    if (m_itemIt != m_plcf.m_items.end())
        return *m_itemIt;
    return nullptr;
}

// Note: m_indexIt-=2 as we have n+1 indices!
template<class T>
T *PLCFIterator<T>::toLast()
{
    m_itemIt = m_plcf.m_items.end();
    m_indexIt = m_plcf.m_indices.end();
    if (m_itemIt == m_plcf.m_items.begin())
        return 0;
    --m_itemIt;
    m_indexIt -= 2;
    return *m_itemIt;
}

template<class T>
U32 PLCFIterator<T>::currentLim() const
{
    std::vector<U32>::const_iterator it = m_indexIt;
    if (m_itemIt == m_plcf.m_items.end())
        return 0;
    ++it;
    return *it;
}

template<class T>
PLCFIterator<T> &PLCFIterator<T>::operator++()
{
    if (m_itemIt == m_plcf.m_items.end())
        return *this;
    ++m_itemIt;
    ++m_indexIt;
    return *this;
}

template<class T>
PLCFIterator<T> &PLCFIterator<T>::operator--()
{
    if (m_plcf.m_items.size() != 0 && m_itemIt != m_plcf.m_items.begin()) {
        --m_itemIt;
        --m_indexIt;
    }
    return *this;
}

template<class T>
class PLCFMap
{
public:
    PLCFMap(U32 length, OLEStreamReader *reader, bool preservePos = false);

    T *item(U32 index) const;

private:
    U32 calculateCount(U32 length) const;

    std::map<U32, std::unique_ptr<T>> m_items;
};

template<class T>
PLCFMap<T>::PLCFMap(U32 length, OLEStreamReader *reader, bool preservePos)
{
    if (preservePos)
        reader->push();
    U32 count = calculateCount(length);
    std::vector<U32> indices;
    for (U32 i = 0; i < count + 1; ++i) // n+1 CPs/FCs
        indices.push_back(reader->readU32());
    for (U32 i = 0; i < count; ++i) { // n "T"s
        m_items.insert(std::make_pair(indices[i], std::make_unique<T>(reader, false)));
    }
    if (preservePos)
        reader->pop();
}

template<class T>
T *PLCFMap<T>::item(U32 index) const
{
    auto it = m_items.find(index);
    return it != m_items.end() ? it->second.get() : nullptr;
}

template<class T>
U32 PLCFMap<T>::calculateCount(U32 length) const
{
    if ((length - 4) % (T::sizeOf + 4)) {
        wvlog << "Warning: PLCFMap size seems to be screwed" << Qt::endl;
        wvlog << "Warning: length: " << length << ", size: " << T::sizeOf << ", mod: " << (length - 4) % (T::sizeOf + 4) << Qt::endl;
        return 0;
    }
    return (length - 4) / (T::sizeOf + 4);
}

template<class PHE>
struct BX;
template<class Offset>
class FKP;
template<class Offset>
class FKPIterator;
FKP<BX<Word97::PHE>> *convertFKP(const FKP<BX<Word95::PHE>> &old);

template<class Offset>
class FKP
{
    friend class FKPIterator<Offset>;
    friend FKP<BX<Word97::PHE>> *convertFKP(const FKP<BX<Word95::PHE>> &old);

public:
    FKP(OLEStreamReader *reader, bool preservePos = false);
    FKP(const U8 *ptr);
    FKP(const FKP<Offset> &rhs);
    ~FKP()
    {
        delete[] m_rgfc;
        delete[] m_rgb;
        delete[] m_fkp;
    }

    unsigned int crun() const
    {
        return m_crun;
    }
    bool isEmpty() const
    {
        return m_crun == 0;
    }

    // don't assign it
    FKP<Offset> &operator=(const FKP<Offset> &rhs) = delete;

private:
    // An empty default constructor for the convertFKP friend. Don't use it
    // unless you know what you are doing :-)
    FKP() = default;

    U8 m_crun;
    U32 *m_rgfc; // array of FCs (crun+1)
    Offset *m_rgb; // array of offsets/BXs
    U16 m_internalOffset; // offset to the start position of the "rest"
    U8 *m_fkp; // the "rest" of the FKP
};

template<class Offset>
FKP<Offset>::FKP(OLEStreamReader *reader, bool preservePos)
{
    if (preservePos)
        reader->push();
    reader->push();
    reader->seek(511, WV2_SEEK_CUR);
    m_crun = reader->readU8();
    reader->pop();

    m_rgfc = new U32[m_crun + 1];
    for (U16 i = 0; i <= m_crun; ++i) // <= crun, because of crun+1 FCs!
        m_rgfc[i] = reader->readU32();

    m_rgb = new Offset[m_crun];
    for (U8 i = 0; i < m_crun; ++i)
        m_rgb[i].read(reader, false);

    m_internalOffset = (static_cast<U16>(m_crun) + 1) * 4 + static_cast<U16>(m_crun) * Offset::sizeOf;

    // store the rest of the FKP in an internal array for later use
    const U16 length = 511 - m_internalOffset;
    m_fkp = new U8[length]; // 511, because we don't need crun
    for (U16 i = 0; i < length; ++i)
        m_fkp[i] = reader->readU8();

    if (preservePos)
        reader->pop();
}

template<class Offset>
FKP<Offset>::FKP(const U8 *ptr)
{
    m_crun = ptr[511];

    m_rgfc = new U32[m_crun + 1];
    for (U16 i = 0; i <= m_crun; ++i, ptr += 4) // <= crun, because of crun+1 FCs!
        m_rgfc[i] = readU32(ptr);

    m_rgb = new Offset[m_crun];
    for (U8 i = 0; i < m_crun; ++i, ptr += Offset::sizeOf)
        m_rgb[i].readPtr(ptr);

    m_internalOffset = (static_cast<U16>(m_crun) + 1) * 4 + static_cast<U16>(m_crun) * Offset::sizeOf;

    // store the rest of the FKP in an internal array for later use
    U16 length = 511 - m_internalOffset;
    m_fkp = new U8[length]; // 511, because we don't need crun
    for (U16 i = 0; i < length; ++i, ++ptr)
        m_fkp[i] = *ptr;
}

template<class Offset>
FKP<Offset>::FKP(const FKP<Offset> &rhs)
    : m_crun(rhs.m_crun)
    , m_internalOffset(rhs.m_internalOffset)
{
    m_rgfc = new U32[m_crun + 1];
    ::memcpy(m_rgfc, rhs.m_rgfc, sizeof(U32) * (m_crun + 1));

    m_rgb = new Offset[m_crun];
    for (U8 i = 0; i < m_crun; ++i)
        m_rgb[i] = rhs.m_rgb[i];

    const U16 length = 511 - m_internalOffset;
    m_fkp = new U8[length]; // 511, because we don't need crun
    ::memcpy(m_fkp, rhs.m_fkp, sizeof(U8) * length);
}

// Attention: This iterator has a non standard behavior of the current()
// method. Use it that way: for( ; !it.atEnd(); ++it)
template<class Offset>
class FKPIterator
{
public:
    FKPIterator(const FKP<Offset> &fkp)
        : m_fkp(fkp)
    {
    }

    void toFirst()
    {
        m_index = 0;
    }
    void toLast()
    {
        m_index = m_fkp.m_crun - 1;
    }

    U32 currentStart() const
    {
        if (m_index < m_fkp.m_crun)
            return m_fkp.m_rgfc[m_index];
        return 0;
    }
    U32 currentLim() const
    {
        if (m_index < m_fkp.m_crun)
            return m_fkp.m_rgfc[m_index + 1];
        return 0;
    }
    Offset currentOffset() const
    {
        if (m_index < m_fkp.m_crun)
            return m_fkp.m_rgb[m_index];
        return Offset();
    }

    // Pointer to the start of the current CHPX/PAPX/..., 0 if we are at the end of the array
    // Attention: This iterator has a non standard behavior of the current()
    // method. Use it that way: for( ; !it.atEnd(); ++it)
    const U8 *current() const;

    FKPIterator &operator++()
    {
        if (m_index < m_fkp.m_crun)
            ++m_index;
        return *this;
    }
    FKPIterator &operator--()
    {
        if (m_index > 0)
            --m_index;
        return *this;
    }

    U8 index() const
    {
        return m_index;
    }
    void setIndex(U8 index)
    {
        if (index < m_fkp.m_crun)
            m_index = index;
    }

    bool atEnd() const
    {
        return m_index >= m_fkp.m_crun;
    }

    // don't copy or assign it
    FKPIterator(const FKPIterator<Offset> &rhs) = delete;
    FKPIterator<Offset> &operator=(const FKPIterator<Offset> &rhs) = delete;

private:
    const FKP<Offset> &m_fkp;
    U8 m_index{0};
};

template<class Offset>
const U8 *FKPIterator<Offset>::current() const
{
    if (m_index < m_fkp.m_crun) {
        // Note: The first byte of the "offset" types (BX or U8) is always
        // the word offset into the array (or 0!).
        const U8 tmp = m_fkp.m_rgb[m_index].offset;
        // Now we have to calculate the real offset and then locate it
        // within our cached array...
        if (tmp != 0) {
            const int pos = tmp * 2 - m_fkp.m_internalOffset;
            if (pos < 0) {
                wvlog << "ERROR: FKP internalOffset (" << m_fkp.m_internalOffset << ") is bigger than "
                      << "2*" << (int)tmp << ", FKP array index would be negative!" << Qt::endl;
                return nullptr;
            } else if (pos >= 511 - m_fkp.m_internalOffset) {
                wvlog << "ERROR: FKP array index (" << pos << " is bigger than allocated size (" << 511 - m_fkp.m_internalOffset << ")" << Qt::endl;
                return nullptr;
            } else {
                return &m_fkp.m_fkp[pos];
            }
        }
    }
    return nullptr;
}

/**
 * BX entry in a PAP FKP (1 byte word offset + 1 PHE). This is a template, as Word 95
 * PHEs are shorter than Word 97 ones.
 */
template<typename PHE>
struct BX {
    /**
     * Creates an empty BX structure and sets the defaults
     */
    BX()
    {
        clear();
    }
    /**
     * Simply calls read(...)
     */
    BX(OLEStreamReader *stream, bool preservePos = false)
    {
        clear();
        read(stream, preservePos);
    }
    /**
     * Simply calls readPtr(...)
     */
    BX(const U8 *ptr)
    {
        clear();
        readPtr(ptr);
    }

    /**
     * This method reads the BX structure from the stream.
     * If  preservePos is true we push/pop the position of
     * the stream to save the state. If it's false the state
     * of stream will be changed!
     */
    bool read(OLEStreamReader *stream, bool preservePos = false)
    {
        if (preservePos)
            stream->push();
        offset = stream->readU8();
        phe.read(stream, false);
        if (preservePos)
            stream->pop();
        return true;
    }
    /**
     * This method reads the struct from a pointer
     */
    void readPtr(const U8 *ptr)
    {
        offset = *ptr;
        ++ptr;
        phe.readPtr(ptr);
    }

    /**
     * Same as reading :)
     */
    bool write(OLEStreamWriter *stream, bool preservePos = false) const
    {
        if (preservePos)
            stream->push();
        stream->write(offset);
        phe.write(stream, false);
        if (preservePos)
            stream->pop();
        return true;
    }

    /**
     * Set all the fields to the initial value (default is 0)
     */
    void clear()
    {
        offset = 0;
        phe.clear();
    }

    // Size of the structure
    static const unsigned int sizeOf;

    // Data
    /**
     * One byte offset. This byte is a word offset, so that we can
     * address 512 bytes with one unsigned char and all the CHPX/PAPX
     * grpprls are aligned at 2 byte boundaries.
     */
    U8 offset;

    /**
     * The PHE for the paragraph described by this BX structure.
     */
    PHE phe;
};

template<typename PHE>
const unsigned int BX<PHE>::sizeOf = 1 + PHE::sizeOf;

template<typename PHE>
bool operator==(const BX<PHE> &lhs, const BX<PHE> &rhs)
{
    return lhs.offset == rhs.offset && lhs.phe == rhs.phe;
}

template<typename PHE>
bool operator!=(const BX<PHE> &lhs, const BX<PHE> &rhs)
{
    return !(lhs == rhs);
}

/**
 * A fake "BX" entry in a CHP FKP (1 byte word offset only).
 */
struct CHPFKP_BX {
    /**
     * Creates an empty CHPFKP_BX structure and sets the defaults
     */
    CHPFKP_BX();
    /**
     * Simply calls read(...)
     */
    CHPFKP_BX(OLEStreamReader *stream, bool preservePos = false);
    /**
     * Simply calls readPtr(...)
     */
    CHPFKP_BX(const U8 *ptr);

    /**
     * This method reads the CHPFKP_BX structure from the stream.
     * If  preservePos is true we push/pop the position of
     * the stream to save the state. If it's false the state
     * of stream will be changed!
     */
    bool read(OLEStreamReader *stream, bool preservePos = false);
    /**
     * This method reads the struct from a pointer
     */
    void readPtr(const U8 *ptr);

    /**
     * Same as reading :)
     */
    bool write(OLEStreamWriter *stream, bool preservePos = false) const;

    /**
     * Set all the fields to the initial value (default is 0)
     */
    void clear();

    // Size of the structure
    static const unsigned int sizeOf;

    // Data
    /**
     * One byte offset. This byte is a word offset, so that we can
     * address 512 bytes with one unsigned char and all the CHPX/PAPX
     * grpprls are aligned at 2 byte boundaries.
     */
    U8 offset;
};

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

// This enum is a "convenience enum" for reading the piece table
typedef enum {
    clxtGrpprl = 1,
    clxtPlcfpcd = 2
} clxtENUM;

} // namespace wvWare

#endif // WORD_HELPER_H
