// column.h --
// This is part of Metakit, see http://www.equi4.com/metakit.html

/** @file
 * Definition of the column classes
 */

#pragma once

/////////////////////////////////////////////////////////////////////////////
// Declarations in this file

class c4_Column; // a column in a table
class c4_ColIter; // an iterator over column data
class c4_ColCache; // manages a cache for columns

class c4_Persist; // not defined here
class c4_Strategy; // not defined here

/////////////////////////////////////////////////////////////////////////////

class c4_Column
{
    c4_PtrArray _segments;
    t4_i32 _position;
    t4_i32 _size;
    c4_Persist *_persist;
    t4_i32 _gap;
    int _slack;
    bool _dirty;

public:
    c4_Column(c4_Persist *persist_);
    //: Constructs a column using the specified persistence manager.
    ~c4_Column();

    void SetBuffer(t4_i32);
    //: Allocate a new buffer of the specified size.

    c4_Persist *Persist() const;
    //: Returns persistence manager for this column, or zero.
    c4_Strategy &Strategy() const;
    //: Returns the associated strategy pointer.
    t4_i32 Position() const;
    //: Special access for the DUMP program.
    t4_i32 ColSize() const;
    //: Returns the number of bytes as stored on disk.
    bool IsDirty() const;
    //: Returns true if contents needs to be saved.

    void SetLocation(t4_i32, t4_i32);
    //: Sets the position and size of this column on file.
    void PullLocation(const t4_byte * &ptr_);
    //: Extract position and size of this column.

    int AvailAt(t4_i32 offset_) const;
    //: Returns number of bytes we can access at once.
    const t4_byte *LoadNow(t4_i32);
    //: Makes sure the data is loaded into memory.
    t4_byte *CopyNow(t4_i32);
    //: Makes sure a copy of the data is in memory.
    void Grow(t4_i32, t4_i32);
    //: Grows the buffer by inserting space.
    void Shrink(t4_i32, t4_i32);
    //: Shrinks the buffer by removing space.
    void SaveNow(c4_Strategy &, t4_i32 pos_);
    //: Save the buffer to file.

    const t4_byte *FetchBytes(t4_i32 pos_, int len_, c4_Bytes &buffer_, bool
                              forceCopy_);
    //: Returns pointer to data, use buffer only if non-contiguous.
    void StoreBytes(t4_i32 pos_, const c4_Bytes &buffer_);
    //: Stores a copy of the buffer in the column.

    bool RequiresMap() const;
    void ReleaseAllSegments();

    static t4_i32 PullValue(const t4_byte * &ptr_);
    static void PushValue(t4_byte * &ptr_, t4_i32 v_);

    void InsertData(t4_i32 index_, t4_i32 count_, bool clear_);
    void RemoveData(t4_i32 index_, t4_i32 count_);
    void RemoveGap();

    enum {
        kSegBits = 12, kSegMax = 1 << kSegBits, kSegMask = kSegMax - 1
    };

private:
    static int fSegIndex(t4_i32 offset_);
    static t4_i32 fSegOffset(int index_);
    static int fSegRest(t4_i32 offset_);

    bool UsesMap(const t4_byte *) const;
    bool IsMapped() const;

    void ReleaseSegment(int);
    void SetupSegments();
    void Validate() const;
    void FinishSlack();

    void MoveGapUp(t4_i32 pos_);
    void MoveGapDown(t4_i32 pos_);
    void MoveGapTo(t4_i32 pos_);

    t4_byte *CopyData(t4_i32, t4_i32, int);
};

/////////////////////////////////////////////////////////////////////////////

class c4_ColOfInts : public c4_Column
{
public:
    c4_ColOfInts(c4_Persist *persist_, int width_ = sizeof(t4_i32));

    int RowCount() const;
    void SetRowCount(int numRows_);

    void FlipBytes();

    int ItemSize(int index_);
    const void *Get(int index_, int &length_);
    void Set(int index_, const c4_Bytes &buf_);

    t4_i32 GetInt(int index_);
    void SetInt(int index_, t4_i32 value_);

    void Insert(int index_, const c4_Bytes &buf_, int count_);
    void Remove(int index_, int count_);

    static int CalcAccessWidth(int numRows_, t4_i32 colSize_);

    void SetAccessWidth(int bits_);
    void FixSize(bool fudge_);
    void ForceFlip();

    static int DoCompare(const c4_Bytes &b1_, const c4_Bytes &b2_);

private:
    using tGetter = void (c4_ColOfInts::*)(int);
    using tSetter = bool (c4_ColOfInts::*)(int, const t4_byte *);

    void Get_0b(int index_);
    void Get_1b(int index_);
    void Get_2b(int index_);
    void Get_4b(int index_);
    void Get_8i(int index_);
    void Get_16i(int index_);
    void Get_16r(int index_);
    void Get_32i(int index_);
    void Get_32r(int index_);
    void Get_64i(int index_);
    void Get_64r(int index_);

    bool Set_0b(int index_, const t4_byte *item_);
    bool Set_1b(int index_, const t4_byte *item_);
    bool Set_2b(int index_, const t4_byte *item_);
    bool Set_4b(int index_, const t4_byte *item_);
    bool Set_8i(int index_, const t4_byte *item_);
    bool Set_16i(int index_, const t4_byte *item_);
    bool Set_16r(int index_, const t4_byte *item_);
    bool Set_32i(int index_, const t4_byte *item_);
    bool Set_32r(int index_, const t4_byte *item_);
    bool Set_64i(int index_, const t4_byte *item_);
    bool Set_64r(int index_, const t4_byte *item_);

    void ResizeData(int index_, int count_, bool clear_ = false);

    tGetter _getter;
    tSetter _setter;

    union {
        t4_byte _item[8]; // holds temp result (careful with alignment!)
        double _aligner; // needed for SPARC
    };

    int _currWidth; // number of bits used for one entry (0..64)
    int _dataWidth; // number of bytes used for passing a value along
    int _numRows;
    bool _mustFlip;
};

/////////////////////////////////////////////////////////////////////////////

class c4_ColIter
{
    c4_Column &_column;
    t4_i32 _limit;
    t4_i32 _pos;
    int _len;
    const t4_byte *_ptr;

public:
    c4_ColIter(c4_Column &col_, t4_i32 offset_, t4_i32 limit_);
    //  ~c4_ColIter ();

    bool Next();
    bool Next(int max_);

    const t4_byte *BufLoad() const;
    t4_byte *BufSave();
    int BufLen() const;
};

/////////////////////////////////////////////////////////////////////////////

#if q4_INLINE
#include "column.inl"
#endif

/////////////////////////////////////////////////////////////////////////////
