/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2011 Melin Software HB
    
    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 3 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, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Stigbergsvägen 7, SE-75242 UPPSALA, Sweden
    
************************************************************************/

#include "stdafx.h"

#include "resource.h"

#include <commctrl.h>
#include <commdlg.h> 

#include "oEvent.h"
#include "xmlparser.h"
#include "gdioutput.h"
#include "csvparser.h"
#include "SportIdent.h"
#include "meos_util.h"
#include "TabRunner.h"
#include "TabTeam.h"
#include "Table.h"
#include <cassert>
#include "oListInfo.h"
#include "TabSI.h"

TabRunner::TabRunner(oEvent *poe):TabBase(poe)
{
  lastRace=0;
  currentMode = 0;
  runnerId=0;
}

TabRunner::~TabRunner(void)
{
}

void disablePunchCourse(gdioutput &gdi);

bool TabRunner::loadPage(gdioutput &gdi, int runnerId)
{
  oe->checkDB();
  assert(this);
  loadPage(gdi);
  pRunner r=oe->getRunner(runnerId, 0);
  if(r){
	  selectRunner(gdi, r);
	  return true;
  }
  return false;
}

void TabRunner::enableControlButtons(gdioutput &gdi, bool enable, bool vacant)
{
  if (enable) {
    gdi.enableInput("Remove");
  	gdi.enableInput("Save");
    gdi.enableInput("Undo");
    if (vacant) {
      gdi.disableInput("Move");
  	  gdi.disableInput("NoStart");
    }
    else {
      gdi.enableInput("Move");
  	  gdi.enableInput("NoStart");
    }
  }
  else {
    gdi.disableInput("Remove");
  	gdi.disableInput("Save");
    gdi.disableInput("Undo");
    gdi.disableInput("Move");
	  gdi.disableInput("NoStart");
  }
}



void TabRunner::selectRunner(gdioutput &gdi, pRunner r)
{
  if(!r) {
    runnerId=0;
	  gdi.setText("Name", "");
    gdi.selectItemByData("RCourse", 0);

    //Don't clear club and class

    gdi.setText("CardNo", "");
		gdi.setText("Start", "-");
		gdi.setText("Finish", "-");

		gdi.setText("Time", "-");
		gdi.setText("Points", "");
    gdi.selectItemByData("Status", 0);

		gdi.clearList("Punches");
    gdi.clearList("Course");
	
		gdi.selectItemByData("Runners", -1);
		gdi.setInputFocus("Name", true);
    gdi.clearList("MultiR");
    gdi.disableInput("MultiR");
    gdi.enableEditControls(false);
    enableControlButtons(gdi, false, false);
    disablePunchCourse(gdi);

    gdi.disableInput("EditTeam");
    gdi.setText("RunnerInfo", "", true);
    return;
  } 

  gdi.enableEditControls(true);
 	disablePunchCourse(gdi);

  pRunner parent=r->getMultiRunner(0);

	r->synchronizeAll();
	//r->apply(false);
  vector<int> mp;
  r->evaluateCard(mp, 0, false);
  /*
  if(parent!=r) {
	  parent->synchronize();
	  parent->apply(false);
  }*/

  gdi.selectItemByData("Runners", parent->getId());

  runnerId=r->getId();

	gdi.setText("Name", r->getName());
	gdi.setText("Club", r->getClub());

	oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone);
	gdi.addItem("RClass", lang.tl("Ny klass"), 0);
	gdi.selectItemByData("RClass", r->getClassId());
#ifdef _DEBUG
  vector<int> delta;
  vector<int> place;
  vector<int> after;
  vector<int> placeAcc;
  vector<int> afterAcc;

  r->getSplitAnalysis(delta);
  r->getLegTimeAfter(after);
  r->getLegPlaces(place);
  
  r->getLegTimeAfterAcc(afterAcc);
  r->getLegPlacesAcc(placeAcc);
  
  gdi.setInputStatus("EditTeam", r->getTeam()!=0);
  
  if (r->getTeam()) {
    gdi.setText("Team", r->getTeam()->getName());
  }
  else
    gdi.setText("Team", "");

  string out;
  for (size_t k = 0; k<delta.size(); k++) {
    out += itos(place[k]);
    if (k<placeAcc.size())
      out += " (" + itos(placeAcc[k]) + ")";
  
    if (after[k]>0)
      out+= " +" + getTimeMS(after[k]);

    if (k<afterAcc.size() && afterAcc[k]>0)
      out+= " (+" + getTimeMS(afterAcc[k]) + ")";

    if (delta[k]>0)
      out+= " B: " + getTimeMS(delta[k]);
    
    out += " | ";

  }
  gdi.restore("fantom", false);
  gdi.setRestorePoint("fantom");
  gdi.addStringUT(0, out);
  gdi.refresh();    
#endif

  int numMulti=parent->getNumMulti();
  if(numMulti==0) {
    gdi.clearList("MultiR");
    gdi.disableInput("MultiR");
    lastRace=0;
  }
  else {
    char bf[32];
    gdi.clearList("MultiR");
    gdi.enableInput("MultiR");

    for (int k=0;k<numMulti+1;k++) {
      sprintf_s(bf, lang.tl("Lopp %d").c_str(), k+1);
      gdi.addItem("MultiR", bf, k);
    }
    gdi.selectItemByData("MultiR", r->getRaceNo());
  }

	oe->fillCourses(gdi, "RCourse", true);
	gdi.addItem("RCourse", lang.tl("[Klassens bana]"), 0);
	gdi.selectItemByData("RCourse", r->getCourseId());

	gdi.setText("CardNo", parent->getCardNo());
  gdi.check("RentCard", parent->getDI().getInt("CardFee")>0);
  gdi.setText("Fee", parent->getDI().getInt("Fee"));	

  if(parent!=r) {
    gdi.disableInput("CardNo");
    gdi.disableInput("RentCard");
    gdi.disableInput("Fee");
    gdi.disableInput("RClass");
  }
  else {
    gdi.enableInput("CardNo");
    gdi.enableInput("RentCard");
    gdi.enableInput("Fee");
    gdi.enableInput("RClass");
  }
  
  enableControlButtons(gdi, true, r->isVacant());
  
  gdi.setText("Start", r->getStartTimeS());
	gdi.setText("Finish", r->getFinishTimeS());

	gdi.setText("Time", r->getRunningTimeS());
	gdi.setText("Points", itos(r->getRogainingPoints()));

	//gdi.setText("Status", r->GetStatusS());
	gdi.selectItemByData("Status", r->getStatus());
  gdi.setText("RunnerInfo", lang.tl(r->getProblemDescription()), true);
    
	pCard pc=r->getCard();

	pCourse pcourse=r->getCourse();

	if(pc)	{
		gdi.setTabStops("Punches", 70);
		pc->fillPunches(gdi, "Punches", pcourse);
	}
	else gdi.clearList("Punches");

	if(pcourse)	{
    pcourse->synchronize();
		gdi.setTabStops("Course", 50);			
		pcourse->fillCourse(gdi, "Course");
	}
	else
		gdi.clearList("Course");

}

int RunnerCB(gdioutput *gdi, int type, void *data)
{
  TabRunner &tc = dynamic_cast<TabRunner &>(*gdi->getTabs().get(TRunnerTab));

  return tc.runnerCB(*gdi, type, data);
}

int PunchesCB(gdioutput *gdi, int type, void *data)
{
  TabRunner &tc = dynamic_cast<TabRunner &>(*gdi->getTabs().get(TRunnerTab));
  return tc.punchesCB(*gdi, type, data);
}

int VacancyCB(gdioutput *gdi, int type, void *data)
{
  TabRunner &tc = dynamic_cast<TabRunner &>(*gdi->getTabs().get(TRunnerTab));

  return tc.vacancyCB(*gdi, type, data);
}


pRunner TabRunner::save(gdioutput &gdi, int runnerId)
{
  bool create=(runnerId==0);

  if (create)
    return 0;

	string name=gdi.getText("Name");

	if(name.empty()) 
    throw std::exception("Alla deltagare måste ha ett namn.");

  int cardNo = gdi.getTextNo("CardNo");

	ListBoxInfo lbi;
  gdi.getSelectedItem("Club", &lbi);
	
  int clubId = 0;
  if (!lbi.text.empty()) {
	  pClub pc = oe->getClub(lbi.text);
    if(!pc)
      pc=oe->addClub(lbi.text);
	  pc->synchronize();

    clubId = pc->getId();
  }

  gdi.getSelectedItem("RClass", &lbi);

  int classId = 0;
  if (signed(lbi.data)<=0) {
    if (gdi.ask("Vill du skapa en ny klass?")) {
	    pClass pc=oe->addClass(oe->getAutoClassName());
      pc->synchronize();
      classId = pc->getId();
    }
  }
  else 
    classId = lbi.data;

  int year = 0;
	pRunner r;
	if(runnerId==0) 
		r=oe->addRunner(name, clubId, classId, cardNo, year, true);
  else { 
    r=oe->getRunner(runnerId, 0);
    if (r->getName() != name || r->getClubId() != clubId)
      r->updateFromDB(name, clubId, classId, cardNo, r->getBirthYear());    
  }

	if (r) {
    runnerId=r->getId();

	  r->setName(name);				
	  r->setCardNo(cardNo, true);
    if(gdi.isChecked("RentCard"))
      r->getDI().setInt("CardFee", oe->getDI().getInt("CardFee"));
    else
      r->getDI().setInt("CardFee", 0);
    
    if (oe->useEconomy())
      r->getDI().setInt("Fee", gdi.getTextNo("Fee"));
    
	  r->setStartTimeS(gdi.getText("Start"));
	  r->setFinishTimeS(gdi.getText("Finish"));


    r->setClubId(clubId);

    oe->fillClubs(gdi, "Club");
    gdi.setText("Club", r->getClub());

    r->setClassId(classId);
    
    gdi.getSelectedItem("RCourse", &lbi);
	  r->setCourseId(lbi.data);

	  gdi.getSelectedItem("Status", &lbi);

	  r->setStatus((RunnerStatus)lbi.data);
  
	  vector<int> mp;
	  r->evaluateCard(mp, 0, true);

	  r->synchronizeAll();
  }
  else
    runnerId=0;

  oe->fillRunners(gdi, "Runners");

	if (create) 
    selectRunner(gdi, 0);
  else 
    selectRunner(gdi, r);

  return r;
}

int TabRunner::runnerCB(gdioutput &gdi, int type, void *data)
{
	if(type==GUI_BUTTON){
		ButtonInfo bi=*(ButtonInfo *)data;

    if (bi.id=="Search") {
      ListBoxInfo lbi;
      gdi.getSelectedItem("Runners", &lbi);
      bool formMode = currentMode == 0;
      oe->fillRunners(gdi, "Runners", !formMode, !formMode);
      pRunner r=oe->findRunner(gdi.getText("SearchText"), lbi.data);

      if(r) {
        if (formMode)
          selectRunner(gdi, r);
        gdi.selectItemByData("Runners", r->getId());
      }
      else
        gdi.alert("Löparen hittades inte");
    }
    else if(bi.id=="Pair") {
      ListBoxInfo lbi;
      pRunner r=0;
      if (gdi.getSelectedItem("Runners", &lbi) &&
                      (r=oe->getRunner(lbi.data, 0))!=0) {
        int newCardId=static_cast<pCard>(bi.getExtra())->getId();
        int oldCardId=r->setCard(newCardId);
        gdi.restore("CardTable");
        Table &t=gdi.getTable();
        t.reloadRow(newCardId);
        if(oldCardId)
          t.reloadRow(oldCardId);
        gdi.enableTables();
        gdi.refresh();
      }      
    }
    else if(bi.id=="Unpair") {
      ListBoxInfo lbi;
      pCard c=static_cast<pCard>(bi.getExtra());

      if(c->getOwner())
        c->getOwner()->setCard(0);

      gdi.restore("CardTable");
      Table &t=gdi.getTable();
      t.reloadRow(c->getId());
      gdi.enableTables();
      gdi.refresh();
    }
    else if (bi.id=="TableMode") {
      if (currentMode == 0 && runnerId>0)
        save(gdi, runnerId);

      currentMode = 1;
      loadPage(gdi);
    }
    else if (bi.id=="FormMode") {
      if (currentMode != 0) {
        currentMode = 0;
        gdi.enableTables();
        loadPage(gdi);
      }
    }
    else if (bi.id=="Vacancy") {
      if (currentMode==0 && runnerId>0)
        save(gdi, runnerId);

      if (currentMode != 4) 
        showVacancyList(gdi, "add");
    }
    else if (bi.id=="Cards") {
      if (currentMode == 0 && runnerId>0)
        save(gdi, runnerId);

      if (currentMode != 3) 
        showCardsList(gdi);
    }
    else if (bi.id=="InForest") {
      if (currentMode==0 && runnerId>0)
        save(gdi, runnerId);

      if (currentMode != 2) 
        showInForestList(gdi);
    }
    else if (bi.id=="CancelInForest") {
      clearInForestData();
      loadPage(gdi);
    }
    else if (bi.id=="CancelReturn") {
      loadPage(gdi);
    }
    else if (bi.id=="SetDNS") {
      for (size_t k=0; k<unknown.size(); k++)
        if (unknown[k]->getStatus()==StatusUnknown) 
          unknown[k]->setStatus(StatusDNS);

      //Reevaluate and synchronize all
      oe->reEvaluateAll(true);
      clearInForestData();
      showInForestList(gdi);
    }
    else if (bi.id=="SetUnknown") {
      for (size_t k=0; k<known_dns.size(); k++)
        if (known_dns[k]->getStatus()==StatusDNS) 
          known_dns[k]->setStatus(StatusUnknown);

      //Reevaluate and synchronize all
      oe->reEvaluateAll(true);
      clearInForestData();
      showInForestList(gdi);
    }
    else if (bi.id=="Cancel") {
      gdi.restore("CardTable");
      gdi.enableTables();
      gdi.refresh();
    }
    else if (bi.id=="SplitPrint") {
			if(!runnerId)
				return 0;
			pRunner r=oe->getRunner(runnerId, 0);
			if(!r) return 0;

      gdioutput gdiprint(2.0, splitPrinter);
      r->printSplits(gdiprint);
      gdiprint.print(oe, 0, false, true);
      gdiprint.getPrinterSettings(splitPrinter);
    }
    else if (bi.id == "EditTeam") {
      pRunner r = oe->getRunner(runnerId, 0);
      if (r && r->getTeam()) {
        TabTeam *tt = (TabTeam *)gdi.getTabs().get(TTeamTab);
        tt->loadPage(gdi, r->getTeam()->getId());
      }
    }
    else if(bi.id=="Save") {
		  save(gdi, runnerId);
     	return true;
		}
    else if(bi.id=="Undo") {
      selectRunner(gdi, oe->getRunner(runnerId, 0));
     	return true;
		}
		else if(bi.id=="Add") {
      if (runnerId>0)
        save(gdi, runnerId);

      ListBoxInfo lbi;
      gdi.getSelectedItem("RClass", &lbi);

      pRunner r = oe->addRunner(oe->getAutoRunnerName(), 0,0,0,0, false);
      if (signed(lbi.data)>0)
        r->setClassId(lbi.data);
      else  
        r->setClassId(oe->getFirstClassId(false));

      oe->fillRunners(gdi, "Runners");
      selectRunner(gdi, r);
      gdi.setInputFocus("Name", true);
		}
		else if(bi.id=="Remove") {
			if(!runnerId)
				return 0;
		
			if(gdi.ask("Vill du verkligen ta bort löparen?")) {
				if(oe->isRunnerUsed(runnerId))
					gdi.alert("Löparen ingår i ett lag och kan inte tas bort.");
				else {
					oe->removeRunner(runnerId);
					oe->fillRunners(gdi, "Runners");
          selectRunner(gdi, 0);
				}
			}
		}
    else if (bi.id=="NoStart") {
      if(!runnerId)
				return 0;
		  pRunner r = oe->getRunner(runnerId, 0);
      r = r->getMultiRunner(0);

      if (r && gdi.ask("Bekräfta att deltagaren har lämnat återbud.")) {
        if (r->getStartTime()>0) {
          pRunner newRunner = oe->addRunnerVacant(r->getClassId());
          newRunner->cloneStartTime(r);
          newRunner->setStartNo(r->getStartNo());
          if (r->getCourseId())
            newRunner->setCourseId(r->getCourseId());
          newRunner->synchronizeAll();
        }
        
        for (int k=0;k<r->getNumMulti()+1; k++) {
          pRunner rr = r->getMultiRunner(k);
          if (rr) {
            rr->setStartTime(0);
            rr->setStartNo(0);
            rr->setStatus(StatusDNS);
            rr->setCardNo(0, false);
          }
        }
        r->synchronizeAll();
        selectRunner(gdi, r);
      }
    }
    else if (bi.id == "Move") {
      pRunner r = oe->getRunner(runnerId, 0);

      if(!runnerId || !r)
				return 0;
      gdi.clearPage(true);

      gdi.pushX();
      gdi.fillRight();
      gdi.addString("", boldLarge, "Klassbyte");
      gdi.addStringUT(boldLarge, r->getName()).setColor(colorDarkRed);
      
      //gdi.fillRight();
      //gdi.addString("", 0, "Deltagare:");
      gdi.fillDown();
      gdi.dropLine(2);
      gdi.popX();
      gdi.addString("", 0, "Välj en vakant plats nedan.");

      oe->generateVacancyList(gdi, RunnerCB);
  
      gdi.dropLine();
      gdi.addButton("CancelReturn", "Avbryt", RunnerCB);
    }
		else if (bi.id == "RemoveC")	{
			ListBoxInfo lbi;
			gdi.getSelectedItem("Punches", &lbi);

			DWORD rid=runnerId;
			if(!rid)
				return 0;

			pRunner r=oe->getRunner(rid, 0);

			if(!r) return 0;

			pCard card=r->getCard();

			if(!card) return 0;

			card->deletePunch(pPunch(lbi.data));
      card->synchronize();
			//Update runner
			vector<int> mp;
			r->evaluateCard(mp, 0, true);			

      card->fillPunches(gdi, "Punches", r->getCourse());

      gdi.setText("Time", r->getRunningTimeS());
			gdi.selectItemByData("Status", r->getStatus());
		}
		else if(bi.id=="Check")	{
		}
	}
	else if (type==GUI_LISTBOX) {
		ListBoxInfo bi=*(ListBoxInfo *)data;

		if (bi.id=="Runners") {
      if (gdi.isInputChanged(""))
        save(gdi, runnerId);

			pRunner r=oe->getRunner(bi.data, 0);

      if (r==0)
        throw std::exception("Internal error runner index");

      if(lastRace<=r->getNumMulti())
			  r=r->getMultiRunner(lastRace);

      selectRunner(gdi, r);
		}
		else if (bi.id=="RClass") {
			gdi.selectItemByData("RCourse", 0);

			if (bi.data==0) {
				gdi.clearList("Course");				
			}
			else {
				pClass Class=oe->getClass(bi.data);

				if(Class && Class->getCourse())
					Class->getCourse()->fillCourse(gdi, "Course");
        else
          gdi.clearList("Course");
			}
		}
		else if (bi.id=="RCourse") {
			if (bi.data==0) {
				gdi.clearList("Course");
        pRunner r=oe->getRunner(runnerId, 0);
        if(r) {  //Fix for multi classes, course depends on runner.
          ListBoxInfo lbi;
				  gdi.getSelectedItem("RClass", &lbi);
          if (signed(lbi.data)>0) {
            r->setClassId(lbi.data);
            r = oe->getRunner(runnerId, 0);
            if (r) {
              r->synchronize(true);
              if(r->getCourse())
                r->getCourse()->fillCourse(gdi, "Course");
            }
          }
        }
        if (!r) {
				  ListBoxInfo lbi;
				  gdi.getSelectedItem("RClass", &lbi);
				  pClass Class=oe->getClass(lbi.data);
				  if(Class && Class->getCourse())
					  Class->getCourse()->fillCourse(gdi, "Course");       
        }
			}
			else {
				pCourse course=oe->getCourse(bi.data);

				if(course)
					course->fillCourse(gdi, "Course");
			}
		}
    else if (bi.id=="MultiR") {
      pRunner r=oe->getRunner(runnerId, 0);
      lastRace=bi.data;
      if(r) 
        selectRunner(gdi, r->getMultiRunner(bi.data));
    }
	}
	else if (type==GUI_EVENT) {
		EventInfo ei=*(EventInfo *)data;

		if(ei.id=="LoadRunner") {
			pRunner r=oe->getRunner(ei.data, 0);
			if (r) {
        selectRunner(gdi, r);
				gdi.selectItemByData("Runners", r->getId());
			}
		}
    else if(ei.id=="CellAction") {
      oBase *b=static_cast<oBase *>(ei.getExtra());
      cellAction(gdi, ei.data, b);
    }
	}
  else if(type==GUI_CLEAR) {
    if (runnerId>0)
      save(gdi, runnerId);

    return true;
  }
  else if (type == GUI_LINK) {
    oBase *b = static_cast<oBase*>((static_cast<TextInfo*>(data))->getExtra());
    oRunner *vacancy = dynamic_cast<oRunner *>(b);
    
    if (vacancy==0 || vacancy->getClassId()==0)
      return -1;

    pRunner r = oe->getRunner(runnerId, 0);

    vacancy->synchronize();
    r->synchronize();

    
    if (r==0)
      return -1;

    char bf[1024];
    sprintf_s(bf, lang.tl("Bekräfta att %s byter klass till %s.").c_str(), 
                  r->getName().c_str(), vacancy->getClass().c_str());
    if (gdi.ask(string("#") + bf)) {
  
      vacancy->synchronize();
      if (!vacancy->isVacant())
        throw std::exception("Starttiden är upptagen.");

      oRunner temp(oe, 0);
      temp.setTemporary();
      temp.setStartNo(r->getStartNo());
      temp.setClassId(r->getClassId());
      temp.apply(false);
      temp.cloneStartTime(r);
      
      r->setClassId(vacancy->getClassId());
      // Remove or create multi runners
      r->createMultiRunner(true, true);
      r->apply(false);
      r->cloneStartTime(vacancy);
      r->setStartNo(vacancy->getStartNo());
      
      vacancy->setClassId(temp.getClassId());
      // Remove or create multi runners
      vacancy->createMultiRunner(true, true);
      vacancy->apply(false);
      vacancy->cloneStartTime(&temp);
      r->synchronizeAll();
      vacancy->synchronizeAll();
      
      loadPage(gdi);
      selectRunner(gdi, r);
    }
  }
	return 0;
}

void TabRunner::showCardsList(gdioutput &gdi)
{ 
  currentMode = 3;
  gdi.clearPage(false);
  gdi.dropLine(0.5);
  gdi.addString("", boldLarge, "Hantera löparbrickor");
  gdi.addString("", 10, "help:14343");
  addToolbar(gdi);
  cardModeStartY=gdi.getCY();      
  Table *t=oe->getCardsTB();
  gdi.addTable(t,gdi.getCX(),gdi.getCY()+15);
  gdi.registerEvent("CellAction", RunnerCB);
  gdi.refresh();
}

int TabRunner::vacancyCB(gdioutput &gdi, int type, void *data)
{	
  if (type == GUI_BUTTON) {
		ButtonInfo bi=*(ButtonInfo *)data;
	
		if (bi.id == "VacancyAdd") {
      showVacancyList(gdi, "add");
    }
    else if (bi.id == "Cancel")
      loadPage(gdi);
  }
  else if (type == GUI_LINK) {
    oBase *b = static_cast<oBase*>((static_cast<TextInfo*>(data))->getExtra());

    oRunner *r = dynamic_cast<oRunner *>(b);
    
    if (r==0)
      return -1;
    
    r->synchronize();
    if (!r->isVacant())
      throw std::exception("Starttiden är upptagen.");

    string name = gdi.getText("Name");
    
    if (name.empty())
      throw std::exception("Alla deltagare måste ha ett namn.");

    int cardNo = gdi.getTextNo("CardNo");

    if (cardNo!=r->getCardNo() && oe->checkCardUsed(gdi, cardNo))
      return 0;
  
    string club = gdi.getText("Club");
    int birthYear = 0;
    pClub pc = oe->getClubCreate(0, club);

    r->updateFromDB(name, pc->getId(), r->getClassId(), cardNo, birthYear);

    r->setName(name);
    r->setCardNo(cardNo, true);
    
    r->setClub(club);

    if (oe->useEconomy()) {
      int fee = gdi.getTextNo("Fee");
      r->getDI().setInt("Fee", fee);
    }

    if(gdi.isChecked("RentCard"))
      r->getDI().setInt("CardFee", oe->getDI().getInt("CardFee"));
    else
      r->getDI().setInt("CardFee", 0);
    
    r->synchronizeAll();
    showVacancyList(gdi, "", r->getClassId());
  }
  else if (type==GUI_INPUT) {
    InputInfo ii=*(InputInfo *)data;

    if (ii.id=="CardNo") {
      int cardNo = gdi.getTextNo("CardNo");
      if (gdi.getText("Name").empty())
        setCardNo(gdi, cardNo);
    }
  }
  else if (type==GUI_POSTCLEAR) {
    // Clear out SI-link
    TabSI &tsi = dynamic_cast<TabSI &>(*gdi.getTabs().get(TSITab));
    tsi.setCardNumberField("");  

    return true;
  }
  return 0;
}

void TabRunner::setCardNo(gdioutput &gdi, int cardNo) 
{
  pRunner db_r=oe->dbLookUpByCard(cardNo);

  if(db_r) {
    gdi.setText("Name", db_r->getName());
    gdi.setText("Club", db_r->getClub());
  }
}

void TabRunner::showVacancyList(gdioutput &gdi, const string &method, int classId) 
{
  gdi.clearPage(true);
  currentMode = 4;

  if (method == "") {
    gdi.dropLine(0.5);
    gdi.addString("", boldLarge, "Tillsatte vakans");
    addToolbar(gdi);

    gdi.dropLine();

    gdi.fillRight();
    gdi.pushX();
    gdi.addButton("VacancyAdd", "Tillsätt vakans", VacancyCB);
    //gdi.addButton("Cancel", "Återgå", VacancyCB);
    gdi.popX();
    gdi.fillDown();
    gdi.dropLine(2);

    if (classId==0)
      oe->generateVacancyList(gdi, 0);
    else {
      oListParam par;
      par.selection.insert(classId);
      oListInfo info;
      par.listCode = EStdStartList;      
      oe->generateListInfo(par, gdi.getLineHeight(), info);      
      oe->generateList(gdi, false, info);
    }
  }
  else if (method == "add") {
    gdi.dropLine(0.5);
    gdi.addString("", boldLarge, "Tillsätt vakans");
    addToolbar(gdi);

    gdi.dropLine();

    gdi.fillRight();
    gdi.pushX();

    gdi.addInput("CardNo", "", 8, VacancyCB, "Bricka:");
    
    TabSI &tsi = dynamic_cast<TabSI &>(*gdi.getTabs().get(TSITab));
    tsi.setCardNumberField("CardNo");
    //Remember to clear SI-link when page is cleared.
    gdi.setPostClearCb(VacancyCB);

    gdi.dropLine(1);
    gdi.addCheckbox("RentCard", "Hyrd", 0, false);
    gdi.dropLine(-1);
    
    gdi.addInput("Name", "", 16, 0, "Namn:");

    gdi.addCombo("Club", 220, 300, 0, "Klubb:");
	  oe->fillClubs(gdi, "Club");
    
    if (oe->useEconomy())
      gdi.addInput("Fee", "", 4, 0, "Avgift:");

    gdi.fillDown();
    gdi.popX();
    gdi.dropLine(3);
    //gdi.addButton("Cancel", "Avbryt", VacancyCB);

    gdi.addString("", boldText, "Välj klass och starttid nedan");
    oe->generateVacancyList(gdi, VacancyCB);
  }
  else if (method == "move") {

  }
} 

int SportIdentCB(gdioutput *gdi, int type, void *data);

void TabRunner::clearInForestData() 
{
  unknown_dns.clear();
  known_dns.clear();
  known.clear();
  unknown.clear();
}

void TabRunner::showInForestList(gdioutput &gdi)
{
  gdi.clearPage(true);
  currentMode = 2;
  gdi.dropLine(0.5);
  gdi.addString("", boldLarge, "Hantera kvar-i-skogen");
  addToolbar(gdi);
  gdi.dropLine();
  gdi.setRestorePoint("Help");
  gdi.addString("", 10, "help:425188");
  gdi.dropLine();
  gdi.pushX();
  gdi.fillRight();
  gdi.addButton("Import", "Importera stämplingar...", SportIdentCB).setExtra(LPVOID(1));
  gdi.addButton("SetDNS", "Sätt okända löpare utan registrering till <Ej Start>", RunnerCB);
  gdi.fillDown();
  gdi.addButton("SetUnknown", "Återställ löpare <Ej Start> med registrering till <Status Okänd>", RunnerCB);
  gdi.dropLine();
  gdi.popX();

  clearInForestData();
  oe->analyseDNS(unknown_dns, known_dns, known, unknown);

  if (!unknown.empty()) {
    gdi.dropLine();
    gdi.dropLine(0.5);
    gdi.addString("", 1, "Löpare, Status Okänd, som saknar registrering");
    listRunners(gdi, unknown, true);
  }
  else {
    gdi.disableInput("SetDNS");
  }

  if (!known.empty()) {
    gdi.dropLine();
    gdi.addString("", 1, "Löpare, Status Okänd, med registrering (kvar-i-skogen)");
    gdi.dropLine(0.5);
    listRunners(gdi, known, false);
  }

  if (!known_dns.empty()) {
    gdi.dropLine();
    gdi.addString("", 1, "Löpare, Ej Start, med registrering (kvar-i-skogen!?)");
    gdi.dropLine(0.5);
    listRunners(gdi, known_dns, false);
  }
  else
    gdi.disableInput("SetUnknown");

  if (known.empty() && unknown.empty() && known_dns.empty()) {
    gdi.addString("", 10, "inforestwarning");
  }
}

void TabRunner::listRunners(gdioutput &gdi, const vector<pRunner> &r, bool filterVacant) const
{
  char bf[64];
  int yp = gdi.getCY();
  int xp = gdi.getCX();

  for (size_t k=0; k<r.size(); k++) {
    if (filterVacant && r[k]->isVacant())
      continue;
    sprintf_s(bf, "%d.", k+1);
    gdi.addStringUT(yp, xp, 0, bf);
    gdi.addStringUT(yp, xp+40, 0, r[k]->getNameAndRace(), 190);
    gdi.addStringUT(yp, xp+200, 0, r[k]->getClass(), 140);
    gdi.addStringUT(yp, xp+350, 0, r[k]->getClub(), 190);
    sprintf_s(bf, "(%d)", r[k]->getCardNo());
    if (r[k]->getCardNo()>0)
      gdi.addStringUT(yp, xp+550, 0, bf, 240);
    yp += gdi.getLineHeight();
  }
}

void TabRunner::cellAction(gdioutput &gdi, DWORD id, oBase *obj)
{
  if (id==TID_RUNNER || id==TID_CARD) {
    gdi.disableTables();
    pCard c=dynamic_cast<pCard>(obj);
    if (c) {
      gdi.setRestorePoint("CardTable");
      int orgx=gdi.getCX();
      gdi.dropLine(1);
      gdi.setCY(cardModeStartY);
      gdi.scrollTo(orgx, cardModeStartY);
      gdi.addString("", 1, "Para ihop bricka X med en deltagare#" + c->getCardNo());
      gdi.addString("", 0, "Nuvarande innehavare: X.#" + (c->getOwner() ? c->getOwner()->getName().c_str() : MakeDash("-")));
      
      gdi.dropLine(1);
      gdi.pushX();
      gdi.fillRight();
      gdi.addListBox("Card", 150, 300, 0, "Vald bricka:");
      c->fillPunches(gdi, "Card", 0);
      gdi.disableInput("Card");

      gdi.pushX();
      gdi.fillRight();
      gdi.popX();

      gdi.fillDown();
      gdi.addListBox("Runners", 350, 300, 0, "Deltagare:");
      gdi.setTabStops("Runners", 200, 300);
      oe->fillRunners(gdi, "Runners", true, true);
      if(c->getOwner())
        gdi.selectItemByData("Runners", c->getOwner()->getId());

      gdi.popX();
      gdi.fillRight();
      gdi.addInput("SearchText", "", 15).setBgColor(colorLightCyan);
      gdi.addButton("Search", "Sök deltagare", RunnerCB, "Sök på namn, bricka eller startnummer.");
     
      gdi.popX();
      gdi.dropLine(3);
      gdi.addButton("Pair", "Para ihop", RunnerCB).setExtra(c);
      gdi.addButton("Unpair", "Sätt som oparad", RunnerCB).setExtra(c);
      gdi.addButton("Cancel", "Avbryt", RunnerCB);
      gdi.fillDown();
      gdi.popX();
      gdi.refresh();
    }
  }
}

void disablePunchCourseAdd(gdioutput &gdi)
{
  gdi.disableInput("AddC");
	gdi.disableInput("AddAllC");
  gdi.selectItemByData("Course", -1);
}

void disablePunchCourseChange(gdioutput &gdi)
{
	gdi.disableInput("SaveC");
	gdi.disableInput("RemoveC");
	gdi.disableInput("PTime");
	gdi.setText("PTime", "");  
  gdi.selectItemByData("Punches", -1);

}

void disablePunchCourse(gdioutput &gdi)
{
  disablePunchCourseAdd(gdi);
  disablePunchCourseChange(gdi);
}

void UpdateStatus(gdioutput &gdi, pRunner r)
{
	if(!r) return;

	gdi.setText("Start", r->getStartTimeS());
	gdi.setText("Finish", r->getFinishTimeS());
	gdi.setText("Time", r->getRunningTimeS());
	gdi.selectItemByData("Status", r->getStatus());
  gdi.setText("RunnerInfo", lang.tl(r->getProblemDescription()), true);
}

int TabRunner::punchesCB(gdioutput &gdi, int type, void *data)
{
	DWORD rid=runnerId;
	if(!rid)
		return 0;

	pRunner r=oe->getRunner(rid, 0);

	if(!r){
		gdi.alert("Deltagaren måste sparas innan stämplingar kan hanteras.");
		return 0;
	}

	
	if(type==GUI_LISTBOX){
		ListBoxInfo bi=*(ListBoxInfo *)data;

		if (bi.id=="Punches") {
			if (bi.data) {
        pPunch punch = pPunch(bi.data);
				pCard card=r->getCard();

				if(!card) return 0;
				
        string ptime=punch->getTime();//;card->getPunchTime(punch);

				if (ptime!="") {
					gdi.enableInput("SaveC");
					gdi.setText("PTime", ptime);
				}
				gdi.enableInput("RemoveC");
				gdi.enableInput("PTime");
			}
			else {
				gdi.disableInput("SaveC");
				gdi.disableInput("RemoveC");
				gdi.setText("PTime", "");
			}
      disablePunchCourseAdd(gdi);
		}
		else if (bi.id=="Course") {
			if (signed(bi.data)>=0) {
				pCourse pc=r->getCourse();
				
				if(!pc) return 0;

				gdi.enableInput("AddC");
				gdi.enableInput("AddAllC");
			}
			else{
				gdi.disableInput("AddC");
				gdi.disableInput("AddAllC");
			}
      disablePunchCourseChange(gdi);
		}
	}
	else if(type==GUI_BUTTON){
		ButtonInfo bi=*(ButtonInfo *)data;
		pCard card=r->getCard();

		if(!card){
			if(!gdi.ask("Ingen bricka har lästs in för denna löpare. Vill du lägga till stämplingar manuellt?"))
				return 0;
	
			card=oe->allocateCard(r);

			card->setCardNo(r->getCardNo());
			vector<int> mp;
			r->addPunches(card, mp);

		}
	
		if(bi.id=="AddC"){
			vector<int> mp;
			r->evaluateCard(mp);
			
			pCourse pc=r->getCourse();

			if(!pc) return 0;

			ListBoxInfo lbi;

			if(!gdi.getSelectedItem("Course", &lbi))
				return 0;
			
		  oControl *oc=pc->getControl(lbi.data);

			if(!oc) return 0;
			vector<int> nmp;

      if (oc->getStatus() == oControl::StatusRogaining) {
		    r->evaluateCard(nmp, oc->getFirstNumber()); //Add this punch					
      }
      else {
        for (size_t k = 0; k<mp.size(); k++) {
				  if(oc->hasNumber(mp[k])) 
					  r->evaluateCard(nmp, mp[k]); //Add this punch					
			  }
      }
			//synchronize SQL
			card->synchronize();
      r->synchronize(true);
      r->evaluateCard(mp);
			card->fillPunches(gdi, "Punches", pc);
			UpdateStatus(gdi, r);
		}
		else if(bi.id=="AddAllC"){
			vector<int> mp;
			r->evaluateCard(mp);
			vector<int>::iterator it=mp.begin();
	

			while(it!=mp.end()){
				vector<int> nmp;
        r->evaluateCard(nmp, *it); //Add this punch					
				++it;
        if(nmp.empty())
          break;
			}

			//synchronize SQL
			card->synchronize();
      r->synchronize(true);
      r->evaluateCard(mp);
			card->fillPunches(gdi, "Punches", r->getCourse());
			UpdateStatus(gdi, r);
		}
		else if(bi.id=="SaveC"){						
			//int time=oe->GetRelTime();

			ListBoxInfo lbi;

			if(!gdi.getSelectedItem("Punches", &lbi))
				return 0;
			
			pCard pc=r->getCard();
			
			if(!pc) return 0;
			
      pPunch pp = pPunch(lbi.data);

			pc->setPunchTime(pp, gdi.getText("PTime"));

			vector<int> mp;
			r->evaluateCard(mp);

			//synchronize SQL
			card->synchronize();
			r->synchronize();
      r->evaluateCard(mp);
      card->fillPunches(gdi, "Punches", r->getCourse());
			UpdateStatus(gdi, r);
      gdi.selectItemByData("Punches", size_t(pp));
		}
	}
	return 0;
}

bool TabRunner::loadPage(gdioutput &gdi)
{
  oe->reEvaluateAll(true);

  clearInForestData();
	gdi.selectTab(tabId);
	gdi.clearPage(false);
	int basex=gdi.getCX();

  if(currentMode == 1) {
    Table *tbl=oe->getRunnersTB();
    addToolbar(gdi);
    gdi.dropLine(1);
    gdi.addTable(tbl, basex, gdi.getCY());
    return true;
  }
  else if (currentMode == 2) {
    showInForestList(gdi);
    return true;
  }
  else if (currentMode == 3) {
    showCardsList(gdi);
    return true;
  }
  else if (currentMode == 4) {
    showVacancyList(gdi, "add");
    return true;
  }

  gdi.pushX();
  gdi.dropLine(0.5);
  gdi.addString("", boldLarge, "Deltagare");
  gdi.fillRight();
  gdi.addInput("SearchText", "", 13).isEdit(false).setBgColor(colorLightCyan).ignore(true);
  gdi.dropLine(-0.2);
  gdi.addButton("Search", "Sök", RunnerCB, "Sök på namn, bricka eller startnummer.");
  gdi.dropLine(2);
  gdi.popX();

	gdi.fillDown();
	gdi.addListBox("Runners", 212, 400, RunnerCB).isEdit(false).ignore(true);
	oe->fillRunners(gdi, "Runners");

	gdi.newColumn();
	gdi.fillDown();

  gdi.dropLine(1);
	gdi.addInput("Name", "", 16, 0, "Namn:");
	gdi.addCombo("Club", 220, 300, 0, "Klubb:");
	oe->fillClubs(gdi, "Club");
  gdi.pushX();

  if (oe->hasTeam()) {    
    gdi.fillRight();
    gdi.addInput("Team", "", 16, 0, "Lag:").isEdit(false);
    gdi.disableInput("Team");
    gdi.fillDown();
    gdi.dropLine(0.9);
    gdi.addButton("EditTeam", "...", RunnerCB, "Ändra lag");
    gdi.popX();
  }

  gdi.fillRight();
	gdi.addSelection("RClass", 150, 300, RunnerCB, "Klass:");
	oe->fillClasses(gdi, "RClass", oEvent::extraNone, oEvent::filterNone);	
	gdi.addItem("RClass", lang.tl("Ny klass"), 0);
	
  gdi.fillDown();
  
  if (oe->useEconomy())
    gdi.addInput("Fee", "", 6, 0, "Avgift:");
  else
    gdi.dropLine(3);

  gdi.dropLine(0.4);

  if (oe->hasMultiRunner()) {
    gdi.fillRight();
    gdi.popX();
    gdi.addString("", 0, "Välj lopp:");
    gdi.fillDown();
    gdi.dropLine(-0.2);
    gdi.addSelection("MultiR", 160, 100, RunnerCB);
  }

  gdi.popX();

	gdi.addSelection("RCourse", 220, 300, RunnerCB, "Bana:");
	oe->fillCourses(gdi, "RCourse", true);
	gdi.addItem("RCourse", lang.tl("[Klassens bana]"), 0);

  gdi.pushX();
  gdi.fillRight();
  gdi.addInput("CardNo", "", 8, 0, "Bricka:");
  gdi.dropLine(1);
  gdi.addCheckbox("RentCard", "Hyrd", 0, false);
	
  gdi.dropLine(2);
  gdi.popX();
	
	gdi.addInput("Start", "", 5, 0, "Starttid:");
	
	gdi.fillDown();
	gdi.addInput("Finish", "", 5, 0, "Måltid:");
	
	gdi.popX();
	
	gdi.pushX();
	gdi.fillRight();

  gdi.addInput("Time", "", 5, 0, "Löptid:").isEdit(false).ignore(true);
	gdi.disableInput("Time");

  if (oe->hasRogaining()) {
    gdi.addInput("Points", "", 5, 0, "Poäng:").isEdit(false).ignore(true);
	  gdi.disableInput("Points");
  }

	gdi.fillDown();
	gdi.addSelection("Status", 80, 160, 0, "Status:");
  oe->fillStatus(gdi, "Status");
	gdi.popX();
	gdi.selectItemByData("Status", 0);

  gdi.addString("RunnerInfo", 1, "").setColor(colorRed);

  gdi.dropLine(1.0);
  gdi.fillRight();
  gdi.addButton("Save", "Spara", RunnerCB, "help:save");	
  gdi.addButton("Undo", "Ångra", RunnerCB);	
  gdi.dropLine(2.5);
  gdi.popX();
  gdi.addButton("Remove", "Radera", RunnerCB);
  gdi.addButton("Add", "Ny deltagare", RunnerCB);
  gdi.popX();
  gdi.fillRight();
  gdi.dropLine(2.5);
  gdi.addButton("Move", "Klassbyte", RunnerCB);
  gdi.addButton("NoStart", "Återbud", RunnerCB);
  enableControlButtons(gdi, false, false);
  gdi.fillDown();

  gdi.newColumn();
  gdi.dropLine();
	gdi.addListBox("Punches", 140, 300, PunchesCB, "Stämplingar:").ignore(true);
	gdi.addButton("RemoveC", "Ta bort stämpling >>", RunnerCB);

	gdi.pushX();
	gdi.fillRight();
	gdi.addInput("PTime", "", 5, 0, "Tid:");
	gdi.fillDown();
	gdi.dropLine();
	gdi.addButton("SaveC", "Spara", PunchesCB);

	gdi.popX();
  gdi.addButton("SplitPrint", "Skriv ut sträcktider", RunnerCB).isEdit(true);
	gdi.pushY();

	gdi.newColumn();
  gdi.dropLine();
  gdi.fillDown();
	gdi.addListBox("Course", 140, 300, PunchesCB, "Banmall:").ignore(true);
	gdi.addButton("AddC", "<< Lägg till stämpling", PunchesCB);
	gdi.addButton("AddAllC", "<< Lägg till alla", PunchesCB);
	
  gdi.synchronizeListScroll("Punches", "Course");
	disablePunchCourse(gdi);
	gdi.popX();
	gdi.popY();

	gdi.fillDown();
  gdi.setCY(gdi.getHeight());
  gdi.setCX(gdi.scaleLength(100));
	
  gdi.addString("", 10, "help:41072");		

	gdi.registerEvent("LoadRunner", RunnerCB);
	
  gdi.setOnClearCb(RunnerCB);

  addToolbar(gdi);
  /*gdi.addButton(2+button_w, 2, button_w, "InForest", "Kvar-i-skogen", RunnerCB);
  gdi.addButton(2+2*button_w, 2 ,button_w, "Cards", "Hantera brickor", RunnerCB);
  gdi.addButton(2+3*button_w, 2 ,button_w, "Vacancy", "Vakanser", RunnerCB);
*/
  selectRunner(gdi, oe->getRunner(runnerId, 0));
  gdi.refresh();
  return true;
}

void TabRunner::addToolbar(gdioutput &gdi) {

  const int button_w=gdi.scaleLength(130);

  gdi.addButton(2+0*button_w, 2, button_w, "FormMode", 
    "Formulärläge", RunnerCB, "", false, true).fixedCorner();
  gdi.check("FormMode", currentMode==0);

  gdi.addButton(2+1*button_w, 2, button_w, "TableMode", 
            "Tabelläge", RunnerCB, "", false, true).fixedCorner();
  gdi.check("TableMode", currentMode==1);
  
  gdi.addButton(2+2*button_w, 2, button_w, "InForest", 
            "Kvar-i-skogen", RunnerCB, "", false, true).fixedCorner();
  gdi.check("InForest", currentMode==2);
  
  gdi.addButton(2+3*button_w, 2 ,button_w, "Cards", 
            "Hantera brickor", RunnerCB, "", false, true).fixedCorner();
  gdi.check("Cards", currentMode==3);
  
  gdi.addButton(2+4*button_w, 2 ,button_w, "Vacancy", 
            "Vakanser", RunnerCB, "", false, true).fixedCorner();
  gdi.check("Vacancy", currentMode==4);
}
