#include <linklistt.h>

//  put some notes here about how we do things for perf reasons.
//  so our behavior is pathological to our client usage
//  and is not totall generic.

enum 
{
    IDLDATAF_MATCH_EXACT            = 0x00000001,    //  self only
    IDLDATAF_MATCH_IMMEDIATE        = 0x00000003,    //  includes self and immediate children
    IDLDATAF_MATCH_RECURSIVE        = 0x00000007,    //  includes self and all children
//    IDLDATAF_IUNKNOWN               = 0x80000000,
//    IDLDATAF_SINGLETON              = 0x10000000,
};
typedef DWORD IDLDATAF;

class CIDLData
{
public: // basically a struct
    BOOL Init(IDLDATAF flags, INT_PTR data);
//    ~CIDLData()  { if (_flags & IDLDATAF_IUNKNOWN) ((IUnknown *)_data)->Release(); }
    HRESULT GetData(IDLDATAF flags, INT_PTR *pdata);

    IDLDATAF _flags;
    INT_PTR _data;
};

class CIDLMatchMany;
class CIDLTree;
class CIDLNode
{
public: // methods
    ~CIDLNode();

protected:  // methods
    HRESULT GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CIDLNode **ppin, IDLDATAF *pflagsFound = NULL);
    HRESULT IDList(LPITEMIDLIST *ppidl);
    BOOL Init(LPCITEMIDLIST pidl, CIDLNode *pinParent);

    BOOL _InitSF();
    HRESULT _BindToFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf);
    BOOL _IsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    CLinkedNode<CIDLNode> *_GetKid(LPCITEMIDLIST pidl);
    HRESULT _AddData(IDLDATAF flags, INT_PTR data);
    HRESULT _RemoveData(INT_PTR data);
    void _FreshenKids(void);
    BOOL _IsEmpty(void) { return _listKids.IsEmpty() && _listDatas.IsEmpty();}

protected:  // members
    LONG _cUsage;
    LPITEMIDLIST _pidl;
    IShellFolder *_psf;
    CIDLNode *_pinParent;

    //  lists
    CLinkedList<CIDLNode> _listKids;
    CLinkedList<CIDLData> _listDatas;

    friend class CIDLTree;
    friend class CIDLMatchMany;
};

typedef DWORD IDLMATCHF;

class CIDLMatchMany 
{
public:
    HRESULT Next(INT_PTR *pdata, LPITEMIDLIST *ppidl);
    CIDLMatchMany(IDLDATAF flags, CIDLNode *pin) 
        : _flags(flags), _pin(pin) { if (pin) _lw.Init(&pin->_listDatas);}
        
protected:  // members
    IDLDATAF _flags;
    CIDLNode *_pin;
    CLinkedWalk<CIDLData> _lw;
};

class CIDLTree : CIDLNode
{
public:
    HRESULT AddData(IDLDATAF flags, LPCITEMIDLIST pidlIndex, INT_PTR data);
    HRESULT RemoveData(LPCITEMIDLIST pidlIndex, INT_PTR data);
    HRESULT MatchOne(IDLDATAF flags, LPCITEMIDLIST pidlMatch, INT_PTR *pdata, LPITEMIDLIST *ppidl);
    HRESULT MatchMany(IDLDATAF flags, LPCITEMIDLIST pidlMatch, CIDLMatchMany **ppmatch);
    HRESULT Freshen(void);
    
    static HRESULT Create(CIDLTree **pptree);
protected:  // methods
    CIDLNode *_MatchNode(LPCITEMIDLIST pidlMatch, IDLMATCHF *pflags);
};