#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <resultset.h>
#include "ps.h"
#include "available.h"
#include "boxbuild.h"

using namespace std;

void getConstraints(int idx,MYSQL conn,vector<vector<int> > &triggers,vector<vector<int> > &constraints);
//int get50Point(MYSQL db,int order,string tblname);
vector<int> figureSucceeds(ResultSet data,const ResultSet run,int target);

void calcNever(ofstream& out,vector<string> notavail);
void calcCombos(vector<string> names, vector<string> partners, ResultSet& data,string combofile);
void calcFooter(ofstream& out,ResultSet& run);
void printBars(ofstream& out,vector<string> namelist,vector<double> valuelist);
void calcBars(ofstream& out,
	      vector<int> succeeds,
	      vector<string> notavail,
	      vector<string> names,
	      vector<string> partners,
	      int numsucceeds,
	      vector<string>& partnerlist,
	      vector<string>& namelist,
	      vector<double>& valuelist);

int main(int argc,char** argv){
  string outfile,combos,host,user,pass,db,output;
  if(argc<2)
    {
      cout<<"Usage: "<<argv[0]<<" <run> [<filename> <combos> <host> <user> <pass> <db> <outputdb>]\n";
      return 1;
    }
  int runidx=atoi(argv[1]); // Grab the run's index
  if(argc==9)
    {
      outfile=argv[2];
      combos=argv[3];
      host=argv[4];
      user=argv[5];
      pass=argv[6];
      db=argv[7];
      output=argv[8];
    }
  else
    {
      cout<<"\nUsing default command-line params.\n";
      outfile="selsuccess.ps";
      combos="combos.txt";
      host="127.0.0.1";
      user="root";
      pass="blah";
      db="plum_data";
      output="plum_output";      
    }

  MYSQL plum_data=ResultSet::makeConn(host,user,pass,db);
  MYSQL plum_output=ResultSet::makeConn(host,user,pass,output);
  ResultSet run(&plum_data);
  run.query<<"select * from runs where idx="<<runidx;
  run.store();run.next(); // run now has all the parameters for this run

  // names and partners are now the lists of rack names and partners, in idx order.
  vector<string> names,partners;
  ResultSet tmp(&plum_data);
  tmp.query<<"select acronym, partner from racks order by idx asc";
  tmp.store();
  while(tmp.next())
    {
      names.push_back(tmp["acronym"]);
      partners.push_back(tmp["partner"]);
    }

  // Rackdata now has the data for each rack including its selection status
  ResultSet rackdata(&plum_data);
  rackdata.query<<"select racks.acronym,racks.idx,selection.status"
		<<" from selection,racks where selection.run="<<runidx
		<<" and selection.rack=racks.idx"
		<<" order by racks.idx";
  rackdata.store();

  // Build constraint table
  vector<vector<int> > triggers,constraints;
  getConstraints(runidx,plum_data,triggers,constraints);

  // Get successful percents
  int target=get50Point(plum_output,(int)run["numcombos"]/2,run["tblname"]);
  // The original parsed the point right behind the 50% point. I don't know why.
  //if(target!=run["min"])
  //  target--;

  // Get the results from the 50% point.
  ResultSet data(&plum_output);
  data.query<<"select selects from "<<run["tblname"]<<" where selectednum="<<target<<" and any_exceeded_inc=0";
  data.store(); // Data now has all the successful combinations
  int numsucceeds=data.size(); // The total number of successful combinations

  // Figure succeeds
  vector<int> succeeds=figureSucceeds(data,run,target);

  // We're going to assume that all runs use new constraints.
  vector<string> notavail;
  for(int n=0;n!=names.size();n++)
    if(!available(rackdata,constraints,triggers,names[n]))
      notavail.push_back(names[n]);

  // Make the box data
  boxBuild(runidx);

  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  // Begin writing to output files //////////////////////////////////////////////////////////////////////////////////////////
  ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  ofstream psout;psout.open(outfile.c_str());

  int nevercols=notavail.size()/10;
  if(notavail.size()%10==0)nevercols++;

  vector<string> partnerlist,namelist;
  vector<double> valuelist;

  calcBars(psout,succeeds,notavail,names,partners,numsucceeds,partnerlist,namelist,valuelist);
  
  printFile(psout,valuelist.size(),notavail.size(),numPartners(),target,run["name"]);

  printBars(psout,namelist,valuelist);

  printTables(psout,notavail.size());

  calcNever(psout,notavail);
  calcCombos(names,partners,data,combos);
  calcFooter(psout,run);
  writePartnerTable(psout);

  printFooter(psout);

  psout.close();

  return 0;
}



void calcFooter(ofstream& out,ResultSet& run){
  out<<"RUNX TEXTCELL add RUNY 0 () [\n";
  out<<"("<<run["upmass"]<<")\n";
  out<<"("<<run["bulk"]<<")\n";
  out<<"("<<run["MLE"]<<")\n";
  out<<"("<<run["crewtime"]<<")\n";
  out<<"("<<run["crewtrng"]<<")\n";
  out<<"("<<run["stowage"]<<")\n";
  out<<"("<<run["power"]<<")\n";
  out<<"("<<run["keepalive"]<<")\n";
  out<<"("<<run["hskpg"]<<")\n";
  out<<"] tableColumn\n\n";
}


void calcCombos(vector<string> names, vector<string> partners, ResultSet& data,string combofile){
  // Print the tab-separated combinations to combos file
  ofstream combos;combos.open(combofile.c_str());
  for(int n=0;n!=names.size();n++)
    combos<<names[n]<<'\t';
  combos<<'\n';
  for(int n=0;n!=partners.size();n++)
    combos<<partners[n]<<'\t';
  combos<<'\n';
  if(data.size()!=0)
    while(data.next())
      {
	string selects=data["selects"];
	for(int n=0;n!=selects.length();n++)
	  combos<<selects[n]<<'\t';
	combos<<'\n';
      }
  combos.close();

}

void calcNever(ofstream& out,vector<string> notavail){
  out<<"NEVERX NEVERY 0 () [\n";
  int n=0;
  while(n<30 && n<notavail.size()) // FIXME 30 is size of one column, shouldn't be const
    {
      out<<"("<<notavail[n]<<")\n";
      n++;
    }
  out<<"] tableColumn\n";
  if(n<notavail.size())
    {
      out<<"NEVERX TEXTCELL add NEVERY 0 () [\n";
      while(n<60) // FIXME 30 is size of one column, shouldn't be const
	{
	  if(n>=notavail.size())out<<"()\n";
	  else out<<"("<<notavail[n]<<")\n";
	  n++;
	}
      out<<"] tableColumn\n";
    }
}


void calcBars(ofstream& out,
	      vector<int> succeeds,
	      vector<string> notavail,
	      vector<string> names,
	      vector<string> partners,
	      int numsucceeds,
	      vector<string>& partnerlist,
	      vector<string>& namelist,
	      vector<double>& valuelist){
  // Print the succeeds for each rack to BARS
  for(int n=0;n!=succeeds.size();n++)
    {
      if(partners[n]=="Unknown")continue; // Don't print for racks that have no country, for some reason.
      double value=(double)succeeds[n]/(double)numsucceeds;value*=100.0;
      if(numsucceeds==0)value=0;
      bool avail=true;
      for(int j=0;j!=notavail.size();j++) // Don't print for racks that can't be selected, for some reason.
	if(notavail[j]==names[n])avail=false;
      if(avail)
	{
	  vector<double>::iterator vi=valuelist.begin();
	  vector<string>::iterator pi=partnerlist.begin(),ni=namelist.begin();
	  while(vi!=valuelist.end() && *vi > value )
	    {vi++;ni++;pi++;}
	  partnerlist.insert(pi,partners[n]);
	  namelist.insert(ni,names[n]);
	  valuelist.insert(vi,value);
	}
    }

}

void printBars(ofstream& out,vector<string> namelist,vector<double> valuelist){
  for(int n=0;n!=valuelist.size();n++)
    out<<n<<" ("<<namelist[n]<<") "<<valuelist[n]<<" drawBar\n";
}


vector<int> figureSucceeds(ResultSet data,const ResultSet run,int target){
  if(data.size()!=0)
    {
      data.next();
      int numracks=((string)data["selects"]).length();
      vector<int> succeeds;succeeds.resize(numracks); // The first is a dummy, MySQL indexes from 1
      for(int n=0;n!=succeeds.size();n++)
	succeeds[n]=0;
      do
	{
	  string selects=data["selects"];
	  for(int n=0;n!=selects.size();n++)
	    if(selects[n]=='1')succeeds[n]++;
	}while(data.next());
      return succeeds;
    }
  else
    {
      ResultSet num(run.getConn());
      num.query<<"select count(idx) as count from racks";
      num.store();num.next();
      vector<int> succeeds;succeeds.resize(num["count"]);
      for(int n=0;n!=succeeds.size();n++)
	succeeds[n]=0;
      return succeeds;
    }
}







void getConstraints(int idx,MYSQL conn,vector<vector<int> > &triggers,vector<vector<int> > &constraints){
  ResultSet runcons(&conn); // The constraints selected in this run
  runcons.query<<"select cons from runcons where run="<<idx;
  runcons.store();

  triggers.clear();constraints.clear();
  if(runcons.size()==0)return;
  while(runcons.next())
    {
      ResultSet consquery(&conn); // Make resultset on this database conn
      // Query DB for the triggers for this constraint.
      consquery.query<<"select rack,trigger from constraints where name=\""<<runcons["cons"]<<'\"';
      consquery.store(); // Run the query
      vector<int> trigger,racks; // These are the triggers/racks lists for this constraint
      if(consquery.size()==0)continue;
      while(consquery.next()) // Add them all in from the resultset
	{
	  int rack=(int)consquery["rack"];
	  if(consquery["trigger"]==1)
	    trigger.push_back(rack);
	  racks.push_back(rack);
	}
      triggers.push_back(trigger); // Add this to the overall triggers list.
      constraints.push_back(racks); // Add this to the overall triggers list.
    }
}
