/**
* @file LabeledSetSelection.h
* @brief Select a subset of a LabeledSet
* @author Erik Rodner
* @date 02/08/2008

*/
#ifndef LABELEDSETSELECTIONINCLUDE
#define LABELEDSETSELECTIONINCLUDE

#include "core/image/ImageT.h"
#include "core/vector/VectorT.h"
#include "core/vector/MatrixT.h"

#include <set>
#include <map>

namespace OBJREC
{

/** Select a subset of a LabeledSet */
template <class F>
class LabeledSetSelection
{

  protected:

  public:

    static void selectRandom ( const std::map<int,int> & fixedPositiveExamples, const F & base, F & positive, F & negative )
    {
      F & base_nonconst = const_cast< F & > ( base );
      //srand(time(NULL));

      for ( std::map<int,int>::const_iterator i = fixedPositiveExamples.begin();
            i != fixedPositiveExamples.end() ;
            i++ )
      {
        int classno = i->first;
        int fixedPositiveExamples = i->second;

        std::set<int> memory;

        int count = base_nonconst[classno].size();
        if ( ( count < fixedPositiveExamples ) )
        {
          fthrow ( NICE::Exception, "LabeledSetSelection::selectRandom: unable to select " << fixedPositiveExamples
                   << " of " << count << " examples" );
        }

        for ( int j = 0 ; j < fixedPositiveExamples ; j++ )
        {
          int k;
          do
          {
            k = rand() % count;
          }
          while ( memory.find ( k ) != memory.end() );

          memory.insert ( k );
          positive.add_reference ( classno, base_nonconst[classno][k] );
        }

        for ( int k = 0 ; k < count ; k++ )
          if ( memory.find ( k ) == memory.end() )
            negative.add_reference ( classno, base_nonconst[classno][k] );
      }
    }

    static void selectRandomMax ( const std::map<int,int> & fixedPositiveExamples, const F & base, F & positive, F & negative )
    {
      F & base_nonconst = const_cast< F & > ( base );
      //srand(time(NULL));

      for ( std::map<int,int>::const_iterator i = fixedPositiveExamples.begin();
            i != fixedPositiveExamples.end() ;
            i++ )
      {
        int classno = i->first;
        int fixedPositiveExamples = i->second;

        std::set<int> memory;

        int count = base_nonconst[classno].size();

        int m = fixedPositiveExamples < count ? fixedPositiveExamples : count;
        for ( int j = 0 ; j < m ; j++ )
        {
          int k;
          do
          {
            k = rand() % count;
          }
          while ( memory.find ( k ) != memory.end() );

          memory.insert ( k );
          positive.add_reference ( classno, base_nonconst[classno][k] );
        }

        for ( int k = 0 ; k < count ; k++ )
          if ( memory.find ( k ) == memory.end() )
            negative.add_reference ( classno, base_nonconst[classno][k] );
      }
    }

    static void selectSequentialStep ( const std::map<int, int> & fixedPositiveExamples, const F & base, F & positive, F & negative )
    {
      F & base_nonconst = const_cast< F & > ( base );

      for ( std::map<int,int>::const_iterator i = fixedPositiveExamples.begin();
            i != fixedPositiveExamples.end() ;
            i++ )
      {
        int classno = i->first;
        int fixedPositiveExamples = i->second;
        int count = base_nonconst[classno].size();
        int step = fixedPositiveExamples ? ( count / fixedPositiveExamples ) : 0;

        if ( count < fixedPositiveExamples )
        {
          fthrow ( NICE::Exception, "LabeledSetSelection::selectSequentialStep: unable to select " << fixedPositiveExamples
                   << " of " << count << " examples (classno " << classno << ")" );
        }


        int k = 0;
        for ( int j = 0 ; j < count ; j++ )
        {
          if ( ( step == 0 ) || ( k >= fixedPositiveExamples ) || ( j % step != 0 ) )
            negative.add_reference ( classno, base_nonconst[classno][j] );
          else
          {
            k++;
            positive.add_reference ( classno, base_nonconst[classno][j] );
          }
        }
      }
    };


    static void selectSequential ( const std::map<int, int> & fixedPositiveExamples, const F & base, F & positive, F & negative )
    {
      F & base_nonconst = const_cast< F & > ( base );

      for ( std::map<int,int>::const_iterator i = fixedPositiveExamples.begin();
            i != fixedPositiveExamples.end() ;
            i++ )
      {
        int classno = i->first;
        int fixedPositiveExamples = i->second;
        int count = base_nonconst[classno].size();

        if ( count < fixedPositiveExamples )
        {
          fthrow ( NICE::Exception, "LabeledSetSelection::selectSequential: unable to select " << fixedPositiveExamples
                   << " of " << count << " examples" );
        }

        for ( int j = 0 ; j < fixedPositiveExamples ; j++ )
          positive.add_reference ( classno, base_nonconst[classno][j] );

        for ( int j = fixedPositiveExamples ; j < count ; j++ )
          negative.add_reference ( classno, base_nonconst[classno][j] );
      }

    };

    static void selectClasses ( const std::set<int> & classnos, const F & base, F & positive, F & negative )
    {
      F & base_nonconst = const_cast< F & > ( base );

      std::vector<int> classes;
      base.getClasses ( classes );

      for ( std::set<int>::const_iterator i = classnos.begin();
            i != classnos.end() ;
            i++ )
      {
        int classno = *i;
        int count = base_nonconst[classno].size();
        for ( int j = 0 ; j < count ; j++ )
          positive.add_reference ( classno, base_nonconst[classno][j] );
      }

      for ( std::vector<int>::const_iterator i = classes.begin();
            i != classes.end() ;
            i++ )
      {
        int classno = *i;
        if ( classnos.find ( classno ) != classnos.end() ) continue;
        int count = base_nonconst[classno].size();
        for ( int j = 0 ; j < count ; j++ )
          negative.add_reference ( classno, base_nonconst[classno][j] );
      }

    };

};


} // namespace

#endif
