/***************************************************************************
 *   Copyright (C) 2005-2007 by Rajko Albrecht                             *
 *   ral@alwins-world.de                                                   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
 ***************************************************************************/
#ifndef HELPERSCACHEENTRY_H
#define HELPERSCACHEENTRY_H

#include "svnqttypes.h"
#include "shared_pointer.h"
#include "status.h"

// std::map 'cause TQMap isn't usable
#include <map>
#include <algorithm>
#include <tqstring.h>
#include <tqstringlist.h>

namespace helpers {

/**
    Class for fast search of path based items.

    @author Rajko Albrecht <ral@alwins-world.de>
*/
template<class C> class cacheEntry {
public:
    typedef cacheEntry<C> cache_type;
    typedef typename std::map<TQString,cache_type> cache_map_type;
    typedef typename cache_map_type::const_iterator citer;
    typedef typename cache_map_type::iterator iter;

protected:
    TQString m_key;
    bool m_isValid;
    C m_content;
    cache_map_type m_subMap;

public:
    cacheEntry();
    cacheEntry(const TQString&key);
    cacheEntry(const cacheEntry<C>&other);

    virtual ~cacheEntry(){};

    virtual bool find(TQStringList&,TQLIST<C>&)const;
    //! Checks if cache contains a specific item
    /*!
     * the keylist will manipulated - so copy-operations aren't needed.
     * \param what Stringlist containing the components to search for
     * \return true if found (may or may not valid!) otherwise false
     */
    virtual bool find(TQStringList&what)const;
    //! Checks if cache contains a specific valid item
    /*!
     * if yes, the content will stored in st
     * \param what the keylist to search for
     * \param st target status to store content if found
     * \return true if found
     */
    virtual bool findSingleValid(TQStringList&what,C&st)const;
    //! Checks if cache contains a specific valid item
    /*!
     * in difference to virtual bool find(TQStringList&,svn::StatusEntries&)const no copy operations
     * are made inside so it works much faster for simple find.
     * \param what the keylist to search for
     * \param check_valid_subs if true, return true if a subitem is valid even the item isn't valid
     * \return true if found
     */
    virtual bool findSingleValid(TQStringList&what,bool check_valid_subs)const;
    template<class T> void listsubs_if(TQStringList&_what,T&oper)const;

    virtual void appendValidSub(TQLIST<C>&)const;
    virtual bool isValid()const
    {
        return m_isValid;
    }
    virtual const C&content()const
    {
        return m_content;
    }
    virtual bool deleteKey(TQStringList&,bool exact);
    virtual void insertKey(TQStringList&,const C&);
    virtual void setValidContent(const TQString&key,const C&st)
    {
        kdDebug()<<"Insert for "<<key<<endl;
        m_key = key;
        m_isValid=true;
        m_content=st;
    }
    virtual bool hasValidSubs()const;
    virtual void markInvalid() {
        m_content=C();
        m_isValid=false;
    }
    const TQString&key()const {
        return m_key;
    }

    cacheEntry<C>& operator=(const cacheEntry<C>&other);
#if 0
    void dump_tree(int level=0)const
    {
        TQString pre;
        pre.fill('-',level);
        std::map<TQString,cacheEntry>::const_iterator it;
        for (it=m_subMap.begin();it!=m_subMap.end();++it) {
            std::cout<<pre.latin1()<<it->first.latin1() << " (" << it->second.m_key.latin1() << ")"<<std::endl;
            it->second.dump_tree(level+1);
        }
    }
#endif
};

typedef cacheEntry<svn::StatusPtr> statusEntry;

template<class C> inline cacheEntry<C>::cacheEntry()
    : m_key(""),m_isValid(false),m_content()
{
}

template<class C> inline cacheEntry<C>::cacheEntry(const TQString&key)
    : m_key(key),m_isValid(false),m_content()
{
}

template<class C> inline cacheEntry<C>::cacheEntry(const cacheEntry<C>&other)
    : m_key(other.m_key),m_isValid(other.m_isValid),
        m_content(other.m_content),m_subMap(other.m_subMap)
{
}

template<class C> inline cacheEntry<C>& cacheEntry<C>::operator=(const cacheEntry<C>&other)
{
    m_key=other.m_key;
    m_isValid = other.m_isValid;
    m_content = other.m_content;
    m_subMap = other.m_subMap;
    return *this;
}

template<class C> inline  bool cacheEntry<C>::find(TQStringList&what,TQLIST<C>&t)const
{
    if (what.count()==0) {
        return false;
    }
    citer it;
    it = m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
       // kdDebug()<<what[0]<< " not found in tree"<<endl;
        return false;
    }
   // kdDebug()<<what[0]<< " found in tree"<<endl;
    if (what.count()==1) {
       // kdDebug()<<"Seems last item in stage "<< m_key << " - " << what[0] << endl;
//         if (it->second.m_key == what[0]) {
        /* the item itself */
        if (it->second.isValid()) {
            t.append(it->second.content());
        }
        /* and now check valid subitems */
           // kdDebug()<<"Appending valid subs"<<endl;
        it->second.appendValidSub(t);
           // kdDebug()<<"Appended valid subs"<<endl;
        return true;
//        }
        return false;
    }
    what.erase(what.begin());
   // kdDebug()<<"Searching "<<what<<" in next stage"<<endl;
    return it->second.find(what,t);
}

template<class C> inline bool cacheEntry<C>::find(TQStringList&what)const
{
    if (what.count()==0) {
        return false;
    }
    citer it = m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
        return false;
    }
    if (what.count()==1) {
        return true;
    }
    what.erase(what.begin());
    return it->second.find(what);
}

template<class C> inline bool cacheEntry<C>::findSingleValid(TQStringList&what,C&t)const
{
    if (what.count()==0) {
        return false;
    }
    //kdDebug()<<"cacheEntry::findSingleValid(TQStringList&what,C&t)"<< what << endl;
    citer it;
    it = m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
        //kdDebug()<<"Not found here..."<<endl;
        return false;
    }
    if (what.count()==1) {
        //kdDebug()<<"Found here and set content. "<<it->second.isValid()<<endl;
        t=it->second.content();
        return it->second.isValid();
    }
    what.erase(what.begin());
    //kdDebug()<<"Search next stage down..."<<endl;
    return it->second.findSingleValid(what,t);
}

template<class C> inline bool cacheEntry<C>::findSingleValid(TQStringList&what,bool check_valid_subs)const
{
    if (what.count()==0) {
        return false;
    }
   // kdDebug()<<"cacheEntry::findSingleValid(TQStringList&what,svn::Status&t)"<< what << endl;
    citer it = m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
        return false;
    }
    if (what.count()==1) {
        return it->second.isValid()||(check_valid_subs&&it->second.hasValidSubs());
    }
    what.erase(what.begin());
    return it->second.findSingleValid(what,check_valid_subs);
}

template<class C> inline void cacheEntry<C>::appendValidSub(TQLIST<C>&t)const
{
    citer it;
    for (it=m_subMap.begin();it!=m_subMap.end();++it) {
        if (it->second.isValid()) {
           // kdDebug()<<"Appending single sub"<<endl;
            t.append(it->second.content());
        } else {
           // kdDebug()<<it->second.key()<<" isnt valid"<<endl;
        }
        it->second.appendValidSub(t);
    }
}

template<class C> inline bool cacheEntry<C>::deleteKey(TQStringList&what,bool exact)
{
    if (what.count()==0) {
        return true;
    }
    iter it=m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
        return true;
    }
    bool caller_must_check = false;
    /* first stage - we are the one holding the right key */
    if (what.count()==1){
        if (!exact || !it->second.hasValidSubs()) {
            m_subMap.erase(it);
            caller_must_check = true;
        } else {
            it->second.markInvalid();
        }
    } else {
        /* otherwise go trough tree */
        what.erase(what.begin());
        bool b = it->second.deleteKey(what,exact);
        if (b && !it->second.hasValidSubs()) {
            m_subMap.erase(it);
            caller_must_check = true;
        }
    }
    return caller_must_check;
}

template<class C> inline bool cacheEntry<C>::hasValidSubs()const
{
    citer it;
    for (it=m_subMap.begin();it!=m_subMap.end();++it) {
        if (it->second.isValid()||it->second.hasValidSubs()) {
            return true;
        }
    }
    return false;
}

template<class C> inline void cacheEntry<C>::insertKey(TQStringList&what,const C&st)
{
    if (what.count()==0) {
        return;
    }
    //kdDebug()<<"inserting "<<what<< "into " << m_key << endl;
    TQString m = what[0];

    if (m_subMap.find(m)==m_subMap.end()) {
        m_subMap[m].m_key=m;
    }
    if (what.count()==1) {
        // kdDebug()<<"Inserting valid key "<< m << endl;
        m_subMap[m].setValidContent(m,st);
        // kdDebug()<<"Inserting valid key done"<< endl;
        return;
    }
    what.erase(what.begin());
    //kdDebug()<<"Go into loop"<<endl;
    m_subMap[m].insertKey(what,st);
}

template<class C> template<class T> inline void cacheEntry<C>::listsubs_if(TQStringList&what,T&oper)const
{
    if (what.count()==0) {
        /* we are the one to get the list for*/
        oper = for_each(m_subMap.begin(),m_subMap.end(),oper);
        return;
    }
    /* otherwise find next */
    citer it = m_subMap.find(what[0]);
    if (it==m_subMap.end()) {
        /* not found */
        return;
    }
    what.erase(what.begin());
    it->second.listsubs_if(what,oper);
}

template<class C> class itemCache
{
public:
    typedef cacheEntry<C> cache_type;
    typedef typename std::map<TQString,cache_type> cache_map_type;
    typedef typename cache_map_type::const_iterator citer;
    typedef typename cache_map_type::iterator iter;

protected:
    cache_map_type m_contentMap;

public:
    itemCache():m_contentMap(){}
    virtual ~itemCache(){};

    void setContent(const TQLIST<C>&dlist);
    void clear(){m_contentMap.clear();}
    //! Checks if cache contains a specific item
    /*!
     * the keylist will manipulated - so copy-operations aren't needed.
     * \param what Stringlist containing the components to search for
     * \return true if found (may or may not valid!) otherwise false
     */
    virtual bool find(const TQString&what)const;
    virtual bool find(const TQString&,TQLIST<C>&)const;

    virtual void deleteKey(const TQString&what,bool exact);
    virtual void insertKey(const C&,const TQString&path);
    virtual bool findSingleValid(const TQString&what,C&)const;
    virtual bool findSingleValid(const TQString&what,bool check_valid_subs)const;

    template<class T>void listsubs_if(const TQString&what,T&oper)const;

    void dump_tree();
};

template<class C> inline void itemCache<C>::setContent(const TQLIST<C>&dlist)
{
    m_contentMap.clear();
    citer it;
    for (it=dlist.begin();it!=dlist.end();++it) {
        TQStringList _keys = TQStringList::split("/",(*it).path());
        if (_keys.count()==0) {
            continue;
        }
        m_contentMap[_keys[0]]=statusEntry(_keys[0]);
        if (_keys.count()==1) {
            m_contentMap[_keys[0]].setValidContent(_keys[0],(*it));
        } else {
            _keys.erase(_keys.begin());
            m_contentMap[_keys[0]].insertKey(_keys,(*it));
        }
    }
}

template<class C> inline void itemCache<C>::insertKey(const C&st,const TQString&path)
{
   // kdDebug()<<"Inserting "<<st.path()<<endl;
    TQStringList _keys = TQStringList::split("/",path);
    if (_keys.count()==0) {
        return;
    }
    iter it=m_contentMap.find(_keys[0]);

    if (it==m_contentMap.end()) {
        m_contentMap[_keys[0]]=cache_type(_keys[0]);
    }
    if (_keys.count()==1) {
        m_contentMap[_keys[0]].setValidContent(_keys[0],st);
    } else {
        TQString m = _keys[0];
        _keys.erase(_keys.begin());
        m_contentMap[m].insertKey(_keys,st);
    }
}

template<class C> inline bool itemCache<C>::find(const TQString&what)const
{
    if (m_contentMap.size()==0) {
        return false;
    }
    TQStringList _keys = TQStringList::split("/",what);
    if (_keys.count()==0) {
        return false;
    }
    citer it=m_contentMap.find(_keys[0]);
    if (it==m_contentMap.end()) {
        return false;
    }
    if (_keys.count()==1) {
        return true;
    }
    _keys.erase(_keys.begin());
    return it->second.find(_keys);
}

template<class C> inline bool itemCache<C>::find(const TQString&_what,TQLIST<C>&dlist)const
{
    if (m_contentMap.size()==0) {
        return false;
    }
    TQStringList what = TQStringList::split("/",_what);
    if (what.count()==0) {
        return false;
    }
    citer it=m_contentMap.find(what[0]);
    if (it==m_contentMap.end()) {
        return false;
    }
    what.erase(what.begin());
   // kdDebug()<<"itemCache::find(const TQString&_what,svn::StatusEntries&dlist) "<<what<<endl;
    return it->second.find(what,dlist);
}

template<class C> inline void itemCache<C>::deleteKey(const TQString&_what,bool exact)
{
    if (m_contentMap.size()==0) {
        return;
    }
    TQStringList what = TQStringList::split("/",_what);
    if (what.count()==0) {
        return;
    }
    iter it=m_contentMap.find(what[0]);
    if (it==m_contentMap.end()) {
        return;
    }
    /* first stage - we are the one holding the right key */
    if (what.count()==1){
        if (!exact || !it->second.hasValidSubs()) {
            /* if it has no valid subs delete it */
            m_contentMap.erase(it);
        } else {
            /* otherwise mark as invalid */
            it->second.markInvalid();
        }
        return;
    } else {
        /* otherwise go trough tree */
        what.erase(what.begin());
        bool b = it->second.deleteKey(what,exact);
        if (b && !it->second.hasValidSubs()) {
            m_contentMap.erase(it);
        }
    }
}

template<class C> inline void itemCache<C>::dump_tree()
{
    citer it;
    for (it=m_contentMap.begin();it!=m_contentMap.end();++it) {
//        std::cout<<it->first.latin1() << " (" << it->second.key().latin1() << ")"<<std::endl;
//        it->second.dump_tree(1);
    }
}

template<class C> inline bool itemCache<C>::findSingleValid(const TQString&_what,C&st)const
{
    if (m_contentMap.size()==0) {
        return false;
    }
    TQStringList what = TQStringList::split("/",_what);
    if (what.count()==0) {
        return false;
    }
    //kdDebug()<<"Itemcache What: "<<what << endl;
    citer it=m_contentMap.find(what[0]);
    if (it==m_contentMap.end()) {
        //kdDebug()<<"Entry in cache not found"<<endl;
        return false;
    }
    if (what.count()==1) {
        if (it->second.isValid()) {
            st=it->second.content();
            return true;
        }
        return false;
    }
    //kdDebug()<<"Stage down"<<endl;
    what.erase(what.begin());
    return it->second.findSingleValid(what,st);
}

template<class C> inline bool itemCache<C>::findSingleValid(const TQString&_what,bool check_valid_subs)const
{
    if (m_contentMap.size()==0) {
        return false;
    }
    TQStringList what = TQStringList::split("/",_what);
    if (what.count()==0) {
        return false;
    }
    citer it=m_contentMap.find(what[0]);
    if (it==m_contentMap.end()) {
        return false;
    }
    if (what.count()==1) {
        return it->second.isValid()||(check_valid_subs&&it->second.hasValidSubs());
    }
    what.erase(what.begin());
    return it->second.findSingleValid(what,check_valid_subs);
}

template<class C> template<class T> inline void itemCache<C>::listsubs_if(const TQString&_what,T&oper)const
{
    if (m_contentMap.size()==0) {
        return;
    }
    TQStringList what = TQStringList::split("/",_what);
    if (what.count()==0) {
        return;
    }
    citer it=m_contentMap.find(what[0]);

    if (it==m_contentMap.end()) {
        return;
    }
    if (what.count()==1) {
        oper = for_each(m_contentMap.begin(),m_contentMap.end(),oper);
        return;
    }
    what.erase(what.begin());
    it->second.listsubs_if(what,oper);
}

typedef cacheEntry<svn::StatusPtr> ptrEntry;
typedef itemCache<svn::StatusPtr> statusCache;

class ValidRemoteOnly
{
    svn::StatusEntries m_List;
public:
    ValidRemoteOnly():m_List(){}
    void operator()(const std::pair<TQString,helpers::ptrEntry>&_data)
    {
        if(_data.second.isValid() && _data.second.content()->validReposStatus()&&!_data.second.content()->validLocalStatus()) {
            m_List.push_back(_data.second.content());
        }
    }
    const svn::StatusEntries&liste()const{return m_List;}
};

}

#endif
