// Change History
// 9/15/04 AV Development: Define FAST_CALL if not already defined

//----------------------------------------------------------------------------
// AT_PagedMem.h
//
//
// (C) Archi-Tech Systems Inc, 2003
//----------------------------------------------------------------------------
#ifndef __AT_Utilities_PagedMem_H__
#define __AT_Utilities_PagedMem_H__

#include "at_types.h"
#include <vector>

#ifndef FAST_CALL
    #define FAST_CALL _fastcall
#endif

//------------------------------------------------------------------------------

class AT_PagedMem
{
private:
    uint PageSize, PageUsed, PageLimit, Size;
    std::vector<byte *> Pages;

    enum Parameters
        {
        PAGE_LIMIT = 65535
        };

protected:
    // copy constructor:
    AT_PagedMem(const AT_PagedMem & s) : PageSize(0), PageUsed(0),
        Size(0), PageLimit(s.PageLimit) {}
public:
    // default
    AT_PagedMem(uint sz = 0) : PageSize(0), PageUsed(0), Size(0)
        { uint s = sz ? sz : sizeof(byte);
            PageLimit = PAGE_LIMIT - (PAGE_LIMIT % s); }
    ~AT_PagedMem() { Clean(); }
    void SetPageLimit(uint limit = PAGE_LIMIT ) { PageLimit = limit; }
    const byte * FAST_CALL Get(uint sz)
        {
        if(PageUsed + sz >=PageSize || !GetPageCount())
            {
            if(PageSize < sz)
                PageSize = sz;
            if(PageSize < PageLimit)
                PageSize = 2 * PageSize;
            PageUsed = 0;
            byte * p = NULL;
            for(uint cnt = 0; cnt < 10; ++cnt)
                {
                try
                    {
                    p = new byte [ PageSize + 10 ];
                    break;
                    }
                catch(...)
                    {
                    assert(0);
                    //Sleep(1000);
                    if(cnt == 9)
                        return NULL;
                    }
                }
            if(!p)
                return NULL;
            Pages.push_back(p);
            }
        byte * mem = Pages[Pages.size()-1] + PageUsed;
        PageUsed += sz;
        Size += sz;
        return mem;
        }
    void RemoveLast(uint sz)
        { if(PageUsed >= sz) PageUsed -= sz; Size -= sz; }
    void Clean(void)
        {
        for(uint i=0; i<Pages.size(); ++i)
            delete [] Pages[i];

        Pages.resize(0);
        Size = PageSize = PageUsed = 0;
        }
    uint GetSize() const { return Size; }
    uint GetPageSize() const { return PageSize; }
    uint GetPageCount() const { return uint(Pages.size()); }
    uint GetPageUsed() const { return PageUsed; }
};

class AT_FixedPagedMem
{
private:
    uint PageSize, PageUsed, Size;
    std::vector<byte *> Pages;
    uint CurPageCount;
    enum { PAGE_SIZE = 1000000 };
    
protected:
    // copy constructor:
    AT_FixedPagedMem(const AT_FixedPagedMem & s) : PageSize(0), PageUsed(0),
        Size(0), CurPageCount(0), Pages() {}
public:
    // default
    AT_FixedPagedMem(void) : PageSize(PAGE_SIZE), PageUsed(0), Size(0), CurPageCount(0)
        {}
    ~AT_FixedPagedMem() { Destroy(); }
    void SetPageSize(uint size = PAGE_SIZE ) { PageSize = size; }
    const byte * FAST_CALL Get(uint sz)
        {
        if(PageUsed + sz > PageSize || !GetPageCount())
            {
            PageUsed = 0;
            if(sz > PageSize)
                PageSize = sz;
            if(CurPageCount + 1 > Pages.size())
                {
                byte * p = NULL;
                for(uint cnt = 0; cnt < 10; ++cnt)
                    {
                    try
                        {
                        p = new byte [ PageSize ];
                        break;
                        }
                    catch(...)
                        {
                        //Sleep(1000);
                        if(cnt == 9)
                            return NULL;
                        }
                    }
                if(!p)
                    return NULL;
                Pages.push_back(p);
                CurPageCount = uint(Pages.size());
                }
            else
                ++CurPageCount;
            }
        byte * mem = Pages[CurPageCount - 1] + PageUsed;
        PageUsed += sz;
        Size += sz;
        return mem;
        }
    const byte * FAST_CALL Get(uint off, uint sz) const
        {
        if(off + sz > GetSize())
            return NULL;

        uint page = off / GetPageSize();
        uint page_off = off - page * GetPageSize();
        return Pages[page] + page_off;
        }
    const byte * FAST_CALL Get(uint off, uint sz, uint & esz) const
        {
        if(off + sz > GetSize())
            return NULL;

        uint page = off / GetPageSize();
        uint page_off = off - page * GetPageSize();
        esz = GetPageSize() - page_off;
        
        return Pages[page] + page_off;
        }
    uint MemRead(byte * dest, uint sz, uint off = 0) const
    	{
    	const byte * src;
      sz = min(sz, GetSize() - off);
        uint end = sz, read_sz;
        uint read_off = off;
    	while(sz && (src = Get(read_off, sz, end)) != NULL)
    		{
          read_sz = min(end, sz);
    		memcpy(dest, src, read_sz);
    		dest += read_sz;
    		read_off += read_sz;
    		sz -= read_sz;
    		}
        return read_off - off;
    	}
    uint MemWrite(byte * src, uint sz, uint off = 0)
    	{
    	byte * dest = NULL;
    	const uint psz = GetPageSize();
    	uint pos = off;
    	while(off > GetSize())
    		{
    		// move to the offset
    		if(pos > psz)
    			{
    			dest = (byte *)Get(psz);
    			pos -= psz;
    			}
    		else
    			dest = (byte *)Get(pos);
    		}
      uint wsz = min(sz, psz - PageUsed);
        while(sz && (dest = (byte *)Get(wsz)) != NULL)
            {
            memcpy(dest, src, wsz);
            sz -= wsz;
            src += wsz;            
            wsz = min(sz, psz);
            }
        return GetSize() - off;
    	}
    void RemoveLast(uint sz)
        { if(PageUsed >= sz) PageUsed -= sz; Size -= sz; }
    void Clean(void)
        {
        CurPageCount = Size = PageUsed = 0;
        }
    void Destroy()
        {
        for(uint i=0; i<Pages.size(); ++i)
            delete [] Pages[i];

        Pages.clear();
        CurPageCount = Size = PageSize = PageUsed = 0;
        }

    uint GetSize() const { return Size; }
    uint GetPageSize() const { return PageSize; }
    uint GetPageCount() const { return CurPageCount; }
    uint GetPageUsed() const { return PageUsed; }
};

template <class T>
class AT_RandomPageAccess
{
public:
    class Iter
    {
    private:
        AT_RandomPageAccess<T> * Ptr;
        uint Offset;

    public:
        Iter(AT_RandomPageAccess<T> * p, uint off = 0) : Ptr(p), Offset(off) {}
        T & operator [] (uint num) { return *(Ptr->Get(Offset + num)); }
        Iter operator + (uint off) { return Iter(Ptr, Offset + off); }
        bool operator ! () { return !Ptr; }
    };

protected:
    enum Parameters
        {
        PAGE_SIZE = 1000000
        };

    AT_FixedPagedMem Mem;
    uint SaveMemSize;

    void set_page_size(uint size = PAGE_SIZE) { size = size - (size % sizeof(T));
       Mem.SetPageSize(size); }

public:
    AT_RandomPageAccess(uint page_sz = PAGE_SIZE) : SaveMemSize(0)
        { set_page_size(page_sz); }
    void SetPageSize(uint size = PAGE_SIZE ) { set_page_size(size); }

    // Sequantial access -----------------------------------
    void Begin() { SaveMemSize = Mem.GetSize(); Clean(); }
    T * Next() { uint sz = sizeof(T);
        if(Mem.GetSize() + sz > SaveMemSize)
            {
            SaveMemSize = 0;
            return NULL;
            }
        return (T *)Mem.Get(sz); }
    void ToEnd() { uint sz = Mem.GetSize();
        if(sz < SaveMemSize) Mem.Get(SaveMemSize - sz); }
    //-------------------------------------------------------
    T * FAST_CALL Get(uint pos) const
        {
        uint sz = sizeof(T);
        uint off = pos * sizeof(T);
        byte * m = (byte *)Mem.Get(off, sz);
        if(m)
            return (T *)m;
        else
            return NULL;
        }
    T * FAST_CALL Get(uint pos)
        {
        byte * m = NULL;
        T * i = NULL;
        uint sz = sizeof(T);
        while(pos >= GetSize())
            {
            m = (byte *)Mem.Get(sz);
            if(!m)
                return NULL;
            i = new(m) T();
            }
        if(i)
            return i;
        uint off = pos * sz;
        m = (byte *)Mem.Get(off, sz);
        if(m)
            return (T *)m;
        }
    void RemoveLast()
        { Mem.RemoveLast(sizeof(T)); }
    void Clean(void)
        {
        uint psz = Mem.GetPageSize();
        Mem.Clean();
        Mem.SetPageSize(psz);
        }
    void Destroy()
        {
        uint psz = Mem.GetPageSize();
        Mem.Destroy();
        Mem.SetPageSize(psz);
        }
    uint GetSize() const { return Mem.GetSize() / sizeof(T); }
};


class AT_FixedMemBlock
{
private:
    uint BlockOffset, Size;
    byte * Memory;

protected:
    // copy constructor:
    AT_FixedMemBlock(const AT_FixedMemBlock & rhs) : BlockOffset(rhs.BlockOffset), Size(rhs.Size),
         Memory(NULL)
    { Memory = new byte [Size]; memcpy(Memory, rhs.Memory, Size); }
public:
    // default
    AT_FixedMemBlock(void) : BlockOffset(0), Size(0), Memory(NULL)
        {}
    AT_FixedMemBlock(uint sz) : BlockOffset(0), Size(0), Memory(NULL) { Alloc(sz); }
    ~AT_FixedMemBlock() { Destroy(); }
    
    const byte * FAST_CALL Get(uint sz)
        {
        if(BlockOffset + sz >= Size)
            return NULL;

        byte * mem = Memory + BlockOffset;
        BlockOffset += sz; // move to next block
        
        return mem;
        }
    void RemoveLast(uint sz)
        { if(BlockOffset >= sz) BlockOffset -= sz; }
    void Clean(void)
        {
        BlockOffset = 0;
        }

    bool Alloc(uint sz) { Destroy(); if(!sz) return false;
        Size = sz;
        try { Memory = new byte [Size + 1]; }
        catch( ... ) { Size = 0; Memory = NULL; return false; }
        return true; }
    void Destroy() { if(Memory) delete [] Memory;
        Memory = NULL; Size = BlockOffset = 0; }
    
    uint GetSize() const { return Size; }
    uint GetOffset() const { return BlockOffset; }
};


#endif  //__AT_Utilities_PagedMem_H__


