/*-----------------------------------------------------------------------------

Copyright (C) 2012

A. Ronald Gallant
Post Office Box 659
Chapel Hill NC 27514
USA

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

-----------------------------------------------------------------------------*/

// For flagmap documentation read flagmap.h and frontend.cpp in that order.

//#define FULL_OUTPUT
#undef FULL_OUTPUT

#include "libscl.h" 
#include "simple.h"
#include "flagmap.h"

using namespace scl; 
using namespace std;
using namespace flagmap;

namespace flagmap {
  void define_flag(std::map<std::string,flagmap::fval>& flag)
  {
    flag["filenames_expected"] = fval("");
    flag["filenames_expected"].set = true;
    flag["h"]    = fval("-h  help message"); 
    flag["c"]    = fval("-c  filename  override default input filename",'s');
  }

  istream& process_filename(istream& fin,
      std::string progname,
      std::map<std::string,flagmap::fval> flag,
      std::vector<std::string> file)
  {
    return process_filename_example(fin,progname,flag,file);
  }
}

int main(int argc, char** argp, char** envp)
{
  map<std::string,fval> flag;
  vector<std::string> file;
  define_flag(flag);
  string progname;
  if (flagmap::process_flag(argc, argp, progname, flag, file) != 0) return 1;

  vector<string> string_names;
  vector< vector<string> > string_data;
  vector<string> numeric_names;
  realmat numeric_data;
  vector<string> unknown_names;

  string filename = "play.csv";
  if (flag["c"].set)  filename = (flag["c"].s);

  INTEGER n = csvread(filename.c_str(), true, string_names, string_data,
                numeric_names, numeric_data, unknown_names);
  if (n == 0) error("/Error, csvread failed//");

  string player = string_data[0][0];
  string strategy = string_data[1][0];
  
  INTEGER r = numeric_data.nrow();
  INTEGER c = numeric_data.ncol();

  #if defined FULL_OUTPUT
    cout << starbox("/For file play.csv, csvread returns//");
    cout << "\tLines read = " << n << '\n';
    cout << "\tstring_data.size() = " << string_data.size() << '\n';
    cout << "\tnumeric_data.nrow() = " << r << '\n';
    cout << "\tnumeric_data.ncol() = " << c << '\n';
  #endif

  if (c != 3) error("Error, expected three columns of numeric_data");

  #if defined FULL_OUTPUT
    cout << starbox("/Names of the columns of numeric_data//");
    for (vector<string>::size_type i = 0; i != numeric_names.size(); ++i) {
      cout << "\t Col"<<fmt('d',4,i+1) << "    " << numeric_names[i] << '\n';
    }

    cout << starbox("/First five rows of numeric_data//");
    cout << numeric_data("1:5","");

    cout << starbox("/Last five rows of numeric_data//");
    cout << numeric_data(seq(numeric_data.nrow()-4,numeric_data.nrow()),"");

    cout << starbox("/Names of the columns of string_data//");
    for (vector<string>::size_type i = 0; i != string_names.size(); ++i) {
      cout << "\t Col" << fmt('d',4,i+1) << "    " << string_names[i] << '\n';
    }

    cout << starbox("/First five rows of string_data//");
    for (vector<string>::size_type i=0; i!=5; ++i) {
      vector< vector<string> >::size_type j;
      for (j=0; j!=string_names.size(); ++j){
        if (j == 0 ) cout << '\t';
        cout << string_data[j][i] << "  ";
      }
      cout << '\n';
    }

    cout << starbox("/Last five rows of string_data//");
    for (vector<string>::size_type i=n-5; i<size_t(n); ++i) {
      vector< vector<string> >::size_type j;
      for (j=0; j!=string_names.size(); ++j){
        if (j == 0 ) cout << '\t';
        cout << string_data[j][i] << "  ";
      }
      cout << '\n';
    }
  #endif

  realmat wager;
  realmat payoff;
  realmat counter;

  wager.reserve(n);
  payoff.reserve(n);
  counter.reserve(n);

  n = 0;
  for (INTEGER i=1; i<=r; ++i) {
    if (string_data[0][i-1] == player && string_data[1][i-1] == strategy) {
      ++n;
      wager.push_back(numeric_data(i,1));
      payoff.push_back(numeric_data(i,2));
      counter.push_back(numeric_data(i,3));
    }
  }

  numeric_data.erase();

  string msg;

  #if defined FULL_OUTPUT
    realmat output(10,3);
    msg = "/First ten rows of data for ";
    msg += player + "/playing " + strategy + "//";
    cout << starbox(msg);
    for (INTEGER i=1; i<=10; ++i) {
      output(i,1) = wager[i];
      output(i,2) = payoff[i];
      output(i,3) = counter[i];
    }
    cout << output;

    msg = "/Last ten rows of data for ";
    msg += player + "/playing " + strategy + "//";
    cout << starbox(msg);
    for (INTEGER i=1; i<=10; ++i) {
      output(i,1) = wager[wager.nrow()-10+i];
      output(i,2) = payoff[payoff.nrow()-10+i];
      output(i,3) = counter[counter.nrow()-10+i];
    }
    cout << output;
    cout.clear();
  #endif

  if (strategy == string("come_bet_with_craps_check_and_odds")
      || strategy == string("pass_line_with_craps_check_and_odds")) {

    n = wager.nrow();

    intvec come_or_pass;
    come_or_pass.reserve(n);
    INTEGER any_craps_bet = 0;
    INTEGER any_craps_payoff = 0;

    for (INTEGER i=1; i<=n; ++i) {

      if (string_data[2][i-1] == string("any_craps_bet") && payoff[i] > 0) {
        any_craps_bet = INTEGER(wager[i]); 
        any_craps_payoff = INTEGER(payoff[i]); 
      }

      if (string_data[2][i-1] == string("come_bet") 
          || string_data[2][i-1] == string("pass_line_bet")) {

        come_or_pass.push_back(i);
      }
    }

    for (INTEGER i=1; i<=n; ++i) {
      wager[i] += any_craps_bet;
    }

    for (INTEGER i=2; i<=n; ++i) {
      if (string_data[2][i-1] == string("any_craps_bet") && payoff[i] > 0) {
        payoff[i-1] += any_craps_payoff;
      }
    }    

    wager = wager(come_or_pass,1);
    payoff = payoff(come_or_pass,1);
    counter = counter(come_or_pass,1);
    n = wager.nrow();

    #if defined FULL_OUTPUT
      msg = "/First ten rows of data after revision//";
      cout << starbox(msg);
      for (INTEGER i=1; i<=10; ++i) {
        output(i,1) = wager[i];
        output(i,2) = payoff[i];
        output(i,3) = counter[i];
      }
      cout << output;

      msg = "/Last ten rows of data after revision//";
      cout << starbox(msg);
      for (INTEGER i=1; i<=10; ++i) {
        output(i,1) = wager[wager.nrow()-10+i];
        output(i,2) = payoff[payoff.nrow()-10+i];
        output(i,3) = counter[counter.nrow()-10+i];
      }
      cout << output;
    #endif
  }

  realmat diff = wager - payoff;

  REAL sum = 0.0;
  REAL sum_min = REAL_MAX;
  REAL sum_max = -REAL_MAX;
  for (INTEGER i=1; i<=n; ++i) {
    sum += diff[i];
    sum_min = sum < sum_min ? sum : sum_min;
    sum_max = sum > sum_max ? sum : sum_max;
  }
  REAL house_bank = -sum_min + 1.0;
  REAL player_bank = sum_max + 1.0;
  REAL house_take = sum;

  #if defined FULL_OUTPUT
    msg = "/First ten rows of diff//";
    cout << starbox(msg);
    cout << diff("1:10","");
  
    msg = "/Last ten rows of diff//";
    cout << starbox(msg);
    cout << diff(seq(diff.nrow()-9,diff.nrow()),"");
  #endif

  stats stats_wager = simple(wager.begin(),wager.end());
  stats stats_payoff = simple(payoff.begin(),payoff.end());
  stats stats_diff = simple(diff.begin(),diff.end());

  wager.erase();
  payoff.erase();

  realmat dist;
  dist.reserve(n);
  for (INTEGER i=1; i<=n; i+=100) {
    REAL sum = 0.0;
    REAL c100 = counter[i] + 100.0;
    for (INTEGER j=i; j<=n; ++j) {
      if (counter[j] > c100) {
        dist.push_back(-sum);  // -sum makes it player's results
        break;
      }
      sum += diff[j];
    }
  } 
    
  #if defined FULL_OUTPUT
    msg = "/First ten rows of dist//";
    cout << starbox(msg);
    cout << dist("1:10","");
  
    msg = "/Last ten rows of dist//";
    cout << starbox(msg);
    cout << dist(seq(dist.nrow()-9,dist.nrow()),"");
  #endif

  dist.sort();

  filename = strategy + ".dat";
  ofstream fout;
  fout.open(filename.c_str());
  if (fout) {
    for (INTEGER i=1; i<=dist.size(); ++i) {
      fout << INTEGER(dist[i]) << '\n';
    }
  }

  INTEGER q01 = INTEGER(0.01*dist.size());
  INTEGER q25 = INTEGER(0.25*dist.size());
  INTEGER q50 = INTEGER(0.50*dist.size());
  INTEGER q75 = INTEGER(0.75*dist.size());
  INTEGER q99 = INTEGER(0.99*dist.size());

  q01 = q01 <= 0 ? 1 : q01;
  q25 = q25 <= 0 ? 1 : q25;
  q50 = q50 <= 0 ? 1 : q50;
  q75 = q75 <= 0 ? 1 : q75;
  q99 = q99 <= 0 ? 1 : q99;

  msg = "/Means, sdevs, cvs, of wagers, payoffs, and 100 roll quantiles/" 
         + strategy + "//"; 
  cout << starbox(msg);
  cout << "\t mean wager    " << stats_wager.mean << '\n';
  cout << "\t sdev wager    " << stats_wager.sdev << '\n';
  cout << "\t cv wager      " << stats_wager.sdev/stats_wager.mean << '\n';
  cout << "\t mean payoff   " << stats_payoff.mean << '\n';
  cout << "\t sdev payoff   " << stats_payoff.sdev << '\n';
  cout << "\t cv payoff     " << stats_payoff.sdev/stats_payoff.mean << '\n';
  cout << '\n';
  cout << "\t house bank " << fmt('f',10,0,house_bank) << '\n';
  cout << "\t house take " << fmt('f',10,0,house_take) << '\n';
  cout << "\t player bank" << fmt('f',10,0,player_bank) << '\n';
  cout << '\n';
  cout << "\t player   0%" << fmt('f',10,0,dist[1]) << '\n';
  cout << "\t player   1%" << fmt('f',10,0,dist[q01]) << '\n';
  cout << "\t player  25%" << fmt('f',10,0,dist[q25]) << '\n';
  cout << "\t player  50%" << fmt('f',10,0,dist[q50]) << '\n';
  cout << "\t player  75%" << fmt('f',10,0,dist[q75]) << '\n';
  cout << "\t player  99%" << fmt('f',10,0,dist[q99]) << '\n';
  cout << "\t player 100%" << fmt('f',10,0,dist[dist.size()]) << '\n';

  msg = "/House advantage and coefficient of variation/" + strategy + "//"; 
  cout << starbox(msg);
  REAL take = (stats_wager.mean - stats_payoff.mean)/stats_wager.mean;
  cout << "\t house %       " << 100.0*take << '\n';
  cout << "\t house cv      " << stats_diff.sdev/stats_wager.mean << '\n';

  msg = "/Stats for diff = wager - payoff/" + strategy + "//"; 
  cout << starbox(msg);
  cout << "\t mean diff     " << stats_diff.mean << '\n';
  cout << "\t sdev diff     " << stats_diff.sdev << '\n';
  cout << "\t skew diff     " << stats_diff.skew << '\n';
  cout << "\t kurt diff     " << stats_diff.kurt << '\n';
  cout << "\t nobs diff     " << stats_diff.nobs << '\n';

  return 0;
}
