/** 
* @file GenericClusterAlgorithmSelection.h
* @brief This class provides a generic chooser function for different clustering techniques
* @date 14-06-2013 (dd-mm-yyyy)
* @author Alexander Freytag
*/

#ifndef _NICE_GENERICCLUSTERALGORITHMSELECTION_INCLUDE
#define _NICE_GENERICCLUSTERALGORITHMSELECTION_INCLUDE

#include <iostream>

//abstract base class
#include "vislearning/math/cluster/ClusterAlgorithm.h"
//derived specializations
#include "vislearning/math/cluster/GMM.h"
#include "vislearning/math/cluster/KMeans.h"
#include "vislearning/math/cluster/KMeansHeuristic.h"
#include "vislearning/math/cluster/KMeansMatlab.h"
#include "vislearning/math/cluster/KMedian.h"
#include "vislearning/math/cluster/RandomClustering.h"
#include "vislearning/math/cluster/SpectralCluster.h"

namespace OBJREC {

  /** @class GenericClusterAlgorithmSelection
  * @brief This class provides a generic chooser function for different clustering techniques
  * The abstract base class is ClusterAlgorithm
  * @date 14-06-2013 (dd-mm-yyyy)
  * @author Alexander Freytag
  */
  class GenericClusterAlgorithmSelection
  {
      public:

        /** 
        * @brief This methode switches between the different clustering techniques. 
        * @param[in] conf - A pointer to the given configfile, which should contain "section" - "clusterTechnique"
        * @param[in] section - This string defines the value for "section" in the configfile.
        * @return ClusterAlgorithm* - The ClusterAlgorithm to cluster samples according to the selected clustering technique.
        * @date 14-06-2013 (dd-mm-yyyy)
        * @author Alexander Freytag
        */
        static
        OBJREC::ClusterAlgorithm *selectClusterAlgorithm ( const NICE::Config *conf, std::string section = "clustering" )
        {
          // return value
          OBJREC::ClusterAlgorithm *clusterAlgo = NULL;
        
          // string which defines the clustering technique
          std::string clusterTechnique = conf->gS(section, "clusterTechnique", "");
          
          std::cerr << "clusterTechnique: " << clusterTechnique << std::endl;
                  
          if ( ( clusterTechnique == "kmeans" ) || ( clusterTechnique == "KMeans" ) )
          {
            clusterAlgo = new OBJREC::KMeans ( conf );
          }        
          else if ( ( clusterTechnique == "kmeansHeuristic" )|| ( clusterTechnique == "KMeansHeuristic" ) )
          {
            clusterAlgo = new OBJREC::KMeansHeuristic ( conf );
          }   
          else if ( ( clusterTechnique == "kmeansMatlab" )|| ( clusterTechnique == "KMeansMatlab" ) )
          {
            clusterAlgo = new OBJREC::KMeansMatlab ( conf );
          }            
          else if ( ( clusterTechnique == "kmedian" )|| ( clusterTechnique == "KMedian" ) )
          {
            clusterAlgo = new OBJREC::KMedian ( conf );
          }     
          else if ( clusterTechnique == "GMM" )
          {
            clusterAlgo = new OBJREC::GMM ( conf );
          } 
          else if ( ( clusterTechnique == "spectral" )|| ( clusterTechnique == "SpectralCluster" ) )
          {
            clusterAlgo = new OBJREC::SpectralCluster ( conf );
          }           
          else if ( clusterTechnique == "RandomClustering" )
          {
            clusterAlgo = new OBJREC::RandomClustering ( conf );
          }            
          else 
          {
            //default: random clustering - it is easy, fast, does not need extra memory, and is still better than a NULL pointer
            std::cerr << "Unknown cluster algorithm selected, use random clustering instead" << std::endl;
            clusterAlgo = new OBJREC::RandomClustering ( conf );
          }
            
          return clusterAlgo;
        };
        
      static
      void restoreClusterAlgorithm ( ClusterAlgorithm * _clusterAlgo, std::istream & is, int format = 0 )
      {
                
        if ( is.good() )
        {
          if ( _clusterAlgo != NULL )
            delete _clusterAlgo;
          
          
          std::string className;
          is >> className; //class name
                              
          if ( className == "<KMeans>" )
          {
            _clusterAlgo = new OBJREC::KMeans();
          }
          else if ( className == "<KMeansHeuristic>" )
          {
            _clusterAlgo = new OBJREC::KMeansHeuristic();            
          }
          else if ( className == "<KMeansMatlab>" )
          {
            _clusterAlgo = new OBJREC::KMeansMatlab();
          }
          else if ( className == "<KMedian>" )
          {
            _clusterAlgo = new OBJREC::KMedian();
          }
          else if ( className == "<GMM>" )
          {
            _clusterAlgo = new OBJREC::GMM();
          } 
          else if ( className == "<SpectralCluster>" )
          {          
            _clusterAlgo = new OBJREC::SpectralCluster();          
          }
          else if ( className == "<RandomClustering>" )
          {          
            _clusterAlgo = new OBJREC::RandomClustering();          
          }          
          else
          {
            fthrow ( NICE::Exception, "GenericClusterAlgorithmSelection::restoreClusterAlgo -- class name " << className << "unknown. Aborting." );
          }
          
          //undo reading of class name
          
          for ( uint i = 0; i < className.size(); i++)
          {
            is.unget();
          }
          
          //now, call the restore method of the underlying object
          //NOTE this could be also done externally, leaving only the actual instantiation of the derived objects here
          _clusterAlgo->restore ( is );
            
        }
        else
        {
          fthrow ( NICE::Exception, "GenericClusterAlgorithmSelection::restoreClusterAlgo -- InStream not initialized - restoring not possible!" );
        }      
    };        
  };

}

#endif