#include <stdio.h>
#include <time.h>
#include <string.h>
#include <math.h>
#include "Stack.c"
#include "pickTwo.c"
#include "exaustCombine.c"
#include "PickObject.c"

// Define the scoring methods. Essentially an enumerated data type.
#define LIFT 0
#define INFOGAIN 1
#define PROBSUPT 2
#define EFFEST 3

// Define the picking methods.  Essentially an enumerated data type.
#define PICK 0
#define EXAUST 1
#define PROBSUPT 2

// Define the discretization methods.  Essentially an enumerated data type.
#define EQUALRANGE 0
#define EQUALFREQ 1

/* Commenting these might be a temporary fix to a big problem, right now I get no errors with it like this
 * but that doesn't mean it is right.  I did this because getnames has these includes in them so it shouldn't matter.
 * I also commented out these three lines in getdata.c, so problems could arise from that as well.
 * #include "defns.i"
 * #include "types.i"
 * #include "extern.i"
 */
#include "getnames.c"
#include "getdata.c"
#include "nBins.c"
#include "eqFr.c"
#include "score.c"


/*  External data, described in extern.i  */
short		MaxAtt, MaxClass, MaxDiscrVal = 2;
ItemNo		MaxItem;
Description	*Item;
DiscrValue	*MaxAttVal;
char		*SpecialStatus;
String		*ClassName, *AttName, **AttValName, FileName = "DF";

/* Lift Globals */
float BaseLift;
int BestClassCount;
float BestSupport;
float ScoreChange;

/* a^2/(a+b) Globals */
int best, rest;
int **aCounts, **bCounts;
//int aCounts[50][10], bCounts[50][10];

/* EffEst Globals */
int LOC, *LOCs;
float Alpha, Beta, Gamma;


void printClassDist( void *i )
{
  PickObject *po = (PickObject*)i;
  float toRet = 0;
  int *freq = (int*)malloc( ( MaxClass + 1 ) * sizeof( int ) ), c, item, att, pairCount = 0, inst = 0, attVal;
  for ( c = 0; c < MaxClass+1; c++ ) freq[c] = 0;
  
  for ( item = 0; item < MaxItem+1; item++ )
  {
    // Determine if this line of data is included in our subset.
    // Our subset is determined by the attribute value pairs this
    // PickObject contains.
    pairCount = 0;
    for ( att = 0; att < po->mSize; att++ )
      for( attVal = 0; attVal < po->mPicked[att].mSize; attVal++ )
	if ( po->mPicked[att].mAttrVal[attVal] == Item[item][po->mPicked[att].mAttr]._discr_val )
	{
	  pairCount++;
	  attVal = po->mPicked[att].mSize;
	}

    if ( pairCount >= po->mSize )
    {
      inst++;
      freq[Class(Item[item])]++;
    }
  }

  printf( "%-30s%20s%20s\n", "Name", "Count", "Score" );
  printf( "%-30s%20s%20s\n", "----", "-----", "-----" );
  for ( c = 0; c < MaxClass+1; c++ )
  {
    printf( "%-30s%20d%20f\n", ClassName[c], freq[c], pow( ScoreChange, c+1 ) );
    toRet += ( freq[c] * pow( ScoreChange, c ) );
  }
}

void printData()
{
  printf( "\n\n" );
  int i, j, k;
  for ( i = 0; i < MaxItem; i++ )
  {
    printf( "%s, ", AttValName[0][Item[i][0]._discr_val] );
    for ( j = 1; j < MaxAtt+1; j++ )
    {
      printf( "%s, ", AttValName[j][Item[i][j]._discr_val] );
    }
    printf( "%s\n", ClassName[Class( Item[i] )] );
  }
  printf( "\n\n" );
}

void printAttributes()
{
  printf( "\n\n" );
  int att, attVal;
  for ( att = 0; att < MaxAtt + 1; att++ )
  {
    printf( "%s: %s", AttName[att], AttValName[att][1] );
    for ( attVal = 2; attVal < MaxAttVal[att] + 1; attVal++ )
      printf( ", %s", AttValName[att][attVal] );
    printf( "\n" );
  }
}

void printRuleFile( FILE *file, PickObject *po )
{
  int i, j;
  if ( SpecialStatus[po->mPicked[0].mAttr] == Nil )
    qsort( po->mPicked[0].mAttrVal, po->mPicked[0].mSize, sizeof( int ), compareInts );
  fprintf( file, "%-30s = [ ", AttName[po->mPicked[0].mAttr] );
  fprintf( file, "%s", AttValName[po->mPicked[0].mAttr][po->mPicked[0].mAttrVal[0]] );
  for ( j = 1; j < po->mPicked[0].mSize; j++ )
    fprintf( file, " OR %s", AttValName[po->mPicked[0].mAttr][po->mPicked[0].mAttrVal[j]] );
  fprintf( file, " ]\n" );
  
  for ( i = 1; i < po->mSize; i++ )
  {
    if ( SpecialStatus[po->mPicked[i].mAttr] == Nil )
      qsort( po->mPicked[i].mAttrVal, po->mPicked[i].mSize, sizeof( int ), compareInts );
    fprintf( file, "and %-26s = [ ", AttName[po->mPicked[i].mAttr] );
    fprintf( file, "%s", AttValName[po->mPicked[i].mAttr][po->mPicked[i].mAttrVal[0]] );
    for ( j = 1; j < po->mPicked[i].mSize; j++ )
      fprintf( file, " OR %s", AttValName[po->mPicked[i].mAttr][po->mPicked[i].mAttrVal[j]] );
    fprintf( file, " ]\n" );
   }

 fprintf( file, "%35cScore: %2.5f\n", ' ', po->mScore );
}

void printRule( PickObject *po )
{
  int i, j;
  
  //scoreTmp( po );
  printf( "%-30s = [ ", AttName[po->mPicked[0].mAttr] );
  printf( "%s(%d)", AttValName[po->mPicked[0].mAttr][po->mPicked[0].mAttrVal[0]], po->mPicked[0].mAttrVal[0] );
  for ( j = 1; j < po->mPicked[0].mSize; j++ )
    printf( " OR %s(%d)", AttValName[po->mPicked[0].mAttr][po->mPicked[0].mAttrVal[j]], po->mPicked[0].mAttrVal[j] );
  printf( " ]\n" );
  
  for ( i = 1; i < po->mSize; i++ )
  {
    printf( "and %-26s = [ ", AttName[po->mPicked[i].mAttr] );
    printf( "%s(%d)", AttValName[po->mPicked[i].mAttr][po->mPicked[i].mAttrVal[0]], po->mPicked[i].mAttrVal[0] );
    for ( j = 1; j < po->mPicked[i].mSize; j++ )
      printf( " OR %s(%d)", AttValName[po->mPicked[i].mAttr][po->mPicked[i].mAttrVal[j]], po->mPicked[i].mAttrVal[j] );
    printf( " ]\n" );
  }

 printf( "%35cScore: %2.5f\n", ' ', po->mScore );
 if ( LOC != 0 )
 {
   printf( "%35cEffort: %2.5f\n", ' ', po->mEffort );
   printf( "%35cPD: %2.5f\n", ' ', po->mPD );
   printf( "%35cPF: %2.5f\n", ' ', po->mPF );
 }
}

void printStack( Stack *stack, int topX )
{
  printf( "\n\n\tThe Stack\n" );
  StackNode *c = stack->mTop;
  char *fmtStr = ( char *)malloc( 11 * sizeof( char ) );
  int x = 0, i, j;
  while ( c != NULL && ( topX == -1 || x < topX ) )
  {
    PickObject *po = (PickObject*)(c->mData);
    
    printClassDist( po );
    printf( "\n" );
    //scoreTmp( po );
    printRule( po );
    c = c->mPrev;
    x++;
  }
  free( fmtStr );
  printf( "\n" );
}

/**
 * This method is invoked when either an unrecognized option is sent in or the user types the ? option.
 **/
void printHelp()
{
  printf( "Usage: ./which [OPTIONS]\n" );
  printf( "  [OPTIONS]\n" );
  printf( "    -?\t%-20s\n", "Help" );
  printf( "    -s\t%-20s\n", "Scoring Type (ps||li|ee)" );
  printf( "\t ig - %s\n", "InfoGain" );
  printf( "\t ps - %s\n", "Prob * Support" );
  printf( "\t li - %s\n", "Lift #" );
  printf( "\t\t # - Base of Lift score.\n" );
  printf( "\t ee - %s\n", "Effort, PD, PF #" );
  printf( "\t\t # - Attribute number for Lines of Code.\n" );
  printf( "    -f\t%-20s\n", "FileName $" );
  printf( "    -z\t%-20s\n", "Stack Size #" );
  printf( "-(q|a)\t%-20s\n", "Discretization Type #" );
  printf( "\t q - Equal Frequency\n" );
  printf( "\t a - Equal Range\n" );
  printf( "\t\t # - Number of Bins.\n" );
  printf( "    -r\t%-20s\n", "Number to Report #" );
  printf( "-(p|e)\t%-20s\n", "Number to Pick  #" );
  printf( "\t p - Number of random combines.\n" );
  printf( "\t e - Number of entire stack combines.\n" );
  printf( "    -b\t%-20s\n", "Best Support #" );
  printf( "    -o\t%-20s\n", "Check Old #1 #2" );
  printf( "\t\t #1 - 1 For True, 0 for False.\n" );
  printf( "\t\t #2 - Number of picks in between checks.\n" );
  printf( "\n---Configuration Paramaters---\n" );
  printf( "\t-alpha\t- Sets up the weight of PD.\n" );
  printf( "\t-beta\t- Sets up the weight of PF.\n" );
  printf( "\t-gamma\t- Sets up the weight of Effort.\n" );
  printf( "\n\n" );
}

/**
 * This is the main file of the Which algorithm.  It will handle the control of all other methods used.
 * @param argc The number of command-line arguments passed in.
 * @param argv A vector of strings that holds the commands passed in, argv[0] is ./which
 * @return 0 if exectution was a success, 1 otherwise.
 **/
int main( int argc, char *argv[] )
{
  // Set up the variables that the user can change with options.
  FileName = "cf";            // Defined in C4.5's data parsing code.
  int StackSize = -1,         // Size of the stack, -1 means the Stack has infinite size.
    PickNo = 0,               // Number of times to invoke the pickTwo module.
    ExaustNo = 0,             // Number of exaustive stack runs.
    PickType = PICK,          // Tells Which which way to pick and combine stack data.
    NBins = 10,               // Number of bins used to discritize the data.
    ReportNo = 5,             // The top # of StackElements to report.
    ScoringType = LIFT,       // The method for scoring the individual StackElements.
    CheckOld = 0,             // If this is one, we care about checking OldLift and terminating early.
    CheckEvery = 100,         // This is how many picks in between Old and current lift checks.
    DiscrMethod = EQUALRANGE, // The method of discretization.
    LOCAtt = 0;               // The attribute index of the lines of code attribute.( Used in Effort Estimation scoring )
  float OldLift = 0.0;        // This is the lift at every 100 runs.  If it does not change by 20%                              // every 100 runs, Which wiill teminate and report that rule.
  
  int t = time(0), p, att, attRange, pNo, BinNo, x;
  float (*score)(void*);
  BestSupport = 0.0;
  ScoreChange = 2;
  LOC = 0;
  PickNo = 0;
  Alpha = Beta = Gamma = 1;

  // Initialize the random timer.
  srand( time( 0 ) );
  
  // Set up a new stack with a max size of the first command-line argument
  //Stack *stack = newStack( StackSize, deletePickObject, setupPickObject );

  FileName = argv[1];

  GetNames();
  GetData( ".data" );

  //nBins( 32 );
  eqFr( 32 );

  for ( att = 0; att < MaxAtt+1; att++ )
  {
    if ( SpecialStatus[att] == Nil )
    {
      //delStack( stack );
      Stack *stack = newStack( StackSize, deletePickObject, setupPickObject );      
      BinNo = 0;
      for ( attRange = 1; attRange <= MaxAttVal[att]; attRange++ )
      {
	float vals[3] = { att, attRange, 0 };
	PickObject *po = setupPickObject( vals );
	po->mScore = scoreInfoGain( po );
	pushWithScore( comparePickObject, po, stack );
	    
      }
      for ( pNo = 0; pNo < 100; pNo++ )
	pickTwo( stack, scoreInfoGain, getScorePickObject, comparePickObject, combinePickObject );
      printStack( stack, -1 );
      printf( "\n\n" );
      att = MaxAtt + 1;
    }
  }
 
  return 0;
}
