#include <iostream>
#include <fstream>
#include <iomanip>
#include "apstring.h"
#include <vector>
#include "exstring.h"
#include "helper.h"
#include "entry.h"
#include "songroot.h"
#include "userroot.h"
#include "songmaster.h"
#include "usermaster.h"
#include "avltree.h"
#include "bintree.h"
//#include "sorts.h"
using namespace std;

entry* pool;
int nextfree=0;
const int POOLSIZE=400000;

inline void build(songmaster &allsongs, usermaster &allusers, entry &bucket)
                 {allsongs.org(allusers.org(bucket,pool,nextfree,POOLSIZE));};
   //substitution function that calls both org functions in order to insert
   //and properly link a new entry

void creation(vector <apstring> &filelist, usermaster &allusers, songmaster &allsongs);
   //function that builds up master lists from list of given files

void stringentries(usermaster &allusers);
   //initializes list of pointers that lead between all entries in board

void assign_djpoints(usermaster &allusers);
   //assigns djpoints to all users

void tops(const songmaster &allsongs, char* filename, apstring title);
   //prints out a list of each song's top scorer

void top_djpoint_10(const usermaster &allusers, char* filename, apstring title);
   //prints the list of all users' 10th djpoints totals, ranked

void biglist(const usermaster &allusers, char* filename);
   //prints out a list of all entries in the database

void percentiles(usermaster &allusers, char* user, const char* filename);
   //prints out a list of a single user's songs with information and percentiles

apstring header(apstring title);
   //provides HTML code for top of page

apstring footer();
   //provides HTML code for bottom of page

template <class T>
void selectionsort(T &stock, int LIMIT, int &swaps, int &comps);

int main()
{
   srand(0);                                             //////////////////
                                                         //INITIALIZATION//
                                                         //////////////////
   pool=new entry[POOLSIZE];
   songmaster allsongs;
   usermaster allusers;
   vector <apstring> filelist;
                                                         ////////////////
                                                         //CONSTRUCTION//
                                                         ////////////////
   get_file_list_unix(filelist,"allusers");

   creation(filelist, allusers, allsongs);
   stringentries(allusers);                              //initialize "wraparound" pointer

   cout<<"Total entries: "<<nextfree<<endl;

   cout<<"Sorting...";
   for(int i=0; i<int(allsongs.songlist.size()); i++)       //sort all song's entries
   {
      allsongs.songlist[i].sort();
      allsongs.songlist[i].rank();
   }
   cout<<" done.\n";

   int dummy, dummy2;
   selectionsort(allsongs.songlist,int(allsongs.songlist.size()),dummy,dummy2);

   assign_djpoints(allusers);
   selectionsort(allusers.userlist,int(allusers.userlist.size()),dummy,dummy);

                                                         //////////////
                                                         //PROCESSING//
                                                         //////////////

   tops(allsongs,"tops.html","Song Tops");               //song tops

   top_djpoint_10(allusers,"djpoint_10.html","DJ Points");

   //percentiles(allusers,"NAUX","pctile.xls");          //percentiles for user NAUX

   //biglist(allusers,"biglist.xls");                       //write out full list
                                                         //////////////////
   //release workspaces                                  //END OF PROGRAM//
   cout<<"\n\nEOF";
   delete [] pool;
   cout<<" OK";
   return 0;
}

void creation(vector <apstring> &filelist, usermaster &allusers, songmaster &allsongs)
{
   for(int fileno=0; fileno<int(filelist.size()); fileno++)  //for all users...
   {
      char targetfile[23];
      //convert filename to char* format
      targetfile[0]='a';
      targetfile[1]='l';
      targetfile[2]='l';
      targetfile[3]='u';
      targetfile[4]='s';
      targetfile[5]='e';
      targetfile[6]='r';
      targetfile[7]='s';
      targetfile[8]='/';
      int n;
      for(n=0; n<filelist[fileno].length(); n++)
         targetfile[9+n]=filelist[fileno][n];
      targetfile[9+n]='\0';
      //targetfile now contains filename data


      exstring data;                               //create an input buffer
      data.readfile(targetfile);

      exstring header, body;
      data.between(header,"<user>","<scoredata>");       //send piece to header
      data.between(body,"<scoredata>","</scoredata>");   //send rest to body

      exstring userid,djname,loginname;

      header.between(userid,"<id>","</id>");                     //acquire user id
      header.between(djname,"<djname><![CDATA[","]]></djname>"); //acquire dj name
      header.between(loginname,"<login><![CDATA[","]]></login>");//acquire login handle

      exstring song,                               //single song entry
               bodytemp;                           //place to temp. store remainder of body

      while(body.length()>3)           //while songs still exist in data
      {
         body.between(song,"<song ","</song>");                   //separate out top song entry...
         body.substring(bodytemp,body.findme("</song>")+6,0);     //and remove it from
         body=bodytemp;                                           //rest of body array

         exstring title;      song.between(title,"<songname><![CDATA[","]]></songname>");
         exstring mode;       song.between(mode,"diff=\"","\">");
         exstring score;      song.between(score,"<exscore>","</exscore>");
         exstring songid;     song.between(songid,"<id>","</id>");
         exstring genre;      song.between(genre,"<genre><![CDATA[","]]></genre>");
         exstring bpm;        song.between(bpm,"<bpm>","</bpm>");
         exstring styles;     song.between(styles,"<styles>","</styles>");
         exstring notes;      song.between(notes,"<notes>","</notes>");
         exstring grade;      song.between(grade,"<grade>","</grade>");
         exstring djpoints;   song.between(djpoints,"<djpoints>","</djpoints>");
         exstring just;       song.between(just,"<justgreats>","</justgreats>");
         exstring great;      song.between(just,"<greats>","</greats>");
         exstring good;       song.between(just,"<good>","</good>");
         exstring bad;        song.between(just,"<bad>","</bad>");
         exstring poor;       song.between(just,"<poor>","</poor>");
         exstring highspeed;  song.between(highspeed,"<highspeed>","</highspeed>");
         exstring hidsud;     song.between(hidsud,"<hidsud>","</hidsud>");
         exstring hardeasy;   song.between(hardeasy,"<hardeasy>","</hardeasy>");
         exstring ranmir;     song.between(ranmir,"<ranmir>","</ranmir>");
         exstring autoscr;    song.between(autoscr,"<autoscratch>","</autoscratch>");
         exstring cleartype;  song.between(cleartype,"<cleartype>","</cleartype>");
         exstring date;       song.between(date,"<date>","</date>");


         if(title.length()>0)       //if entry is legitimate
         {
            entry bucket(title.getmystring(),      //package up the parameter bucket!
                         mode.getmystring(),
                         djname.getmystring(),
                         score.conv_int(),

                         songid.conv_int(),
                         genre.getmystring(),
                         bpm.conv_int(),
                         styles.getmystring(),

                         notes.conv_int(),
                         grade.getmystring(),
                         djpoints.conv_int(),
                         just.conv_int(),

                         great.conv_int(),
                         good.conv_int(),
                         bad.conv_int(),
                         poor.conv_int(),

                         highspeed.conv_int(),
                         hidsud.getmystring()[0],
                         hardeasy.getmystring()[0],
                         ranmir.getmystring()[0],

                         autoscr.getmystring()[0],
                         cleartype.getmystring(),
                         date.getmystring());

            build(allsongs,allusers,bucket);
         }
         if(nextfree%1000==0)
         {
            cout<<nextfree<<" entries allocated\nCurrent user: "
                <<filelist[fileno]<<endl;
         }
      }
   }
}

void assign_djpoints(usermaster &allusers)
{
   for(int i=0; i<int(allusers.userlist.size()); i++)
   {
      for(entry* finger=allusers.userlist[i].head; finger!=NULL; finger=finger->nextsong)
      {
         if(finger->grade=="AAA")
         {
            allusers.userlist[i].djpoint_10+=7;
            allusers.userlist[i].AAA_count++;
         }
         else if(finger->grade=="AA")
         {
            allusers.userlist[i].djpoint_10+=6;
            allusers.userlist[i].AA_count++;
         }
         else if(finger->grade=="A")
         {
            allusers.userlist[i].djpoint_10+=5;
            allusers.userlist[i].A_count++;
         }
         else if(finger->grade=="B")
         {
            allusers.userlist[i].djpoint_10+=4;
            allusers.userlist[i].B_count++;
         }
         else if(finger->grade=="C")
         {
            allusers.userlist[i].djpoint_10+=3;
            allusers.userlist[i].C_count++;
         }
         else if(finger->grade=="D")
         {
            allusers.userlist[i].djpoint_10+=2;
            allusers.userlist[i].D_count++;
         }
         else if(finger->grade=="E")
         {
            allusers.userlist[i].djpoint_10+=1;
            allusers.userlist[i].E_count++;
         }
         else if(finger->grade=="F")
         {
            allusers.userlist[i].djpoint_10+=0;
            allusers.userlist[i].F_count++;
         }

         if(finger->cleartype=="perfect" || finger->cleartype=="perfected")
         {
            allusers.userlist[i].djpoint_10+=3;
            allusers.userlist[i].perfect++;
         }
         else if(finger->cleartype=="combo" || finger->cleartype=="comboed")
         {
            allusers.userlist[i].djpoint_10+=2;
            allusers.userlist[i].combo++;
         }
         else if(finger->cleartype=="cleared")
         {
            allusers.userlist[i].djpoint_10+=1;
            allusers.userlist[i].clear++;
         }
         else if(finger->cleartype=="played")
         {
            allusers.userlist[i].djpoint_10+=0;
            allusers.userlist[i].play++;
         }
      }
   }
}

void tops(const songmaster &allsongs, char* filename, apstring title)
{
   ofstream ouf; ouf.open(filename);

   ouf<<header(title);

   ouf<<"<center><h1>SONG HIGH SCORES</h1>";

   ouf<<"<table border><tr><td width=15%>Title</td><td width=13%>L7</td><td width=13%>7K</td><td width=13%>A7</td><td width=13%>L14</td><td width=13%>14</td><td width=13%>A14</td>";

   int cur_mode=-1;

   for(int i=0; i<int(allsongs.songlist.size()); i++)       //display all songs' leaders
   {
      cur_mode=(cur_mode+1)%6;
      if(cur_mode==0)
         ouf<<"</tr><tr><td>"<<allsongs.songlist[i].head->title<<"</td>";

      if(cur_mode==0 && allsongs.songlist[i].head->mode=="L7" ||
         cur_mode==1 && allsongs.songlist[i].head->mode=="7" ||
         cur_mode==2 && allsongs.songlist[i].head->mode=="A" ||
         cur_mode==3 && allsongs.songlist[i].head->mode=="L14" ||
         cur_mode==4 && allsongs.songlist[i].head->mode=="14" ||
         cur_mode==5 && allsongs.songlist[i].head->mode=="A14")
      {
         ouf<<"<td><center>"<<allsongs.songlist[i].head->djname
            <<"<br>"<<allsongs.songlist[i].head->grade
            <<"<br>"<<allsongs.songlist[i].head->score<<"</td>";
      }
      else
      {
         ouf<<"<td></td>";
         i--;
      }
   }
   ouf<<"</tr></table>";

   ouf<<footer();

   ouf.close();
}

void top_djpoint_10(const usermaster &allusers, char* filename, apstring title)
{
   ofstream ouf; ouf.open(filename);

   ouf<<header(title);

   ouf<<"<center><h1>DJ POINTS (10th)</h1>";

   int width=5;
   ouf<<"<table border><tr>\
<td width=20%>User</td>\
<td width=15%>PTS</td>\
<td width="<<width<<"%>AAA</td>\
<td width="<<width<<"%>AA</td>\
<td width="<<width<<"%>A</td>\
<td width="<<width<<"%>B</td>\
<td width="<<width<<"%>C</td>\
<td width="<<width<<"%>D</td>\
<td width="<<width<<"%>E</td>\
<td width="<<width<<"%>F</td>\
<td width="<<width<<"%></td>\
<td width="<<width<<"%>P</td>\
<td width="<<width<<"%>FC</td>\
<td width="<<width<<"%>clr</td>\
<td width="<<width<<"%>play</td></tr>";

   for(int i=0; i<int(allusers.userlist.size()); i++)
      ouf<<"<tr><td>"<<allusers.userlist[i].djname<<"</td>"
         <<"<td>"<<allusers.userlist[i].djpoint_10<<"</td>"
         <<"<td>"<<allusers.userlist[i].AAA_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].AA_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].A_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].B_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].C_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].D_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].E_count<<"</td>"
         <<"<td>"<<allusers.userlist[i].F_count<<"</td>"
         <<"<td></td>"
         <<"<td>"<<allusers.userlist[i].perfect<<"</td>"
         <<"<td>"<<allusers.userlist[i].combo<<"</td>"
         <<"<td>"<<allusers.userlist[i].clear<<"</td>"
         <<"<td>"<<allusers.userlist[i].play<<"</td></tr>";

   ouf<<"</table>";

   ouf<<footer();

   ouf.close();

}

void biglist(const usermaster &allusers, char* filename)
{
   ofstream ouf;
   ouf.open(filename);
   for(entry* here=allusers.userlist[0].head; here!=NULL; here=here->nextsong_full)
   {
      ouf<<here->djname<<'\t'<<here->title<<'\t'<<here->genre<<'\t'<<here->mode<<'\t'<<here->score<<'\t'
         <<here->grade<<'\t'
         <<here->rank<<'\t'<<here->regs<<'\t'
         <<here->percentile<<'\t'
         <<here->sys_date<<'\t'
         <<endl;
   }
   ouf.close();
}

void stringentries(usermaster &allusers)   //string together all entries
{
   for(int i=0; i<int(allusers.userlist.size()); i++)
   {
      entry* finger=allusers.userlist[i].head;
      while(finger->nextsong!=NULL)
      {
         finger->nextsong_full=finger->nextsong;
         finger=finger->nextsong;
      }
      if(i+1<int(allusers.userlist.size()))
         finger->nextsong_full=allusers.userlist[i+1].head;
   }
}

void percentiles(usermaster &allusers, char* user, const char* filename)
{
   ofstream ouf;
   ouf.open(filename);
   ouf<<user<<" PCTILES\n";
   int finder=allusers.finduser(user);
   for(entry* here=allusers.userlist[finder].head; here!=NULL; here=here->nextsong)
   {
      ouf<<here->title<<'\t'<<here->genre<<'\t'<<here->mode<<'\t'<<here->score<<'\t'
         <<here->grade<<'\t'
         <<here->rank<<'\t'<<here->regs<<'\t'
         <<here->percentile<<'\t'
         <<here->sys_date<<'\t'
         <<endl;
   }
   ouf.close();
}

apstring header(apstring title)
{
   apstring ret="<html>\
            <head>\
            <title>";
            ret+=title;
            ret+="</title>\
            </head>\
            <body bgcolor=\"#000000\" text=\"#ffffff\" link=\"#1015ee\" alink=\"#800080\" vlink=\"#1015ee\">";
   return ret;
}

apstring footer()
{
   return "</body></html>";
}

template <class T>
void selectionsort(T &stock, int LIMIT, int &swaps, int &comps)
{
   for(int i=0; i<LIMIT-1; i++)
   {
      int target=i;
      for(int j=i+1; j<LIMIT; j++)   //seek smallest item
      {
         comps++;
         if(stock[j]<stock[target])
            target=j;
      }
      if(target!=i)
      {
         swap(stock[i],stock[target]);           //swap with front, advance front
         swaps++;
      }
   }
}
