/**
 * @package flib
 */
#ifndef F_LIST_DEF_H_
#define F_LIST_DEF_H_

#include <limits.h>
#include <stdlib.h>

#include "fclassproperty.h"

typedef int flist_ix_t;

//***************************************************************************
//          DULEZITA POZNAMKA
//***************************************************************************
// FVector obsahuje kopie objektu a pri operacich jako Insert nebo Remove
// pole vector ruzne posunuje => objekty musi mit nadefinovan operator=
// pokud je potreba pole zvetsit napr. funkce Append(), muze dojit k prealokovani
// celeho pole => zavolaji se konstruktory pro vsechny prvky noveho pole (new[])
// pak operator= pro vsechny kopirovane prvky a nakonec destruktory pro
// vsechny prvky stareho pole (delete[])
// Co z toho plyne:
// - pouzivat FVector pouze pro jednoduche typy (int, char atd.) a pointery
// - pro slozitejsi objekty pouzivat FList, ktery si uchovava pointery na
//   obsahovane objekty.
// pri ruseni FListu rozhoduje vlastnost DeleteOnRemove zda bude pro ruseny pointer
// volan operator delete
//***************************************************************************

//===========================================================================
//                          FVector
//===========================================================================
//#define FEOFLIST UINT_MAX
    /// index v FVector::Pointer(unsigned ix) je vzdy kladny
//#define MAX_INDEX   UINT_MAX
#define MAX_INDEX   ((flist_ix_t)INT_MAX)
#define FEOFLIST MAX_INDEX
    /// index v FVector::Pointer(unsigned ix) se pretypovava na int
    /// a zaporne hodnoty se berou od konce
//---------------------------------------------------------------------------
//---------------------------------------------------------------------
#define INDEX_BLOCK_LEN	16
#define SHRINK_TRASHOLD	256

/**
 * generic class (based on GSA) to store objects copies (not references) <br>
 * Siutable for storing trivial types like int, char, etc.
 */
template<class T>
class FVector
{
protected:
    T *vector;
//property:
/** number of stored items */
fpropertyr  (flist_ix_t, Cnt);
/** size of alocated space (how many objects can be stored withouth reallocation)*/
fpropertyr  (flist_ix_t, Capacity);
/** class can be refcounted (ie. FString uses this property) */
fpropertyrw (int, RefCnt);
public:
	FVector(flist_ix_t init_capacity = 0)
    /// vytvori vektor o naalokovane delce Capacity() == cnt a delce Cnt() == 0
    {
        vector = NULL;
        fCapacity = 0;
        fCnt = 0;
        fRefCnt = 1;
        setCapacity(init_capacity);
    }
	FVector(const FVector& fv)
    {
        vector = NULL;
        fCapacity = 0;
        fCnt = 0;
        fRefCnt = 1;
        operator=(fv);
    }

	virtual ~FVector() {
        if(vector) free(vector);
    }

    T* Vector() {return vector;}
    void setVector(T* v, flist_ix_t cnt);

    bool IsEmpty() {
        return Cnt() == 0;
    }

    void IncrRefCnt();
    void DecrRefCnt();

    flist_ix_t Len() {
        return Cnt();
    }
    void setLen(flist_ix_t newlen) {
        setCnt(newlen);
    }

    void Clear()
    {
        setCapacity(0);
    }

    void setCnt(flist_ix_t newcnt);
    /// pokud newcnt > Capacity() zvetsi pole

    void setCapacity(flist_ix_t newcnt);
    /// zmeni velikost pole na newcnt
    /// pokud ma nove pole mene prvku nez stare zmensi i Cnt()

	void Shrink();
    /// zmensi alokovanou pamet na minimum

    /** 
     * if before_ix >= Cnt() item is appended
     * if ix < 0 insertion point is counted from the end of list
     */
	void Insert(T item, int before_ix = 0);
	void Append(T item) {
        Insert(item, Cnt());
    }
    void DynaSet(flist_ix_t ix, T item)
    {
        if(ix >= Cnt()) setCnt(ix+1);
        vector[ix] = item;
    }

	virtual void Remove(flist_ix_t ix, int cnt = 1);
	/// removes cnt items starting with index ix

	void RemoveAll() {
        Remove(0, Cnt());
    }
	void RemoveRest(flist_ix_t ix) {
        Remove(ix, Cnt() - ix);
    }

	const T& operator[](flist_ix_t ix) const {
        return *Pointer(ix);
    }
	T& operator[](flist_ix_t ix) {
        return *Pointer(ix);
    }

    /** @return pointer to the ix-th item of FVector */
    T* Pointer(flist_ix_t ix) const;

    const FVector<T>& operator=(const FVector<T>& fa);
    /// nahraje do sebe kopii fa

    void Switch(FVector<T>& fa)
    {
        T *pom = fa.vector; fa.vector = vector; vector = pom;
        flist_ix_t upom = fa.fCnt; fa.fCnt = fCnt; fCnt = upom;
        flist_ix_t apom = fa.fCapacity; fa.fCapacity = fCapacity; fCapacity = apom;
    }

    void Sort() {::MergeSort(*this);}
    flist_ix_t Seek(const T& key);
    flist_ix_t Find(const T& key, flist_ix_t start_ix = 0);
    /// if not found returns FEOFLIST
};

//===========================================================================
//                          FList
//===========================================================================
/**
 * generic class to store objects using their references
 */
template<class T>
class FList : public FVector<T*>
{
private:
///property:
/**
 * If set, class call delete for all stored references if it is destroyed<br>
 * Default is TRUE
 */
fpropertyrw (bool, DeleteOnRemove);
/// if set (default), list items are deleted on item removing
/// or list destruction
public:
	FList(flist_ix_t init_capacity = 0) : FVector<T*>(init_capacity) {
        setCnt(0);
        setDeleteOnRemove(true);
    }

	FList(const FList& fl) : FVector<T*>(fl.Capacity())
    {
        operator=(fl);
    }

	virtual ~FList() {
        Clear();
    }

    void Clear() {
        RemoveAll();
        setCapacity(0);
    }

    void setCnt(flist_ix_t newcnt);
    /// nastavi pocet prvku pole na cnt, ale vsechny jsou NULL
    void setLen(flist_ix_t newcnt) {
        setCnt(newcnt);
    }

    // if before_ix >= Cnt() item is appended
//	void Insert(T *item, unsigned int before_ix = 0) {index.Insert(item, before_ix);}
//	void Append(T *item) {Insert(item, Cnt());}

	// removes clicnt clients starting with index clino
	virtual void Remove(flist_ix_t ix, int cnt = 1);
    /// has to be virtual. In other case RemoveAll() calls FVector::Remove()

//	void RemoveAll() {Remove(0, Cnt());}
//	void RemoveRest(unsigned int ix) {Remove(ix, Cnt() - ix);}

    T* Item(flist_ix_t ix) const
    /// vraci ix-ty prvek FListu (ktery je pointer, samozrejme)
    /// jako [], ale misto reference vraci pointer
    {
        return *Pointer(ix);
    }

    void setItem(flist_ix_t ix, T* pt)
    {
        T *pi = Item(ix);
        if(DeleteOnRemove()) delete pi;
        pi = pt;
    }

    T& ItemRef(flist_ix_t ix) const
    /// vraci referenci na ix-ty prvek FListu (ktery je pointer, samozrejme)
    {
        return *Item(ix);
    }

	T& operator[](flist_ix_t ix) const {
        return ItemRef(ix);
    }
    /// vraci referenci na objekt na ktery ukazuje ix-ty prvek FListu

    const FList<T>& operator=(const FList<T>& fl)
    {
        RemoveAll();
        FVector<T*>::operator=(fl);
        setDeleteOnRemove(false); // to je proto, abyc nedeletil objekty 2x
        return *this;
    }

    const FList<T>& Switch(FList<T>& fl)
    {
        FVector<T*>::Switch(fl);
        bool b = DeleteOnRemove(); setDeleteOnRemove(fl.DeleteOnRemove()); fl.setDeleteOnRemove(b);
        return *this;
    }

    void Sort() {
        bool b = DeleteOnRemove();
        setDeleteOnRemove(false);
        // jinak merge sort zavola delete pro vsechny objekty v listu
        // v destruktoru pomocneho listu
        // viz. T merged = nomerged; v MergeSort()
        ::MergeSort(*this);
        setDeleteOnRemove(b);
    }
    flist_ix_t Seek(const T& key);
    /// aby fungoval Seek() musi mit T operator < a ==
    flist_ix_t Find(const T& key, flist_ix_t start_ix = 0);
    /// if not found returns FEOFLIST
    /// aby fungoval Find() musi mit T operator ==
};

//---------------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
//--------------------------------------------------------------
#endif
