/* javascript routines for ers09 instrument */
  
function computeResults() {
 controlVars['FUNCTION_SUCCESS']=0 ;              // assume failure

var nrounds,clientIDs,poo,useShares,useDiscounts,shareList,discountsList,useQ;

// current round

nround=getVarInfo('nowround.g','value');
if (typeof(submittedData['$rounds'][nround])=='undefined') {
  poo='<b>Error:</b> submitted data does not contain information on this round ('+nround+')';
  return poo;
}

var clientIDs=extractClientList('S',nround);
  
naccept=getVarInfo('naccept.g','value');
useShares=getVarInfo('useShares.g','value');
useDiscounts=getVarInfo('useDiscounts.g','value');

useFee=getVarInfo('use_fee.g','value');
useQ=getVarInfo('qualityDiscount.g','value');
qCost=getVarInfo('quality_cost.g','value');

ntickets=getVarInfo('numtickets.g','value'); 
isDemo=getVarInfo('isDemo.g','value');

poo='<ul> \n' ;
poo=poo+'<li>Current round: '+nround + ' \n ';
if (isDemo==1) poo=poo+' (demo) ' ;
poo=poo+'<li># of entries : '+clientIDs.length+ ' \n ';
poo=poo+'<li># tickets per entry: '+ ntickets+ ' \n ';
poo=poo+'<li>Total #tickets accepted (target): '+ naccept+ ' \n ';

shareList=[];  discountsList=[1,1,1,1,1];
if (useDiscounts==1) discountsList=getVarInfo('discounts.g','value');   

if (useShares==1) shareList=getVarInfo('shares.g','value');
if (useShares==1) {
  poo=poo+ '<li> Maximum accepted per group: '+shareList.join(' | ' )+' \n ';
}

if (useDiscounts==1 || useQ==1 ){
  poo=poo+ '<li> Discounts applied per group: '+discountsList.join(' | ' )+' \n ';
  if (useQ==1) {
      var quality_points=getVarInfo('quality_points.g','value'); var quality_cost=getVarInfo('quality_cost.g','value');
      poo=poo+ '<li> Quality discounts: maximum of '+quality_points+' at '+quality_cost+' per point  \n ';
  }
 }

poo=poo+'\n </ul> \n ';

if (nround==0) {
  poo=poo+'<p/> <b>No offers in this round! </b> ';
  return poo;
}

saveResultsVar()  ;              // clear resultsData
saveResultsVar('','ROUND',nround)  ;    // set ROUND in resultsData

if (useShares!=1) poo=poo+'<p />'+doBasic(clientIDs,useDiscounts,useQ);
if (useShares==1) poo=poo+'<p />'+doShares(clientIDs,useDiscounts,useQ);

return poo ;
}

/*  =======================
 compute basic results: no discounts, no shares.

  STATUS  : 4 (assuming 4 tickets) character "disposition" code for the 4 tickets 
               Possible values are:
                1 : Not offered
                2  : Rejected in first round
                3 : Rejected in 2nd round
                4  : accepted
                .  : not available (not displayed)


*/
function doBasic(clientIDs,useDiscounts,useQ) {

var z={},zoffers={},zoffersA={};
var aid,i,m,zoffer,zstatus,zoffers ;
var totalEarnAllAccept=0,totalCostAllAccept=0;totalCostAllReject=0;
var vlist,tt,amaxbid,zoffer,score0,ofsort,nacceptDo,ddg,indices,im0,igroup ;
var submitGroups=[],acceptGroups=[];
var tmp,noOffers,gotOffers,ilistAccept,ilast;
var roundEarn,vmtx,aearn,glist ;

// make tickets object for all tickets

z=readTickets(clientIDs,useDiscounts,nround,useQ) ;                  // creates a data-object containing the tickets

noOffers=selectRows(z.offers,"$0 eq '' ",z.offers,2);                    // index to not offered tickets
gotOffers=selectRows(z.offers,"$0 ne '' ",z.offers,2);                   // index to offered tickets  

ddg='<ul><li>Number of valid offers: '+gotOffers.length+' \n <li>Number of empty offers: '+noOffers.length+'</ul> \n ';

zoffers=z.extractDO(gotOffers)  ;                // make a data object for accepted tickets only

  zoffers.updateDO(2,'status','2Stat');                           // for entries that are in zoffers
  z.updateDO(zoffers,'status','addStat' );                                // update status array in z 
  nacceptDo=Math.min(naccept,zoffers.nentries);         // can not accept more than total number of offers
  glist=uniqueArray(zoffers.groups);
  z.addDOc([glist],'groupList');                // add a one element array containing an array (a hack)

  submitGroups=freqArray(zoffers.groups,glist,1);           // count of tickets offers in each group

  ofsort=indexSort(zoffers.scores) ;                      // sort on offer_scores
  zoffersA=zoffers.extractDOrule(zoffers.scores,'SMALL',nacceptDo)  ;        // extract nacceptDO accepted offers from submitted offers

  zoffersA.updateDO(4,'status','4stat') ;                      // flag all tickets in this "accepted" DO

/*  could use mtxMath function, but its a bit comlicated so .. */
    aearn=[];
   for (var im=0;im<zoffersA.nentries;im++) {
      aearn[im]=zoffersA.offers[im]-zoffersA.prodcosts[im];
      if (useFee==1) aearn[im]=aearn[im]-zoffersA.fees[im];
      if (useQ==1)   aearn[im]=aearn[im]-(zoffersA.qpoints[im]*qCost);
      zoffersA.earnings[im]=aearn[im].toFixed(2) ;
   }

  if (zoffersA.nentries>0)  z.updateDO(zoffersA,'earnings status ','d1');                 // update earnings and  variables in z (using earnings and status  info in zoffersA)


// some constants 
totalEarnAllAccept=sumArray(aearn);
totalCostAllAccept=sumArray(zoffersA.prodcosts);
totalCostAllReject=sumArray(zoffers.prodcosts)-totalCostAllAccept;
acceptGroups=freqArray(zoffersA.groups,glist,1);           // count of accepted offers in each group

/* --- save to resultsVar. Since need to save by ticket, so do one ticket at a time */
for (var im=0;im<z.nentries;im++) {
  aid=z.ids[im];                                     // client specific resutls (for non-empty offers)
  itick=z.ticknums[im];
  saveResultsVar(aid,'earn['+itick+']' ,z.earnings[im]);
  saveResultsVar(aid,'status['+itick+']',z.status[im]);
}

/* --- aggregate earnings across tickets, and save to  RoundEarn variable */
for (var im=0;im<clientIDs.length;im++) {
    aid=clientIDs[im];                                     // client specific resutls (for non-empty offers)
    ztmp=zoffersA.extractDOrule(zoffersA.ids,'EQUAL',aid); 
    tmp=sumArray(ztmp.earnings);  
    saveResultsVar(aid,'RoundEarn',tmp);
}
 
   
// more  general results for this round (to save to resultsVar

saveResultsVar('*','nTickets',ntickets);
saveResultsVar('*','isDemo',isDemo);

if (nacceptDo>0) {        
   var maxAccept=zoffers.scores[ofsort[nacceptDo-1]] ;
   saveResultsVar('*','maxAccept',fix2(maxAccept));
} else {
    saveResultsVar('*','maxAccept','n.a.');
}
if (nacceptDo>-1) { 
    var minReject=zoffers.scores[ofsort[nacceptDo]] ;
    saveResultsVar('*','minReject',fix2(minReject));
} else {
    saveResultsVar('*','minReject','n.a.');
}
saveResultsVar('*','nSubmits',zoffers.nentries);
saveResultsVar('*','nAccept',nacceptDo);

saveResultsVar('*','totalEarnAllAccept',fix2(totalEarnAllAccept));
saveResultsVar('*','totalCostAllAccept',fix2(totalCostAllAccept));
saveResultsVar('*','totalCostAllReject',fix2(totalCostAllReject));
//alert('acceptgroups '+acceptGroups);
saveResultsVar('*','nAcceptsGroup[*]',acceptGroups);
saveResultsVar('*','nSubmitsGroup[*]',submitGroups);

// and some stuff to display
var sorder=indexSort2(z.offers,z.status,'ND');
var zz=[];
for (var m=0;m<sorder.length;m++) zz=zz.concat(sorder[m]);

tt=makeResultsTable(z,0,useDiscounts,zz,useQ);

controlVars['FUNCTION_SUCCESS']=1 ;              // set success flag


return ddg+'<p/>'+tt ;                  // done!

}


//===========================================
//  Compute results: shares

function doShares(clientIDs,useDiscounts,useQ) {  

var z={};
var noOffers,gotOffers,ddg;
var zoffers,glist,nAcceptDo ;
var m,i,tmp,aearn ;
var zgroup,zgroupA1,zoffers2,zoffers,ztmp,glist2,glist,zoffers3,glist3,maxAccept,minReject,maxAcceptGroup,minRejectGroup;
var totalEarnAllAccept,totalCostAllAccept,totalCostAllReject,acceptGroups=[],acceptGroups1=[],sorder,tt,zz,submitGroups=[] ;

z=readTickets(clientIDs,useDiscounts,nround,useQ) ;                  // creates a data-object containing the tickets

noOffers=selectRows(z.offers,"$0 eq '' ",z.offers,2);                    // index to not offered tickets
gotOffers=selectRows(z.offers,"$0 ne '' ",z.offers,2);                   // index to offered tickets  

glist=uniqueArray(z.groups) ;                     // all groups that have at least one   ticket 

ddg='<ul><li>Number of valid offers: '+gotOffers.length+' \n <li>Number of empty offers: '+noOffers.length+'</ul> \n ';

zoffers=z.extractDO(gotOffers)  ;                // make a data object for accepted tickets only
//alert('asd366fa ' +zoffers.nentries);
zoffers.updateDO(2,'status','2statb') ;                           // default is "reject first round" (for entries submitted offers)
if (zoffers.nentries>0) z.updateDO(zoffers,'status','do2statb') ;                            // update status array in z 

saveResultsVar('*','nSubmits',zoffers.nentries);
naccept=getVarInfo('naccept.g','value');
nacceptDo=Math.min(naccept,zoffers.nentries);         // can not accept more than total number of offers
saveResultsVar('*','nAccept',nacceptDo);

shareList=getVarInfo('shares.g','value');               // number of shares accepted per group (nb: group 0 is never used)

for (var m=0;m<glist.length;m++) {
   submitGroups[glist[m]]=0 ; acceptGroups[glist[m]]=0 ; acceptGroups1[glist[m]]=0 ;
}

ddg='<ul><li>Number of valid offers: '+gotOffers.length+'\n <li>Number of empty offers: '+noOffers.length+'</ul> \n ';

// break into groups
zgroup=[];
for (var i=0;i<glist.length;i++) {
  zgroup[glist[i]]=zoffers.extractDOrule(zoffers.groups,'EQUAL',glist[i]);    
  submitGroups[glist[i]]=zgroup[glist[i]].nentries ;
}

// retain only best sharelist[i] of group i
zgroupA1=[];
for (var i=0;i<glist.length;i++) {
//  if (zgroup[glist[i]].nentries==0) continue ;
  zgroupA1[glist[i]]=zgroup[glist[i]].extractDOrule(zgroup[glist[i]].scores,'SMALL',shareList[glist[i]]);
}

// rejoin after first stage removals
zoffers2=createDO(0);
for (var i=0;i<glist.length;i++) {
  if (zgroupA1[glist[i]].nentries==0) continue ;
  zoffers2=zoffers2.combineDO(zgroupA1[glist[i]]) ;
}

acceptGroups1=freqArray(zoffers2.groups,glist,1) ;          // # survivors of first round, per group

zoffers2.updateDO(3,'status','3stat') ;                           // for entries that are in zoffers2
z.updateDO(zoffers2,'status','do3stat') ;                           // update status array in z 

zoffers3=zoffers2.extractDOrule(zoffers2.scores,'SMALL',nacceptDo);      // the accepted offers

acceptGroups=freqArray(zoffers3.groups,glist,1) ;             // accepted offers, per group

zoffers3.updateDO(4,'status','4stat') ;                           // accepted 



/*  could use mtxMath function, but its a bit comlicated so .. */
 aearn=[];
 for (var im=0;im<zoffers3.nentries;im++) {
      aearn[im]=zoffers3.offers[im]-zoffers3.prodcosts[im];
      if (useFee==1) aearn[im]=aearn[im]-zoffers3.fees[im];
      if (useQ==1)   aearn[im]=aearn[im]-(zoffers3.qpoints[im]*qCost);
      zoffers3.earnings[im]=aearn[im] ;
  }


if (zoffers3.nentries>0) z.updateDO(zoffers3,'earnings status ','d2');                 // update earnings and  variables in z (using earnings and status  info in zoffersA)

// ----  Z now has all informon accepted. So save ticket info to resultsVar. After that, create some general stats and save them
saveResultsVar()  ;              // clear resultsData
saveResultsVar('','ROUND',nround)  ;    // set ROUND in resultsData

/*  Since need to save by ticket, so do one ticket at a time */
for (var im=0;im<z.nentries;im++) {
  aid=z.ids[im];                                     // client specific resutls (for non-empty offers)
  itick=z.ticknums[im];
  saveResultsVar(aid,'earn['+itick+']' ,z.earnings[im]);
  saveResultsVar(aid,'status['+itick+']',z.status[im]);
}

/* --- aggregate earnings across tickets, and save to  RoundEarn variable */

for (var im=0;im<clientIDs.length;im++) {
    aid=clientIDs[im];                                     // client specific resutls (for non-empty offers)
    ztmp=zoffers3.extractDOrule(zoffers3.ids,'EQUAL',aid);    
    tmp=0;
    if (ztmp.nentries>0) tmp=sumArray(ztmp.earnings);    

    saveResultsVar(aid,'RoundEarn',tmp.toFixed(2));
}
 

// .... general stats

maxAccept=maxArray(zoffers3.scores);                    // the max-accepted score (across all groups)

// not max accept per group
maxAcceptGroup=[];                                      // max-accepted score in each group
for (var i=0;i<glist.length;i++) {
  ztmp=zoffers3.extractDOrule(zoffers3.groups,'EQUAL',glist[i],'scores');
  maxAcceptGroup[glist[i]]=maxArray(ztmp.scores) ;
}
minReject=[];                                           // smallest rejected score (across all groups)
ztmp=zoffers2.extractDOrule(zoffers.scores,'GT',maxAccept,'scores groups');       // the scores greater than maxAccept (hence rejected)
minReject=minArray(ztmp.scores);                                // the smalles of these rejected scores

minRejectGroup=[];                                 // smallest rejected score per group
for (var i=0;i<glist.length;i++) {
   ztmp=zoffers2.extractDOrule(zoffers.groups,'EQ',glist[i]);
   minRejectGroup[glist[i]]=minArray(ztmp.scores);
}

// compute some totals 

// some constants 
totalEarnAllAccept=sumArray(zoffers3.earnings);
totalCostAllAccept=sumArray(zoffers3.prodcosts);
totalCostAllReject=sumArray(zoffers.prodcosts)-totalCostAllAccept;              // rejected in either step

saveResultsVar('*','maxAccept',fix2(maxAccept));

saveResultsVar('*','maxAcceptGroup[*]',maxAcceptGroup);
saveResultsVar('*','minRejectGroup[*]',minRejectGroup);
   
saveResultsVar('*','minReject',fix2(minReject));
saveResultsVar('*','nTickets',ntickets);
saveResultsVar('*','isDemo',isDemo);

saveResultsVar('*','totalEarnAllAccept',fix2(totalEarnAllAccept));
saveResultsVar('*','totalCostAllAccept',fix2(totalCostAllAccept));
saveResultsVar('*','totalCostAllReject',fix2(totalCostAllReject));
 
saveResultsVar('*','nAcceptsGroup[*]',acceptGroups);
saveResultsVar('*','nSubmitsGroup[*]',submitGroups);


// and some stuff to display
var sorder=indexSort2(z.status,z.groups,'ND');
var zz=[];
for (var m=0;m<sorder.length;m++) zz=zz.concat(sorder[m]);
tt=makeResultsTable(z,0,useDiscounts,zz,useQ);

controlVars['FUNCTION_SUCCESS']=1 ;              // set success flag
return ddg+'<p>'+tt;

}
 

//=======================================================
// extract basic info from submitData for this round, save in the z object
// Several arrays containing TICKET information are extracted. 
// Note: total number of entries is ntickets*nclients
// globals used: ntickets
// z contains ...
//    arrays saved (arrayList)= ids, offers, gorups, prodcosts, maxbids, fees, times, ticknums, scores, noOffers, gotOffers, earnings, status
//    and nclients arrays: roundEarn
//    constants saved (constList)= warnings, round, nentries, nclients 

function readTickets(clientIDs,useDiscounts,nround,useQ) {

var i,aid,im,zoffer,m,v1;
var oname,gname,pname,mname,fname,igroup,tname,namelist,igroup,itick;
var nentries,scores ;
var bigv=[]; 
var z ;
warnings='';
discountsList=[1,1,1,1,1,1,1];
if (useDiscounts==1) discountsList=getVarInfo('discounts.g','value');   


// have to do some goofy stuff to break out multiple tickets from each submission (each ticket is considered seperately, but not submitted seperately)
for (var m=0;m<ntickets;m++) {
    im=(1*m)+1 ;
    oname='OFFERS_USED['+im+']' ; gname='MYGROUP['+im+']'; pname='PRODCOST_USED['+im+']' ; mname='MAXBID_USED['+im+']' ; fname='FEE_USED['+im+']';
    qname='QPOINTS_USED['+im+']' ;
    tname='='+im ;
//              0    1         2          3        4           5        6       7         8
    namelist='$ID '+oname+' '+gname+' '+pname+' '+mname+' '+fname+' '+qname+' $TIME '+tname  ;
    v1=extractRoundData('S',nround,namelist,'',clientIDs);
    bigv=appendRows(bigv,v1);
}

var nentries=bigv.length ;
z=createDO(nentries);

z.addDOc([nround,ntickets,clientIDs.length],'round tickets nclients');
z.addDO(bigv,'ids offers groups prodcosts maxbids fees qpoints times ticknums');

scores=Concat_Arrays(z.offers) ;    
if (useDiscounts!==0 || useQ==1) {                                  // discount offers when ranking?
   for (var m=0;m<nentries;m++) {
        igroup=z.groups[m];
        if (typeof(discountsList[igroup])=='undefined') {    
           warnings+='Group number '+igroup+' does NOT have an entry in the DISCOUNTS array: '+discountsList+ '\n' ;
        }
        adisc=strip(discountsList[igroup]) ;
        if (adisc==='' || isNaN(adisc)) {
            warnings+= 'Bad discount for group '+igroup+'= '+adisc+' \n';
        }
        scores[m]=fix2( (scores[m]*adisc)-z.qpoints[m]);
    }
}
z.addDO(scores,'scores');
for (var m=0;m<nentries;m++) {                                  // double check offers against maxbid
  if (1*z.offers[m]>1*z.maxbids[m]) {
       warnings+='For id '+z.ids[m]+' the offer ('+z.offers[m]+') is > maxBid '+z.maxbids[m]+') \n ';
  }
}

// these are what are computed (per client and/or per ticket)
z.addDO(0,'earnings');                      // assume earnings of zero for each entry
z.addDO(1,'status');                      // assume earnings of zero for each entry

// save some defaults to resultsvar
for (var i=0;i<nentries;i++){ 
    aid=z.ids[i];
    itick=z.ticknums[i]
    saveResultsVar(aid,'earn['+itick+']',z.earnings[i]);                  // default earnings of 0 for this ticket
    saveResultsVar(aid,'status['+itick+']',z.status[i]);                 // default is this ticket not submitted
}  

z.addDOc(warnings,'warnings',1);
z.addDOc([clientIDs],'clientIDs',1);

return z ;
 
}



//==========================
// create a table of basic results -- z is an object containing results
function makeResultsTable(z,showtime,showscore,sortOrder,useQ) {
  
if (arguments.length<2) showtime=0;
if (arguments.length<3) showscore=0;
if (arguments.length<4) {
   sortOrder=[];
} else {
  if (sortOrder.length!=z.ids.length) {
        alert('Error displaying table: mismatch length in sortOrder: '+z.ids.length+','+sortOrder.length);
        return 1 ;
  }
}

var tt,m,m0;
var oo='Offer';
if (showscore!=0 || useQ==1 ) oo='Offer <span style="font-size:90%">score</span> '
if (showtime==0) {
  tt='<table border="1"><tr><th>ID.....</th><th>Earnings / Status</th><th>Group</th><th>'+oo+'</th><th>ProdCost</th><th>Fee</th><th>MaxBid</th></tr> \n ';
} else {
  tt='<table border="1"><tr><th>ID.....</th><th>Earnings / Status</th><th>Group</th><th>Offer</th><th>ProdCost</th><th>Fee</th><th>MaxBid</th><th>Time</th></tr> \n ';
}

var totearnings=0,totprodcost=0,totoffers=0 ;
for (var m0=0;m0<z.offers.length;m0++){   
  m=m0;
  if (sortOrder.length>0) m=sortOrder[m];       
  if (z.status[m]==4) {
     tt=tt+'<tr bgcolor="lime">';               // ACCEPTED
  }else if (z.status[m]==3) {
     tt=tt+'<tr bgcolor="yellow">';             // REJECT 2nd stage
  }else if (z.status[m]==1) {
     tt=tt+'<tr bgcolor="#dadede">';            // NOT OFFERS
  }else {
     tt=tt+'<tr>';                              // REJECT FIRST stage, or reject if only one stage
  }
  tt=tt+'<td>' +z.ids[m]+'</td> ';
  tt=tt+'<td>'+fix2(z.earnings[m],0)+' / <tt>'+ z.status[m] +'</tt></td> ';  
  if (z.status[m]==4) {
         totoffers=totoffers+(1*z.offers[m]) ;
         totearnings=totearnings+(1*z.earnings[m]);
         totprodcost=totprodcost +(1*z.prodcosts[m]) ;
   }

  tt=tt+'<td>'+z.groups[m]+'</td> ';
  if (showscore!==0) {
      tt=tt+'<td>'+z.offers[m]+'<span style="text-align:right;padding-left:5px;font-size:90%">'+z.scores[m]+'</span></td> ';
  } else {
      tt=tt+'<td>'+z.offers[m]+'</td> ';
  } 
  tt=tt+'<td>'+z.prodcosts[m]+'</td> ';
  tt=tt+'<td>'+z.fees[m]+'</td> ';
  tt=tt+'<td>'+z.maxbids[m]+'</td> ';  
  if (showtime==1)  {
        goo=new Date(); goo.setTime(z.times[m]);
        tt=tt+'<td>'+z.time[m].toLocaleString()+'</td> ';   
   }
  tt=tt+'</tr> \n ';
}
tt=tt+'</table> \n ';

tt=tt+'<p/><b>Total offers for accepted tickets:</b> '+fix2(totoffers);
tt=tt+'<ul><li>Earnings  <tt>'+fix2(totearnings)+'</tt> \n' ;
tt=tt+'<li>Costs= <tt>'+fix2(totprodcost)+'</tt> \n' ;
tt=tt+'</ul>'

return tt ;


}



