#ifndef _picktwo_c_
#define _picktwo_c_

#include <string.h>
#include <stdlib.h>

#include "Stack.c"

/**
 * This method will pick one random StackNode out of the elements in the stack.
 * @param stack The stack to pick the element from.
 * @param probs The probability of each node being picked.
 * @return The picked node.
 **/
StackNode *pick( Stack *stack, float *probs )
{
  StackNode *c = stack->mTop;
  int x, curNode;
  float random = (float)rand()/(float)RAND_MAX * 100.0;
  for ( curNode = 0; curNode < stack->mSize; curNode++ )
  {
    if ( random <= probs[curNode] )
    {
      return c;
    }
    c = c->mPrev;
  }
  return NULL;
}

/**
 * If I new rule that is created that is at the top of the stack, try removing each one of its conjunctions and disjunctions
 * to see if those rules score well.  Keep doing this until we are back to a rule of size 1.
 * @param stack The stack to insert the backselected rules into.
 * @param score The method of scoring.
 * @param compare The method for comparing.
 * @param rule The rule to back-select.
 **/
void backSelect( Stack *stack, float (*score)(void*), float (*compare)(void*,void*), PickObject *rule )
{
  int a, as, v, vs;
  PickObject *nRule;

  if ( rule->mSize < 2 ) return;
  for ( a = 0; a < rule->mSize - 1; a++ )
  {
    nRule = clone( rule );
    free( rule->mPicked[a].mAttrVal );
    for ( as = a; as < rule->mSize - 1; as++ )
      rule->mPicked[as] = rule->mPicked[as+1];
    nRule->mSize--;
    nRule->mPicked = (AttrValPair*)realloc( nRule->mPicked, nRule->mSize * sizeof( AttrValPair ) );
    nRule->mScore = score( nRule );
    if ( compare( nRule, rule ) > 0 )
    {
      pushWithScore( compare, nRule, stack );
      backSelect( stack, score, compare, nRule );
    }
    else
    {
      deletePickObject( nRule );
      free( nRule );
    }
  }
}

/*
struct AttrValPairStruct
{
  int mAttr;
  int *mAttrVal;
  int mSize;
};

struct PickObjectStruct
{
  AttrValPair *mPicked;
  float mScore;
  float mEffort, mPD, mPF;
  int mSize;
  float mSupport;
};
*/

/**
 * This method will pick two elements, at random, from the stack and then combine them for a new item.
 * It will then score them, based on some user defined scoring mechanism, and insert it back into the stack,
 * allowing the insert method to bubble it down to its proper position.
 * @param stack The stack to pick two from.
 * @param score The methed of scoring.
 * @param getScore The method of getting the score of the stack data.
 * @param compare A method of comparing two stack elements.
 * @param combine A method to combine two stack elements's data.
 * @returns -1 If the new item did not make it into the stack, 0 otherwise.
 **/
int pickTwo( Stack *stack, float (*score)(void*), float (*getScore)(void*), 
	     float (*compare)(void*,void*), void *(*combine)(void*,void*) )
{
  // If the stack is empty, no need to do anything.
  if ( stack->mSize <= 0 ) return -1;

  // Total weight of the stack.
  float total = 0;
  // Array of probabilities.  Sum of all elements should add up to 1.
  float *percentages = (float*)malloc( stack->mSize * sizeof( float ) );

  // Go through the list and get the total weight of the stack, ie the sum of the scores.
  StackNode *c = stack->mTop;
  while ( c != NULL )
  {
    total += getScore( c->mData );
    c = c->mPrev;
  }
  
  // Now that we have the total weight, get each element's probability.
  int count = 0;
  c = stack->mTop;
  while ( c != NULL )
  {
    percentages[count] = getScore(c->mData)/total * 100;
    if ( count > 0 ) percentages[count] += percentages[count-1];
    c = c->mPrev;
    count++;
  }


  // Now that we have the distribution, pick two elements from the list and make sure one
  // does not contain the other.
  StackNode *pick1 = pick( stack, percentages ), *pick2 = pick( stack, percentages );
   
  // Create a new node and attempt to stuff it in the stack.
  PickObject *po = combine( pick1->mData, pick2->mData );
  if ( po == NULL ) return -1;

  if ( 0 )
  {
    deletePickObject( po );
    free( po );
    return -1;
  }  

  // Clean up
  free( percentages ); 

  po->mScore = score( po );
  if ( po->mScore < 0.00001 ) po->mScore = 0.00001;

  // If the new rule makes it on to the stack, check to see if it is the best rule so far.
  // If it is, do a greedy back select and see how those rules turn out.
  if ( pushWithScore( compare, po, stack ) != -1 )
  {
    if ( compare( po, stack->mTop->mData ) >= 0 )
      backSelect( stack, score, compare, po );
    return 0;
  }

  return -1;
}

/**
 * Prints a bar chart of the distribution of weights in a stack.
 * @param stack The stack you wish to print the weight distribution of.
 * @param getScore The function used to get the score of an element in the stack.
 **/
void printDistribution( Stack *stack, float (*getScore)(void*) )
{
  int i = 0, x = 0, count = 0;
  float total = 0;
  float *percentages = (float*)malloc( stack->mSize * sizeof( float ) );
  StackNode *c = stack->mTop;

  // Go through the list and get the total weight of the stack, ie the sum of the scores.
  while ( c != NULL )
  {
    total += getScore( c->mData );
    c = c->mPrev;
  }
  
  // Now that we have the total weight, get each element's probability.
  c = stack->mTop;
  while ( c != NULL )
  {
    percentages[count] = getScore(c->mData)/total * 100;
    if ( count > 0 ) percentages[count] += percentages[count-1];
    c = c->mPrev;
    count++;
  }

  // Do the printing
  printf( "\n\nDistribution\n0%96c100\n", ' ' );
  for ( i = 0; i < 100; i++ )
  {
    if ( i > percentages[x] ) 
    {
      printf("|");
      x++;
    }
    else
    {
      printf( "-" );
    }
  }
  printf( "\n" );
  free( percentages );
}


#endif
