/*
 *  Copyright 2010 OpenSourceResearchInstitute.org
 *  Licensed under GNU General Public License version 2
 *
 * Author: seanhalle@yahoo.com
 *
 */
#include <stdio.h>
#include <string.h>
#include "Histogram.h"


/*This Histogram Abstract Data Type has a number of bins plus a range of
 * values that the bins span, both chosen at creation.
 *
 *One creates a Histogram instance using the makeHistogram function, then
 * updates it with the addToHist function, and prints it out with the
 * printHist function.
 *
 *Note, the bin width is an integer, so the end of the range is adjusted
 * accordingly.  Use the bin-width to calculate the bin boundaries.
 */


Histogram *
makeHistogram( int32 numBins, int32 startOfRange, int32 endOfRange )
 {
   Histogram *hist;
   int32 i;


   hist = PR_int__malloc( sizeof(Histogram) );
   hist->bins = PR_int__malloc( numBins * sizeof(int32) );

   hist->numBins      = numBins;
   hist->binWidth     = (endOfRange - startOfRange) / numBins;
   hist->endOfRange   = startOfRange + hist->binWidth * numBins;
   hist->startOfRange = startOfRange;

   for( i = 0; i < hist->numBins; i++ )
    {
      hist->bins[ i ] = 0;
    }

   hist->name = NULL;
   return hist;
 }

inline void
makeHist_helper( Histogram *hist, int32 numBins,
                 int32 startOfRange, int32 binWidth, char *nameCopy )
 {
   hist->numBins      = numBins;
   hist->binWidth     = binWidth;
   hist->endOfRange   = startOfRange + hist->binWidth * numBins;
   hist->startOfRange = startOfRange;
   hist->name         = nameCopy;
   memset( hist->bins, 0, numBins * sizeof(int32) );
 }


Histogram *
makeFixedBinHist( int32 numBins, int32 startOfRange, int32 binWidth,
                  char *name )

 {
   Histogram *hist;

   hist = PR_int__malloc( sizeof(Histogram) );
   hist->bins = PR_int__malloc( numBins * sizeof(int32) );

   char *nameCopy = (char *)PR_int__strDup(name);
   makeHist_helper( hist, numBins, startOfRange, binWidth, nameCopy);

   return hist;
 }

Histogram *
makeFixedBinHistExt( int32 numBins, int32 startOfRange, int32 binWidth,
                     char *name )

 {
   Histogram *hist;

   hist = malloc( sizeof(Histogram) );
   hist->bins = malloc( numBins * sizeof(int32) );

   makeHist_helper( hist, numBins, startOfRange, binWidth, strdup(name));

   return hist;
 }

void inline
addToHist( int32 value, Histogram *hist )
 {
   int32 binIdx;

   if( value < hist->startOfRange )
    { binIdx = 0;
    }
   else if( value > hist->endOfRange )
    { binIdx = hist->numBins - 1;
    }
   else
    {
      binIdx = (value - hist->startOfRange) / hist->binWidth;
    }

   hist->bins[ binIdx ] += 1;
 }

void inline
subFromHist( int32 value, Histogram *hist )
 {
   int32 binIdx;

   if( value < hist->startOfRange )
    { binIdx = 0;
    }
   else if( value > hist->endOfRange )
    { binIdx = hist->numBins - 1;
    }
   else
    {
      binIdx = (value - hist->startOfRange) / hist->binWidth;
    }

   hist->bins[ binIdx ] -= 1;
 }


/*Inline because use with RDTSC in innermost code so need ultra-fast
 */
void inline
addIntervalToHist( uint32 startIntvl, uint32 endIntvl, Histogram *hist )
 {
   int32 value;

   value = endIntvl - startIntvl;
   if( value < 0 || value > 10000000 ) return; //sanity check
   addToHist( value, hist );
 }

void inline
subIntervalFromHist( int32 startIntvl, int32 endIntvl, Histogram *hist )
 {
   int32 value;

   value = endIntvl - startIntvl;
   if( value < 0 || value > 10000000 ) return; //sanity check
   subFromHist( value, hist );
 }

void
saveHistToFile(Histogram *hist)
{
    FILE *output;
    int32 binIdx, binStart, binEnd, centerValue, width;
    int32 maxHeight, i,n;
    float32 total, total2, expectedValue1, expectedValue2;
    
    if(hist == NULL || hist->name == NULL)
        return;
    
    //Calculate the average
    //do all except the top bin
    maxHeight = 0; total = 0.0; expectedValue1 = 0.0;
    for( i = 0; i < hist->numBins -1; i++ )
    {
      if( maxHeight < hist->bins[ i ] ) maxHeight = hist->bins[ i ];
      total += hist->bins[ i ];
      binStart = hist->startOfRange + hist->binWidth * i;
      expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
    }
    //copy and calc expected value minus the top bin
    expectedValue2  = expectedValue1;
    expectedValue2 /= total;
    total2          = total;
    //now do last iteration, to add the top bin
    if(maxHeight < hist->bins[ i ])
                maxHeight = hist->bins[ i ];
   total += hist->bins[ i ];
   binStart = hist->startOfRange + hist->binWidth * i;
   expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
   
   expectedValue1 /= total;
   

   
    
    //If a histogram directory does not exist, do not save to file.
    //TODO change to argument
    char filename[255];    
    for(n=0;n<255;n++)
    {
        sprintf(filename, "./histograms/%s.%d.dat", hist->name,n);
        output = fopen(filename,"r");
        if(output)
        {
            fclose(output);
        }else{
            break;
        }
    }
    printf("Saving Hist to File: %s ...\n", filename);
    output = fopen(filename,"w+");
    if(output == NULL){
        printf("[!]No histogram was saved. To save histograms create folder 'histograms'.\n");
        return;
    }

    fprintf(output, "#\n# Histogram Name: %s\n", hist->name);
    fprintf(output, "# Expected Values\n");
    fprintf(output, "#\tnum samples: %d | expected value: %3.2f \n",
                                               (int)total, expectedValue1 );
    fprintf(output, "#\tminus top bin, num samples: %d | expected value: %3.2f \n",
                                               (int)total2, expectedValue2 );
    fprintf(output, "#\n# [Interval] [Center Value] [Count] [relative Count] [Width]\n");
    
    for( binIdx = 0; binIdx < hist->numBins; binIdx++ )
    {
      binStart = hist->startOfRange + hist->binWidth * binIdx;
      binEnd = binStart + hist->binWidth - 1;
      centerValue = (binStart+binEnd)/2;
      width = (binEnd-binStart)+1;
      fprintf(output, "%d-%d\t%d\t%d\t%.4f\t%d\n", binStart, binEnd, centerValue,
                                             hist->bins[ binIdx ], 
                                             hist->bins[ binIdx ]/total, width);
    }
    
    fclose(output);
    fflush(stdout);
}

void
printHist( Histogram *hist )
 {
   int32   binIdx, i, numBars, maxHeight, barValue, binStart, binEnd;
   float32 total, total2, binPercent, expectedValue1, expectedValue2;

   if( hist == NULL ) return;
   
      //do all except the top bin
   maxHeight = 0; total = 0.0; expectedValue1 = 0.0;
   for( i = 0; i < hist->numBins -1; i++ )
    {
      if( maxHeight < hist->bins[ i ] ) maxHeight = hist->bins[ i ];
      total += hist->bins[ i ];
      binStart = hist->startOfRange + hist->binWidth * i;
      expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
    }
      //copy and calc expected value minus the top bin
   expectedValue2  = expectedValue1;
   expectedValue2 /= total;
   total2          = total;
      //now do last iteration, to add the top bin
   if( maxHeight < hist->bins[ i ] ) maxHeight = hist->bins[ i ];
   total += hist->bins[ i ];
   binStart = hist->startOfRange + hist->binWidth * i;
   expectedValue1 += hist->bins[ i ] * (binStart + hist->binWidth/2.0);
   
   expectedValue1 /= total;
   
   printf( "histogram: " );
   if( hist->name != NULL ) printf( "%s\n", hist->name );
   else printf( "\n" );
   printf( "               num samples: %d | expected value: %3.2f \n",
                                               (int)total, expectedValue1 );
   printf( "minus top bin, num samples: %d | expected value: %3.2f \n",
                                               (int)total2, expectedValue2 );

   if(maxHeight < 60){
       barValue = 1;
       printf("Single Bar Value: %i\n", barValue);
   }else{
       barValue = maxHeight / 60;  //60 spaces across page for tallest bin
       printf("Single Bar Value: %0.3f\n", (float)maxHeight /60);
   }
   
   if( barValue == 0 ) { printf("error: bar val zero\n"); return; }
   for( binIdx = 0; binIdx < hist->numBins; binIdx++ )
    {
      binStart = hist->startOfRange + hist->binWidth * binIdx;
      binEnd = binStart + hist->binWidth - 1;
      binPercent = 100 * hist->bins[ binIdx ] / total;
      printf("bin range: %d - %d | %3.2f", binStart, binEnd, binPercent );

      numBars = hist->bins[ binIdx ] / barValue;
         //print one bin, height of bar is num dashes across page
      for( i = 0; i < numBars; i++ )
       {
         printf("-");
       }
      printf("\n");
    }
 }

void
freeHist( Histogram *hist )
 {
   PR_int__free( hist->bins );
   PR_int__free( hist->name );
   PR_int__free( hist );
 }
void
freeHistExt( Histogram *hist )
 {
   free( hist->bins );
   free( hist->name );
   free( hist );
 }
