#ifndef CodebookRandomForestINCLUDE
#define CodebookRandomForestINCLUDE


#include "core/vector/VVector.h"
#include "vislearning/features/simplefeatures/Codebook.h"
#include "vislearning/classifier/fpclassifier/randomforest/FPCRandomForests.h"

#include <string>

namespace OBJREC {

/**
 * @brief Random Forest based codebook generator (Moosmann et al.)
 *
 * Implementation of the Extremely randomized clustering forests (ERC) by (Moosmann et al., 2006)
 *
 * There also exists an Matlab Mex implementation for this class (CodebookRandomForestMex.cpp).
 *
 * @example TestCodebookRandomForest
 * @example progCodebookRandomForest
 *
 * @author Erik Rodner (creator), Johannes Ruehle (updater)
 * @date 02/15/2008, 05/05/2014
 */
class CodebookRandomForest : public Codebook
{

    protected:
		/** leafs and their corresponding indices */
	 std::map<DecisionNode *, int> leafMap;

		/** the random forest used for clustering */
		FPCRandomForests *clusterforest; 

		/** maximum depth of the forest */
		int maxDepth;

		/** the forest is reduced in pruneForest to this
		    specific number of leafs */
		int restrictedCodebookSize;

		/** build the a directed relation graph from a forest 
		    @param node starting node for traversal
		    @param parentStructure (a,b) is in this map if b is the parent node of a
		*/
		void buildParentStructure ( DecisionNode *node, 
		 std::map<DecisionNode *, DecisionNode *> & parentStructure );

		/** reduce the cluster forest to a specific number of leafs */
		void pruneForest ();

		/** enumerate all leafs and store this information in leafMap */
		void buildLeafMap ();

    public:
		/** simple constructor */
		CodebookRandomForest( int maxDepth, int restrictedCodebookSize = 0 );
		
		/** standard constructor */
		CodebookRandomForest( FPCRandomForests *clusterforest, int maxDepth, int restrictedCodebookSize = 0 );
		
		/** simple destructor */
		virtual ~CodebookRandomForest();
		
		/**
		 * set a new clusterforest
		 * @param clusterforest new random forest
		 */
		void setClusterForest( FPCRandomForests *clusterforest);
		
		/**
		 * not supported, please use one of the other vote functions
		 */
        void vote ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const;
        virtual void voteVQ ( const NICE::Vector & feature, int & codebookEntry, double & weight, double & distance ) const
        {
            this->vote(feature,codebookEntry, weight, distance);
        }

        void vote ( const NICE::Vector & feature, NICE::Vector & histogram, int & codebookEntry, double & weight, double & distance ) const;
        virtual void voteVQ (const NICE::Vector &feature, NICE::Vector &histogram, int & codebookEntry, double & weight, double & distance ) const {
            this->vote(feature, histogram, codebookEntry, weight, distance);
        }
        virtual void voteVA ( const NICE::Vector & feature, NICE::Vector & votes ) const {
            int codebookEntry = 0;
            double weight = 0.0f;
            double distance = 0.0f;
            this->vote(feature, votes, codebookEntry, weight, distance);
        }


	
		/** this is the preferred voting interface for this codebook */
        void vote ( const NICE::Vector & feature, NICE::SparseVector & votes ) const;

		/** normal codebook voting, but additionally returns a probability distribution for the class label **/
        void voteAndClassify ( const NICE::Vector & feature, NICE::SparseVector & votes, FullVector & distribution ) const;
        /** normal codebook voting, but additionally returns a probability distribution for the class label **/
        void voteAndClassify ( const NICE::Vector & feature, NICE::SparseVector & votes, NICE::Vector & distribution ) const;
        virtual void voteVA ( const NICE::Vector & feature, NICE::SparseVector & votes ) const {
            this->vote(feature, votes);
        }
		
		/** this codebook method gives multiple votes for each local feature (depending
		    on the number of decision trees in the forest */
        bool allowsMultipleVoting () { return true; }

		/** get the random forest for direct access */
        FPCRandomForests *getRandomForest (void) { return clusterforest; }

		void add ( const Codebook *codebook );
		void copy ( const Codebook *codebook );
		Codebook *clone () const;

		/** clear the codebook */
		void clear ();

		/** read the codebook from a stream */
		void restore ( std::istream & is, int format = 0);

		/** write the codebook to a stream */
		void store ( std::ostream & os, int format = 0) const;

        int getMaxDepth() const
        {
            return this->maxDepth;
        }

        int getRestrictedCodebookSize() const
        {
            return restrictedCodebookSize;
        }
};


} // namespace

#endif
