/** 
* @file KMeans.h
* @brief K-Means
* @author Erik Rodner, Alexander Freytag
* @date 29-10-2007 (dd-mm-yyyy)
*/
#ifndef KMEANSINCLUDE
#define KMEANSINCLUDE

// nice-core includes
#include <core/basics/Exception.h>
#include <core/basics/Config.h>
// 
#include <core/vector/Distance.h>
#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"
#include <core/vector/Distance.h>

#include "ClusterAlgorithm.h"


namespace OBJREC {

/** K-Means */
/**
  * @class K-Means
  * @brief K-Means
  * @author Erik Rodner, Alexander Freytag
  * @date 29-10-2007 (dd-mm-yyyy)
*/  
class KMeans : public ClusterAlgorithm
{

  protected:
        
      /************************
      * 
      *   protected variables
      * 
      **************************/         
        
        //! desired number of clusters
        int noClusters;
        
        //! specify which distance to use for calculating assignments
        std::string distanceType;
        
        //! the actual distance metric
        NICE::VectorDistance<double> *distancefunction;
        
        //! maximum difference between prototype-solutions of two iterations for convergence
        double d_minDelta;
        
        //! maximum number of iterations until convergence
        int i_maxIterations;          
        
      /************************
      * 
      *   protected methods
      * 
      **************************/          
        
      //! compute the distance between two features using the specified distance metric
        double vectorDistance(const NICE::Vector &vector1, const NICE::Vector &vector2, uint distancetype);
        
      //! compute assignments of all given features wrt to the currently known prototypes (cluster centroids) == ~ E-step
        double compute_assignments ( const NICE::VVector & features,
                                    const NICE::VVector & prototypes,
                                    std::vector<int> & assignment );

      //! compute number of assignments for every currently found cluster
        double compute_weights ( const NICE::VVector & features,
                                std::vector<double> & weights,
                                std::vector<int>    & assignment );

      //! compute the difference between prototypes of previous iteration and those currently found
        double compute_delta ( const NICE::VVector & oldprototypes,
                              const NICE::VVector & prototypes );

      //! compute (update) prototypes given the current assignments == ~ M-step
        int compute_prototypes ( const NICE::VVector & features,
                                NICE::VVector & prototypes,
                                std::vector<double> & weights,
                                const std::vector<int>    & assignment );
        
      //! have an initial guess, i.e., randomly pick some features as initial cluster centroids
        void initial_guess ( const NICE::VVector & features,
                            NICE::VVector & prototypes );
      //! give additional information for the current iteration
        void print_iteration ( int iterations,
                              NICE::VVector & prototypes,
                              double delta );

    public:
      
    ///////////////////// ///////////////////// /////////////////////
    //                   CONSTRUCTORS / DESTRUCTORS
    ///////////////////// ///////////////////// ///////////////////// 

    /** 
     * @brief default constructor
     * @date 12-02-2014 (dd-mm-yyyy )
     * @author Alexander Freytag
     */
    KMeans ( );        

      /**
      * @brief simple constructor
      * @param _noClusters the number of clusters to be computed
      * @param _distanceMode a string specifying the distance function to be used (default: euclidean)
      */
      KMeans( const int & _noClusters , const std::string & _distanceMode="euclidean");
    
      /**
      * @brief standard constructor
      * @param conf config file specifying all relevant variable settings
      * @param _section name of the section within the configfile where the settings can be found (default: KMeans)
      */
      KMeans( const NICE::Config * _conf, const std::string & _confSection = "KMeans");      

      /** simple destructor */
      virtual ~KMeans();
        
    /** 
     * @brief Jobs previously performed in the config-version of the constructor, read settings etc.
     * @author Alexander Freytag
     * @date 12-02-2014 ( dd-mm-yyyy )
     */    
    void initFromConfig ( const NICE::Config * _conf, const std::string & _confSection = "KMeans");     
    
    ///////////////////// ///////////////////// /////////////////////
    //                      CLUSTERING STUFF
    ///////////////////// ///////////////////// //////////////////         

        
        /**
        *@brief this is the actual method that performs the clustering for a given set of features
        *@author Erik Rodner, Alexander Freytag
        *@date 29-10-2007 (dd-mm-yyyy)
        *@param   features input features to be clustered
        *@param   prototypes computed prototypes (cluster medoids) for the given samples
        *@param   weights number of assignments for every cluster
        *@param   assignment explicite assignments of features to computed cluster medoids
        */            
      void cluster ( const NICE::VVector & features,
                    NICE::VVector & prototypes,
                    std::vector<double> & weights,
                    std::vector<int>    & assignment );
      
    ///////////////////// INTERFACE PERSISTENT /////////////////////
    // interface specific methods for store and restore
    ///////////////////// INTERFACE PERSISTENT /////////////////////      
    
    /**
    * @brief Load object from external file (stream)
    * @author Alexander Freytag
    * @date 12-02-2014 ( dd-mm-yyyy )
    */
    void restore ( std::istream & is, int format = 0 );

    /**
    * @brief Save object to external file (stream)
    * @author Alexander Freytag
    * @date 12-02-2014 ( dd-mm-yyyy )
    */
    void store ( std::ostream & os, int format = 0 ) const;

    /**
    * @brief Clear object
    * @author Alexander Freytag
    * @date 12-02-2014 ( dd-mm-yyyy )
    */
    void clear ();          

  };


} // namespace

#endif