// -*- C++ -*-

#include <string>
#include <ept/token.h>
#include <ept/core/apt.h>
#include <apt-pkg/algorithms.h>

#ifndef EPT_APT_ACTION_H
#define EPT_APT_ACTION_H

namespace ept {
namespace core {
namespace package {

struct Action {
    enum Type { Install, ReInstall, Remove, Keep, Purge, SystemUpgrade };
    Token m_token;
    Type m_type;

    Token token() { return m_token; }
    Type type() { return m_type; }

    void apply( package::Source &pkgs )
    {
        Type a = m_type;
        pkgDepCache &dc = pkgs.db().state();

        if ( a == SystemUpgrade ) {
            pkgDistUpgrade( dc );
        } else {
            if ( !pkgs.exists( m_token ) )
                return;
            pkgCache::PkgIterator p = pkgs.lookupToken( m_token );

            pkgProblemResolver fix( &dc );
            if ( a == Install || a == ReInstall ) {
                fix.Clear( p );
                fix.Protect( p );
                dc.MarkInstall( p, true );
                fix.InstallProtect();
                if ( a == ReInstall )
                    dc.SetReInstall( p, true );
            } else if ( a == Remove || a == Purge ) {
                fix.Clear( p );
                fix.Protect( p );
                fix.Remove( p );
                dc.MarkDelete( p, a == Purge ? true : false );
            } else if ( a == Keep ) {
                fix.Clear( p );
                fix.Protect( p );
                dc.MarkKeep( p, true );
            }
            fix.Resolve( true );
        }
    }

    bool redundant( package::Source &pkgs ) {
        if ( m_type == SystemUpgrade ) {
            // check whether we have any upgradable packages
            return false;
        }
        if ( !pkgs.exists( m_token ) )
            return true;
        PackageState s = pkgs.db().packageState( m_token );
        Type a = m_type;
        // if ( a == Keep && !s.upgradable() )
        // return true;
        if ( ( a == Install || a == ReInstall )
             && ( !s.upgradable() && s.installed() ) )
            return true;
        if ( ( a == Remove || a == Purge ) && !s.installed() )
            return true;
        return false;
    }

    Action( Token t, Type a )
        : m_token( t ), m_type( a )
    {}
};

struct ActionList {
    typedef std::vector< Action > List;
    List m_list;

    void clear() {
        m_list.clear();
    }

    bool empty() {
        return m_list.empty();
    }

    void add( Action a ) {
        List::iterator rm = m_list.end(), i;
        for ( i = m_list.begin(); i != m_list.end(); ++i ) {
            if ( i->token() == a.token() ) {
                rm = i;
                break;
            }
        }
        if ( rm != m_list.end() )
            m_list.erase( rm );
        // if ( a.type() != Action::Keep )
        m_list.push_back( a );
    }

    Action latest() {
        return m_list.back();
    }

    void replay( package::Source &pkgs ) {
        for ( List::iterator i = m_list.begin(); i != m_list.end(); ++i ) {
            i->apply( pkgs );
        }
    }

    void prune( package::Source &pkgs ) {
        List l;
        std::swap( l, m_list );
        for ( List::iterator i = m_list.begin(); i != m_list.end(); ++i ) {
            if ( !i->redundant( pkgs ) )
                m_list.push_back( *i );
        }
        /* We want to do but can't bind reference parameters.... (or
           maybe use remove_copy_if or whatever ... ugly
           std::remove_if( m_list.begin(), m_list.end(), std::bind2nd(
           std::mem_fun_ref( &Action::redundant ), pkgs ) ); */
    }
};

}
}
}

#endif
