/************************************************************************
    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 <cassert>

#include "resource.h"

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

#include "oEvent.h"
#include "xmlparser.h"
#include "gdioutput.h"
#include "csvparser.h"
#include "SportIdent.h"
#include "meos_util.h"
#include "oListInfo.h"
#include "TabClass.h"

extern pEvent gEvent;

TabClass::TabClass(oEvent *poe):TabBase(poe)
{
  currentStage=-1;
  EditChanged=false;
  ClassId=0;
  tableMode = false;
  storedNStage = "3";
  storedStart = "";
}

TabClass::~TabClass(void)
{
}

int TimeToRel(const string &s)
{	
	int minute=atoi(s.c_str());

	int second=0;

	int kp=s.find_first_of(':');	
	if(kp>0)
	{		
		string mtext=s.substr(kp+1);
		second=atoi(mtext.c_str());
	}
	return minute*60+second;
}


bool ClassInfoSortStart(ClassInfo &ci1, ClassInfo &ci2)
{
	return ci1.firstStart>ci2.firstStart;
}

int ClassesCB(gdioutput *gdi, int type, void *data)
{
  TabClass &tc = dynamic_cast<TabClass &>(*gdi->getTabs().get(TClassTab));

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

int MultiCB(gdioutput *gdi, int type, void *data)
{
  TabClass &tc = dynamic_cast<TabClass &>(*gdi->getTabs().get(TClassTab));
  return tc.multiCB(*gdi, type, data);
}

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

    if (bi.id=="ChangeLeg") {
      gdi.dropLine();
      legSetup(gdi);
      gdi.disableInput("ChangeLeg");
    }
		else if (bi.id=="SetNStage") {
			if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(ClassId);
      
      if (!pc)
        throw std::exception("Klassen finns ej.");

      int total, finished, dns;
      oe->getNumClassRunners(pc->getId(), 0, total, finished, dns);

      ListBoxInfo lbi;
      gdi.getSelectedItem("Predefined", &lbi);
      oEvent::PredefinedTypes newType = oEvent::PredefinedTypes(lbi.data);

      if (finished > 0) { 
        if (gdi.ask("warning:has_results") == false) 
          return false;
      }
      else if (total>0) {
        bool ok = false;
        ClassType ct = pc->getClassType();
        if (ct == oClassIndividual) {
          switch (newType) {
            case oEvent::PPatrolOptional:
            case oEvent::PPool:
            case oEvent::PPoolDrawn:
            case oEvent::PNoMulti:
              ok = true;
          }
        }

        if (!ok) {
          if (gdi.ask("warning:has_entries") == false)
            return false;
        }
      }

      int nstages = gdi.getTextNo("NStage");
      if (nstages > 0)
        storedNStage = gdi.getText("NStage");
      else {
        if (lbi.data != oEvent::PNoMulti)
          nstages = 1; //Fixed by type
      }

			if (nstages>0 && nstages<41) {
        string st=gdi.getText("StartTime");
        if (oe->convertAbsoluteTime(st)>0)
          storedStart = st;

        save(gdi); //Clears and reloads

				gdi.selectItemByData("Courses", -2);
        gdi.disableInput("Courses");        
        oe->setupRelay(*pc, newType, nstages, st);

				gdi.enableInput("MAdd");
				gdi.enableInput("MCourses");
				gdi.enableInput("MRemove");
        selectClass(gdi, pc->getId());
			}
			else if (nstages==0){
				pc->setNumStages(0);
        pc->synchronize();
				gdi.restore();
				gdi.enableInput("MultiCourse");
				gdi.enableInput("Courses");
        oe->adjustTeamMultiRunners(pc);
			}
			else {
				gdi.alert("Antalet sträckor måste vara ett heltal mellan 0 och 40.");				
			}
		}
    else if(bi.id.substr(0, 7)=="@Course") {
      int cnr=atoi(bi.id.substr(7).c_str());
      
      gdi.restore("Courses", false);
      gdi.setRestorePoint("Courses");
      char bf[128];
      pClass pc=oe->getClass(ClassId);

      if (!pc) {
        return 0;
        gdi.refresh();
      }
      currentStage=cnr;

      sprintf_s(bf, lang.tl("Banor för %s, sträcka %d").c_str(), pc->getName().c_str(), cnr+1);
	    
      gdi.dropLine();
      gdi.pushX();
      gdi.fillRight();
      gdi.addStringUT(1, bf);
      ButtonInfo &bi1 = gdi.addButton("@Course" + itos(cnr-1), "<< Föregående", MultiCB);
      if (cnr<=0)
        gdi.disableInput(bi1.id.c_str());
      ButtonInfo &bi2 = gdi.addButton("@Course" + itos(cnr+1), "Nästa >>", MultiCB);
      if (unsigned(cnr + 1) >= pc->getNumStages())
        gdi.disableInput(bi2.id.c_str());
      gdi.popX();
      gdi.dropLine(2.5);
      gdi.fillRight();
      int x1=gdi.getCX();
      gdi.addListBox("StageCourses", 180, 200, MultiCB, "Sträckans banor:").ignore(true);
      pc->fillStageCourses(gdi, currentStage, "StageCourses");
      int x2=gdi.getCX();
      gdi.fillDown();
      gdi.addListBox("MCourses", 180, 200, MultiCB, "Banor:").ignore(true);
      oe->fillCourses(gdi, "MCourses", true);
      
      gdi.setCX(x1);
      gdi.fillRight();
	    
      gdi.addButton("MRemove", "Ta bort markerad >>", MultiCB);
	    gdi.setCX(x2);
      gdi.fillDown();
      
      gdi.addButton("MAdd", "<< Lägg till", MultiCB);
      gdi.setCX(x1);
      gdi.refresh();
      gdi.scrollTo(gdi.getCX(), gdi.getCY());
    }
		else if(bi.id=="MAdd"){
			DWORD cid=ClassId;
			if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(cid);
		
      if (!pc)
        return false;

			if(currentStage>=0){
			  ListBoxInfo lbi;	
				if(gdi.getSelectedItem("MCourses", &lbi)) {
					int courseid=lbi.data;

					pc->addStageCourse(currentStage, courseid);
					pc->fillStageCourses(gdi, currentStage, "StageCourses");

					pc->synchronize();
					oe->checkOrderIdMultipleCourses(cid);
				}
			}			
		}
		else if(bi.id=="MRemove"){
			if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;

			pClass pc=oe->getClass(cid);

      if (!pc)
        return false;

			if(currentStage>=0){
        ListBoxInfo lbi;
				if(gdi.getSelectedItem("StageCourses", &lbi)) {
					int courseid=lbi.data;

					pc->removeStageCourse(currentStage, courseid, lbi.index);
					pc->synchronize();
					pc->fillStageCourses(gdi, currentStage, "StageCourses");
				}
			}
		}
	}
	else if(type==GUI_LISTBOX) {
		ListBoxInfo bi=*(ListBoxInfo *)data;

    if(bi.id.substr(0, 7)=="LegType") {
      LegTypes lt=LegTypes(bi.data);
      int i=atoi(bi.id.substr(7).c_str());
      pClass pc=oe->getClass(ClassId);
      if (!pc)
        return false;
      pc->setLegType(i, lt);
      char legno[10];
      sprintf_s(legno, "%d", i);
      gdi.setInputStatus(string("StartData")+legno, !pc->startdataIgnored(i));
      gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(i));
      gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(i));

      EditChanged=true;
    }
    else if(bi.id.substr(0, 9)=="StartType") {
      StartTypes st=StartTypes(bi.data);
      int i=atoi(bi.id.substr(9).c_str());
      pClass pc=oe->getClass(ClassId);
      if (!pc)
        return false;
      pc->setStartType(i, st);
      char legno[10];
      sprintf_s(legno, "%d", i);
      gdi.setInputStatus(string("StartData")+legno, !pc->startdataIgnored(i));
      gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(i));
      gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(i));

      EditChanged=true;
    }
    else if (bi.id == "Predefined") {
      bool nleg;
      bool start;
      oe->setupRelayInfo(oEvent::PredefinedTypes(bi.data), nleg, start);
      gdi.setInputStatus("NStage", nleg);
      gdi.setInputStatus("StartTime", start);
      
      string nl = gdi.getText("NStage");
      if (!nleg && nl != "-") {
        storedNStage = nl;
        gdi.setText("NStage", "-");
      }
      else if (nleg && nl == "-") {
        gdi.setText("NStage", storedNStage);
      }

      string st = gdi.getText("StartTime");
      if (!start && st != "-") {
        storedStart = st;
        gdi.setText("StartTime", "-");
      }
      else if (start && st == "-") {
        gdi.setText("StartTime", storedStart);
      }
    }
		else if(bi.id=="Courses")
			EditChanged=true;
	}
	else if(type==GUI_INPUTCHANGE){
		InputInfo ii=*(InputInfo *)data;

    EditChanged=true;
		if(ii.id=="NStage")
			gdi.enableInput("SetNStage");
    //else if(ii.id=="")
	}
	return 0;
}

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

		if (bi.id=="Cancel") {
			loadPage(gdi);
			return 0;
		}
    else if (bi.id=="SwitchMode") {
      if (!tableMode)
        save(gdi);
      tableMode=!tableMode;
      loadPage(gdi);
    }
    else if (bi.id=="Restart") {
      save(gdi);
      gdi.clearPage(true);
      gdi.addString("", 2, "Omstart i stafettklasser");
      gdi.addString("", 10, "help:31661");
      gdi.addListBox("RestartClasses", 200, 250, 0, "Stafettklasser", "", true);
      oe->fillClasses(gdi, "RestartClasses", oEvent::extraNone, oEvent::filterOnlyMulti);
      gdi.pushX();
      gdi.fillRight();
      oe->updateComputerTime();
      int t=oe->getComputerTime()-(oe->getComputerTime()%60)+60;
      gdi.addInput("Rope", oe->getAbsTime(t), 6, 0, "Repdragningstid");
      gdi.addInput("Restart", oe->getAbsTime(t+600), 6, 0, "Omstartstid");
      gdi.dropLine(0.9);
      gdi.addButton("DoRestart","OK", ClassesCB);
      gdi.addButton("Cancel","Stäng", ClassesCB);
      gdi.fillDown();
      gdi.dropLine(3);
      gdi.popX();
    }
    else if (bi.id=="DoRestart") {
      set<int> cls;
      gdi.getSelection("RestartClasses", cls);
      set<int>::iterator it;
      string ropeS=gdi.getText("Rope");
      int rope = oe->getRelativeTime(ropeS);
      string restartS=gdi.getText("Restart");
      int restart = oe->getRelativeTime(restartS);

      if (rope<=0) {
        gdi.alert("Ogiltig reptid.");
        return 0;
      }
      if (restart<=0) {
        gdi.alert("Ogiltig omstartstid.");
        return 0;
      }        
      if (restart<rope) {
        gdi.alert("Reptiden måste ligga före omstartstiden.");
        return 0;
      }
      gdi.addString("", 0, "Sätter rep: " + oe->getAbsTime(rope) + 
                    " och omstart " + oe->getAbsTime(restart) + " för:");
      
      for (it=cls.begin(); it!=cls.end(); ++it) {
        pClass pc=oe->getClass(*it);
        
        if(!pc)
          throw std::exception("Class not found");

        gdi.addString("", 0, pc->getName());

        int ns=pc->getNumStages();

        for (int k=0;k<ns;k++) {
          pc->setRopeTime(k, ropeS);
          pc->setRestartTime(k, restartS);
        }
      }
    }
		else if (bi.id=="DoDrawAll") {
      readClassSettings(gdi);
      ListBoxInfo lbi;
      gdi.getSelectedItem("Method", &lbi);
      bool soft = lbi.data==2;
      bool pairwise = gdi.isChecked("Pairwise");
      
			for(size_t k=0; k<cInfo.size(); k++) {
        const ClassInfo &ci=cInfo[k];
        oe->drawList(ci.classId, 0, drawInfo.firstStart + drawInfo.baseInterval * ci.firstStart,
          drawInfo.baseInterval * ci.interval, ci.nVacant, soft, pairwise, oEvent::drawAll);
			}
			gdi.clearPage(false);
			gdi.addButton("Cancel", "Återgå", ClassesCB);

      oListParam par;
      oListInfo info;
      par.listCode = EStdStartList;
      for (size_t k=0; k<cInfo.size(); k++) 
        par.selection.insert(cInfo[k].classId);

      oe->generateListInfo(par, gdi.getLineHeight(), info);
      oe->generateList(gdi, false, info);
      gdi.refresh();
		}
    else if (bi.id == "RemoveVacant") {
      if (gdi.ask("Vill du radera alla vakanser från tävlingen?")) {
        oe->removeVacanies(0);
      }
    }
    else if (bi.id == "SelectAll") {
      set<int> lst;
      oe->getAllClasses(lst);
      gdi.setSelection("Classes", lst);
    }
    else if (bi.id == "SelectUndrawn") {
      set<int> lst;
      oe->getNotDrawnClasses(lst, false);
      gdi.setSelection("Classes", lst);
    }
    else if (bi.id == "QuickSettings") {
      save(gdi);
      prepareForDrawing(gdi);
    }
    else if (bi.id == "DrawMode") {
      save(gdi);
      ClassId = 0;
		  if(EditChanged)	{
			  if(gdi.ask("Spara ändringar?"))
				  gdi.sendCtrlMessage("Save");
		  }
		
		  EditChanged=false;
		  gdi.clearPage(true);

      gdi.addString("", boldLarge, "Lotta flera klasser");
      gdi.dropLine();

      gdi.pushX();
      gdi.fillRight();
      gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, "Första (ordinarie) start:");
      gdi.addInput("MinInterval", "2:00", 10, 0, "Minsta startintervall:");
      gdi.fillDown();
      gdi.addInput("Vacances", "5%", 10, 0, "Andel vakanser:");
      gdi.popX();
      
      gdi.addSelection("Method", 200, 200, 0, "Metod:");
      gdi.addItem("Method", lang.tl("Lottning"), 1);
      gdi.addItem("Method", lang.tl("SOFT-lottning"), 2);
      gdi.selectItemByData("Method", 1);
      
      gdi.fillDown();
      gdi.addCheckbox("LateBefore", "Efteranmälda före ordinarie");
      //gdi.addCheckbox("Pairwise", "Tillämpa parstart", 0, false);
      gdi.dropLine();
      gdi.popX();
      gdi.fillRight();
      gdi.addButton("AutomaticDraw", "Automatisk lottning", ClassesCB).setDefault();
      gdi.addButton("DrawAll", "Manuell lottning", ClassesCB).setExtra(LPVOID(1));
      gdi.addButton("Cancel", "Återgå", ClassesCB).setCancel();
      gdi.newColumn();

      gdi.addString("", 10, "help_autodraw");
    }
    else if (bi.id == "AutomaticDraw") {
      string firstStart = gdi.getText("FirstStart");
      string minInterval = gdi.getText("MinInterval");
      string vacances = gdi.getText("Vacances");
      bool lateBefore = false;//gdi.isChecked("LateBefore");
      bool pairwise = gdi.isChecked("Pairwise");
      
      ListBoxInfo lbi;
      gdi.getSelectedItem("Method", &lbi);
      bool useSoft = lbi.data==2;
      gdi.clearPage(true);
      oe->automaticDrawAll(gdi, firstStart, minInterval, vacances, 
                            lateBefore, useSoft, pairwise);

      gdi.scrollToBottom();
      gdi.addButton("Cancel", "Återgå", ClassesCB);
    }
    else if (bi.id == "SelectMisses") {
      set<int> lst;
      oe->getNotDrawnClasses(lst, true);
      gdi.setSelection("Classes", lst);
    }
    else if (bi.id == "SelectNone") {
      gdi.setSelection("Classes", set<int>());
    }
		else if (bi.id == "DrawAll") {
      int origin = int(bi.getExtra());
      string firstStart = oe->getAbsTime(3600);
      string minInterval = "2:00";
      string vacances = "5%";
      bool pairwise = false;
      
      if (origin!=13) {
        if (origin!=1) {
          save(gdi);
          ClassId = 0;
			    if(EditChanged)	{
				    if(gdi.ask("Spara ändringar?"))
					    gdi.sendCtrlMessage("Save");
			    }
    		
			    EditChanged=false;
        }
        else {
          firstStart = gdi.getText("FirstStart");
          minInterval = gdi.getText("MinInterval");
          vacances = gdi.getText("Vacances");
          pairwise = gdi.isChecked("Pairwise");
        }

			  gdi.clearPage(false);

        gdi.addString("", boldLarge, "Lotta flera klasser");
  		
        gdi.pushY();
        gdi.addListBox("Classes", 200, 300, 0, "Klasser:", "", true);
        gdi.setTabStops("Classes", 185);
        gdi.fillRight();
        gdi.pushX();

        gdi.addButton("SelectAll", "Välj allt", ClassesCB,  
                      "Välj alla klasser").isEdit(true);

        gdi.addButton("SelectMisses", "Saknad starttid", ClassesCB,  
          "Välj klasser där någon löpare saknar starttid").isEdit(true);
        
        gdi.dropLine(2.3);
        gdi.popX();

        gdi.addButton("SelectUndrawn", "Ej lottade", ClassesCB, 
          "Välj klasser där alla löpare saknar starttid").isEdit(true);

        gdi.fillDown();
        gdi.addButton("SelectNone", "Välj inget", ClassesCB, 
          "Avmarkera allt").isEdit(true);
        gdi.popX();

        oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
        
        int bx = gdi.getCX();
        int by = gdi.getCY()+gdi.getLineHeight();
        gdi.newColumn();
        gdi.popY();

			  gdi.addInput("FirstStart", firstStart, 10, 0, "Första start:");
			  gdi.addInput("BaseInterval", "1:00", 10, 0, "Basintervall (min)");
			  gdi.addInput("MinInterval", minInterval, 10, 0, "Minsta intervall i klass");
			  gdi.addInput("MaxInterval", "8:00", 10, 0, "Största intervall i klass");
			  gdi.addInput("nFields", "10", 10, 0, "Max parallellt startande");
  			
        gdi.dropLine(0.5);
        gdi.addInput("Vacances", vacances, 10, 0, "Andel vakanser:");
			  gdi.addInput("VacancesMin", "1", 10, 0, "Min. vakanser (per klass)");
			  gdi.addInput("VacancesMax", "10", 10, 0, "Max. vakanser (per klass)");
  		
        gdi.dropLine(0.5);
        gdi.addInput("Extra", "15%", 10, 0, "Förväntad andel efteranmälda");
        
        gdi.dropLine();

        by = max(by, gdi.getCY());

        gdi.newColumn();
        gdi.popY();
        gdi.addString("", 10, "help_draw");

        gdi.setCX(bx);
        gdi.setCY(by);
        gdi.pushX();
        gdi.setRestorePoint("Setup");

      }
      else {
        gdi.restore("Setup");
        gdi.enableEditControls(true);
      }
			
			gdi.fillRight();
			gdi.addButton("PrepareDrawAll", "Fördela starttider", ClassesCB);
	    gdi.addButton("DrawAllBefore", "Efteranmälda (före ordinarie)", ClassesCB);
			gdi.addButton("DrawAllAfter", "Efteranmälda (efter ordinarie)", ClassesCB);
			gdi.fillDown();
			
			gdi.addButton("Cancel", "Avbryt", ClassesCB);

			gdi.popX();
			gdi.dropLine();
			gdi.refresh();
		}
		else if (bi.id=="PrepareDrawAll") {
			gdi.restore("Setup");
      gdi.enableEditControls(false);
      drawInfo.classes.clear();

      set<int> classes;
      gdi.getSelection("Classes", classes);

      for (set<int>::iterator it = classes.begin(); it!=classes.end();++it)
        drawInfo.classes[*it] = ClassInfo(*it);

			drawInfo.maxVacancy=gdi.getTextNo("VacancesMax");
      drawInfo.minVacancy=gdi.getTextNo("VacancesMin");
      drawInfo.vacancyFactor = 0.01*atof(gdi.getText("Vacances").c_str());
      drawInfo.extraFactor = 0.01*atof(gdi.getText("Extra").c_str());
				
      drawInfo.baseInterval=TimeToRel(gdi.getText("BaseInterval"));
			drawInfo.minClassInterval=TimeToRel(gdi.getText("MinInterval"));
			drawInfo.maxClassInterval=TimeToRel(gdi.getText("MaxInterval"));
			drawInfo.nFields=gdi.getTextNo("nFields");
			drawInfo.firstStart=oe->getRelativeTime(gdi.getText("FirstStart"));

      if (drawInfo.firstStart<=0) {
        drawInfo.baseInterval = 0;
        gdi.addString("", 0, "Raderar starttider...");
      }

			oe->optimizeStartOrder(gdi, drawInfo, cInfo);
			
      showClassSettings(gdi);
		}
    else if (bi.id == "DrawAdjust") {
      readClassSettings(gdi);

      gdi.restore("Setup");

      oe->optimizeStartOrder(gdi, drawInfo, cInfo);
			
      showClassSettings(gdi);
    }
    else if (bi.id == "DrawAllBefore" || bi.id == "DrawAllAfter") {
      oe->drawRemaining(true, bi.id == "DrawAllAfter");
      loadPage(gdi);
    }
		else if(bi.id=="DoDraw" || bi.id=="DoDrawAfter"  || bi.id=="DoDrawBefore"){			
 			if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;

			gdi.disableInput("DoDraw");
			gdi.disableInput("DoDrawAfter");
      gdi.disableInput("DoDrawBefore");
			gdi.disableInput("Cancel");

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

			gdi.restore();

			gdi.addButton("Cancel", "Återgå", ClassesCB);

			gdi.popX();
		
      int interval=convertAbsoluteTimeMS(gdi.getText("Interval"));
			int vacanses=gdi.getTextNo("Vacanses");
      int leg=gdi.getTextNo("Leg")-1;

      if(leg<0)
        throw std::exception("Felaktig sträcka.");

      int bib=gdi.getTextNo("Bib");
      int t=oe->getRelativeTime(gdi.getText("FirstStart"));

      if (t<=0) 
        throw std::exception("Ogiltig första starttid. Måste vara efter nolltid.");

      oEvent::DrawType type(oEvent::drawAll);
      if (bi.id=="DoDrawAfter")
        type = oEvent::remainingAfter;
      else if (bi.id=="DoDrawBefore")
        type = oEvent::remainingBefore;
      
      bool pairwise = gdi.isChecked("Pairwise");
      
      if(lbi.data==1)
				oe->drawList(cid, leg, t, interval, vacanses, false, pairwise, type);
			else if (lbi.data==2)
        oe->drawList(cid, leg, t, interval, vacanses, true, pairwise, type);
      else if (lbi.data==3)
				oe->drawListClumped(cid, t, interval, vacanses);

			oe->addBib(cid, leg, bib);

			gdi.dropLine();
      pClass pc=oe->getClass(cid);
      gdi.addString("", 1, string("Startlista - X#")+(pc ? pc->getName(): "?"));
			gdi.dropLine();
		
			//oe->generateStartlist(gdi, leg, 0, vector<int>(1, cid));
      oListParam par;
      par.selection.insert(cid);
      oListInfo info;
      par.listCode = EStdStartList;      
      oe->generateListInfo(par, gdi.getLineHeight(), info);      
      oe->generateList(gdi, false, info);

			gdi.refresh();
			return 0;
		}
		else if (bi.id=="Draw") {
      save(gdi);
      if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;
			
			if(oe->classHasResults(cid)) {
				if(!gdi.ask("Klassen har redan resultat. Starttider kommer att skrivas över. Vill du verkligen fortsätta?"))
					return 0;
			}

			pClass pc=oe->getClass(cid);

      if(!pc)
        throw std::exception("Class not found");
      if(EditChanged)			
				gdi.sendCtrlMessage("Save");
		
			gdi.clearPage(false);
			
      gdi.addString("", 1, "Lotta klassen X#"+pc->getName());
			
			gdi.pushX();
			gdi.fillRight();
			gdi.addInput("FirstStart", oe->getAbsTime(3600), 10, 0, "Första start");
			gdi.addInput("Interval", "2:00", 10, 0, "Startintervall (min)");
			gdi.addInput("Vacanses", "0", 10, 0, "Antal vakanser");
			gdi.addInput("Leg", "1", 4, 0, "Str.");
      gdi.fillDown();
      gdi.addInput("Bib", "", 10, 0, "Nummerlappar");
      
			gdi.popX();
			gdi.dropLine();
      gdi.addCheckbox("Pairwise", "Tillämpa parstart", 0, false);
      
			gdi.setRestorePoint();
			gdi.pushX();
			gdi.fillRight();
			
      gdi.addSelection("Method", 200, 200, 0, "Metod:");
      gdi.addItem("Method", lang.tl("Lottning"), 1);
      gdi.addItem("Method", lang.tl("SOFT-lottning"), 2);
      gdi.addItem("Method", lang.tl("Klungstart"), 3);
      gdi.selectItemByData("Method", 1);
      gdi.dropLine(0.9);

      gdi.addButton("DoDraw", "Lotta klassen", ClassesCB, "Lotta om hela klassen");
			gdi.addButton("DoDrawBefore", "Ej lottade, före", ClassesCB, "Lotta löpare som saknar starttid");
	    gdi.fillDown();
		    
      gdi.addButton("DoDrawAfter", "Ej lottade, efter", ClassesCB, "Lotta löpare som saknar starttid");

			gdi.popX();
			gdi.dropLine();
			gdi.addButton("Cancel", "Avbryt", ClassesCB);

      gdi.dropLine();
			
      gdi.addString("", 10, "help:41641");

			EditChanged=false;
			gdi.refresh();
		}
  	else if (bi.id=="Bibs") {
      save(gdi);
      if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;
	
			pClass pc=oe->getClass(cid);
      if (!pc)
        throw std::exception("Class not found");

			gdi.clearPage(false);			
      gdi.addString("", 1, "Nummerlappar i X#" + pc->getName());
			
			gdi.addString("", 0, "Ange första nummerlappsnummer eller lämna blankt för inga nummerlappar");
			gdi.addInput("Bib", "", 10, 0, "Nummerlappar");
	
			gdi.fillRight();
			gdi.addButton("DoBibs", "Tilldela", ClassesCB);	
			gdi.fillDown();			
			gdi.addButton("Cancel", "Avbryt", ClassesCB);
			gdi.popX();
	
			EditChanged=false;
			gdi.refresh();
		}
		else if (bi.id=="DoBibs") {			
 			if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;

			gdi.disableInput("DoBibs");
			gdi.disableInput("Cancel");
	
			gdi.dropLine();
			int bib=gdi.getTextNo("Bib");
			oe->addBib(cid, 0, bib);

			gdi.addString("", 1, "Utfört");
			gdi.dropLine();
			gdi.addButton("Cancel", "Återgå", ClassesCB);

      gdi.refresh();
			return 0;
		}    
		else if (bi.id=="Split") {
      save(gdi);
      if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(ClassId);
      if (!pc)
        throw std::exception("Class not found");

			gdi.clearPage(true);			
      gdi.addString("", 1, "Dela klass: " + pc->getName());

      gdi.addSelection("Type", 200, 100, ClassesCB, "Typ av delning:");
      gdi.addItem("Type", lang.tl("Dela klubbvis"), 1);
      gdi.addItem("Type", lang.tl("Dela efter ranking"), 2);
      gdi.selectItemByData("Type", 1);
      gdi.fillRight();
      gdi.pushX();
      gdi.addString("TypeDesc", 0, "Antal klasser:");
      gdi.setCX(gdi.getCX()+50);
      gdi.addInput("SplitInput", "2", 5);
      gdi.dropLine(3);
      gdi.popX();
      gdi.addButton("DoSplit", "Dela", ClassesCB);
      gdi.addButton("Cancel", "Avbryt", ClassesCB);
      gdi.popX();
    }
    else if (bi.id=="DoSplit") {
      if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(ClassId);
      if (!pc)
        throw std::exception("Class not found");

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

      int number = gdi.getTextNo("SplitInput");

      if (number<=1)
        throw std::exception ("Antal måste vara större än 1.");

      vector<int> outClass;
      if (lbi.data==1)
        oe->splitClass(ClassId, number, outClass);
      else 
        oe->splitClassRank(ClassId, number, outClass);

      gdi.clearPage(true);
      gdi.addButton("Cancel", "Återgå", ClassesCB);
      
      oListParam par;
      par.selection.insert(outClass.begin(), outClass.end());
      oListInfo info;
      par.listCode = EStdStartList;      
      oe->generateListInfo(par, gdi.getLineHeight(), info);      
      oe->generateList(gdi, false, info);
      //oe->generateStartlist(gdi, 0, 0, outClass);
    }
    else if (bi.id=="Merge") {
      save(gdi);
      if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(ClassId);
      if (!pc)
        throw std::exception("Class not found");

			gdi.clearPage(true);			
      gdi.addString("", 1, "Slå ihop klass: " + pc->getName());
      gdi.addString("", 10, "help:12138");
      gdi.addSelection("Class", 150, 300, 0, "Klass att slå ihop:");
      oe->fillClasses(gdi, "Class", oEvent::extraNone, oEvent::filterNone);
      gdi.fillRight();
      gdi.addButton("DoMerge", "Slå ihop", ClassesCB);
      gdi.addButton("Cancel", "Avbryt", ClassesCB);
    }
    else if (bi.id=="DoMerge") {
      if (!checkClassSelected(gdi))
        return false;

			pClass pc=oe->getClass(ClassId);
      if (!pc)
        throw std::exception("Class not found");

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

      if (signed(lbi.data)<=0)
        throw std::exception("Ingen klass vald.");
      
      if (lbi.data==ClassId)
        throw std::exception("En klass kan inte slås ihop med sig själv.");

      oe->mergeClass(ClassId, lbi.data);
      gdi.clearPage(true);
      gdi.addButton("Cancel", "Återgå", ClassesCB);

      oListParam par;
      par.selection.insert(ClassId);
      oListInfo info;
      par.listCode = EStdStartList;      
      oe->generateListInfo(par, gdi.getLineHeight(), info);      
      oe->generateList(gdi, false, info);
      gdi.refresh();
    }
    else if (bi.id=="MultiCourse") {
      save(gdi);
			multiCourse(gdi, 0);
      gdi.refresh();
		}
		else if (bi.id=="Save")
      save(gdi);
		else if (bi.id=="Add") {
			save(gdi);
      if(EditChanged)	{
				if(gdi.ask("Spara ändringar?"))
					gdi.sendCtrlMessage("Save");
			}
      pClass pc = oe->addClass(oe->getAutoClassName(), 0);
      if (pc) {
        oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
        selectClass(gdi, pc->getId());
        gdi.setInputFocus("Name", true);
      }
		}
		else if (bi.id=="Remove") {
			EditChanged=false;
  		if (!checkClassSelected(gdi))
        return false;

			DWORD cid=ClassId;
	
			if(oe->isClassUsed(cid))
				gdi.alert("Klassen används och kan inte tas bort.");
			else
				oe->removeClass(cid);

			oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
      ClassId = 0;
      selectClass(gdi, 0);
		}
	}
	else if (type==GUI_LISTBOX) {
		ListBoxInfo bi=*(ListBoxInfo *)data;

    if(bi.id=="Classes") {
      if (gdi.isInputChanged(""))
        save(gdi);

      selectClass(gdi, bi.data);
    }
		else if(bi.id=="Courses")
			EditChanged=true;
    else if (bi.id=="Type") {
      if (bi.data==1) {
        gdi.setText("TypeDesc", "Antal klasser:", true);
        gdi.setText("SplitInput", "2");
      }
      else {
        gdi.setText("TypeDesc", "Löpare per klass:", true);
        gdi.setText("SplitInput", "100");
      }
    }
	}
	else if(type==GUI_INPUTCHANGE) {
		//InputInfo ii=*(InputInfo *)data;

		//if(ii.id=="Name")
		//	EditChanged=true;
	}
	else if(type==GUI_CLEAR) {
    if (ClassId>0)
      save(gdi);
		if(EditChanged) {
			if(gdi.ask("Spara ändringar?"))
				gdi.sendCtrlMessage("Save");
		}
		return true;
	}
	return 0;
}

void TabClass::readClassSettings(gdioutput &gdi)
{
	for (size_t k=0;k<cInfo.size();k++) {
    ClassInfo &ci = cInfo[k];

    const string &start = gdi.getText("S"+itos(k));
    const string &intervall = gdi.getText("I"+itos(k));
    int vacant = atoi(gdi.getText("V"+itos(k)).c_str());
    int reserved = atoi(gdi.getText("R"+itos(k)).c_str());

    int startPos = oe->getRelativeTime(start) - drawInfo.firstStart;

    if (drawInfo.firstStart == 0 && startPos == -1)
      startPos = 0;
    else if (startPos<0 || (startPos % drawInfo.baseInterval)!=0)
      throw std::exception("Ogiltig tid");

    startPos /= drawInfo.baseInterval;

    int intervalPos = TimeToRel(intervall);

    if (intervalPos<0 || (intervalPos % drawInfo.baseInterval)!=0)
      throw std::exception("Ogiltigt startintervall");

    intervalPos /= drawInfo.baseInterval;

    ci.nVacant = vacant;
    ci.nExtra = reserved;

    // If times has been changed, mark this class to be kept fixed
    if (ci.firstStart != startPos || ci.interval!=intervalPos)
      ci.hasFixedTime = true;

    ci.firstStart = startPos;
    ci.interval = intervalPos;

    drawInfo.classes[ci.classId] = ci;
	}
}

void TabClass::showClassSettings(gdioutput &gdi) 
{
  ClassInfo::sSortOrder=1;			
  sort(cInfo.begin(), cInfo.end());
	ClassInfo::sSortOrder=0;
	
	int laststart=0;
  for (size_t k=0;k<cInfo.size();k++) {
    const ClassInfo &ci = cInfo[k];
		laststart=max(laststart, ci.firstStart+ci.nRunners*ci.interval);
	}

  int y = 0;
  int xp = gdi.getCX();
  const int width = 80;

  if (!cInfo.empty()) {
    
    gdi.addString("", 1, string("Sista start (nu tilldelad): X#")+
      oe->getAbsTime((laststart)*drawInfo.baseInterval+drawInfo.firstStart));
	  gdi.dropLine();
    
    y = gdi.getCY();
    gdi.addString("", y, xp, 1, "Sammanställning, klasser:");
    gdi.addString("", y, xp+300, 0, "Första start:");
    gdi.addString("", y, xp+300+width, 0, "Intervall:");
    gdi.addString("", y, xp+300+width*2, 0, "Vakanser:");
    gdi.addString("", y, xp+300+width*3, 0, "Reserverade:");
  }

  gdi.pushX();
	for (size_t k=0;k<cInfo.size();k++) {
    const ClassInfo &ci = cInfo[k];
		char bf[256];
		int laststart=ci.firstStart + ci.nRunners * ci.interval;
		string first=oe->getAbsTime(ci.firstStart*drawInfo.baseInterval+drawInfo.firstStart);
		string last=oe->getAbsTime((laststart)*drawInfo.baseInterval+drawInfo.firstStart);
    pClass pc=oe->getClass(ci.classId);
		sprintf_s(bf, 256, "%s, %d platser. Start %d-[%d]-%d (%s-%s)",
      pc ? pc->getName().c_str():"", ci.nRunners,
			ci.firstStart, ci.interval, laststart, first.c_str(), last.c_str());
		
    gdi.fillRight();
		gdi.addStringUT(0, bf);
    y = gdi.getCY();
    gdi.addInput(xp+300, y, "S"+itos(k), first, 7);
    gdi.addInput(xp+300+width, y, "I"+itos(k), formatTime(ci.interval*drawInfo.baseInterval), 7);
    gdi.addInput(xp+300+width*2, y, "V"+itos(k), itos(ci.nVacant), 7);
    gdi.addInput(xp+300+width*3, y, "R"+itos(k), itos(ci.nExtra), 7);

    gdi.dropLine(1.6);
    gdi.fillDown();
    gdi.popX();
	}

	gdi.dropLine();
	gdi.fillRight();

  if (!cInfo.empty()) {
    gdi.pushX();
    gdi.addSelection("Method", 200, 200, 0, "Metod:");
    gdi.addItem("Method", lang.tl("Lottning"), 1);
    gdi.addItem("Method", lang.tl("SOFT-lottning"), 2);
      
    gdi.selectItemByData("Method", 1);
    gdi.dropLine(0.9);

    gdi.addButton("DoDrawAll", "Utför lottning", ClassesCB);
    gdi.addButton("DrawAdjust", "Uppdatera fördelning", ClassesCB, 
        "Uppdatera fördelningen av starttider med hänsyn till manuella ändringar ovan");
  }
  gdi.addButton("DrawAll", "Ändra inställningar", ClassesCB,
      "Ändra grundläggande inställningar och gör en ny fördelning").setExtra(LPVOID(13));

  gdi.addButton("Cancel", "Avbryt", ClassesCB);

  gdi.fillDown();
	gdi.dropLine(2);
  gdi.popX();
  if (!cInfo.empty()) 
    gdi.addCheckbox("Pairwise", "Tillämpa parstart", 0, false);
    
	gdi.refresh();
	gdi.updateScrollbars();
}

void TabClass::selectClass(gdioutput &gdi, int cid)
{
  oe->fillCourses(gdi, "Courses", true);
  gdi.addItem("Courses", lang.tl("Ingen bana"), -2);

  if (cid==0) {
		gdi.restore("", true);
		gdi.disableInput("MultiCourse");
   	gdi.enableInput("Courses");
    gdi.enableEditControls(false);
    gdi.setText("Name", "");  	
	  gdi.selectItemByData("Courses", -2);
    gdi.check("CoursePool", false);
    gdi.check("AllowQuickEntry", true);
    gdi.check("NoTiming", false);
 
    ClassId=cid;
    EditChanged=false;
    
    gdi.disableInput("Remove");
    gdi.disableInput("Save");
    return;
  }

	pClass pc = oe->getClass(cid);

  if (!pc) {
    selectClass(gdi, 0);
    return;
  }
  gdi.enableEditControls(true);
  gdi.enableInput("Remove");
  gdi.enableInput("Save");

	pc->synchronize();
	gdi.setText("Name", pc->getName());
  
  gdi.setText("ClassType", pc->getType());
  gdi.setText("StartName", pc->getStart());
  if (pc->getBlock()>0)
    gdi.selectItemByData("StartBlock", pc->getBlock());
  else
    gdi.selectItemByData("StartBlock", -1);
    
  gdi.check("AllowQuickEntry", pc->getAllowQuickEntry());
  gdi.check("NoTiming", pc->getNoTiming());

	if (pc->hasMultiCourse()) {				
		gdi.restore("", false);

    multiCourse(gdi, pc->getNumStages());
    gdi.refresh();
		//gdi.setText("StartTime", pc->GetClassStartTimeS());

		int nstage=pc->getNumStages();
    gdi.setText("NStage", nstage);  

    for (int k=0;k<nstage;k++) {
      char legno[10];
      sprintf_s(legno, "%d", k);

      gdi.selectItemByData((string("LegType")+legno).c_str(), pc->getLegType(k));
      gdi.selectItemByData((string("StartType")+legno).c_str(), pc->getStartType(k));
      gdi.setText(string("StartData")+legno, pc->getStartDataS(k));
      gdi.setInputStatus(string("StartData")+legno, !pc->startdataIgnored(k));
      gdi.setInputStatus(string("Restart")+legno, !pc->restartIgnored(k));
      gdi.setInputStatus(string("RestartRope")+legno, !pc->restartIgnored(k));

      //    gdi.addInput(string("Restart")+legno, "", 4, MultiCB);
      gdi.setText(string("Restart")+legno, pc->getRestartTimeS(k));
      gdi.setText(string("RestartRope")+legno, pc->getRopeTimeS(k));
      gdi.selectItemByData((string("MultiR")+legno).c_str(), pc->getLegRunner(k));
     
    }

    gdi.addItem("Courses", lang.tl("Flera banor"), -3); 
		gdi.selectItemByData("Courses", -3);
		gdi.disableInput("Courses");			

    gdi.check("CoursePool", pc->hasCoursePool());
	}
	else {
		gdi.restore("", true);
		gdi.enableInput("MultiCourse");
   	gdi.enableInput("Courses");
   	pCourse pcourse=pc->getCourse();
	  gdi.selectItemByData("Courses", pcourse ? pcourse->getId():-2);
	}

  gdi.selectItemByData("Classes", cid);

  ClassId=cid;
	EditChanged=false;
}

void TabClass::legSetup(gdioutput &gdi) 
{
  gdi.restore("RelaySetup");
  gdi.pushX();
  gdi.fillDown();

  gdi.addString("", 10, "help:relaysetup");
  gdi.dropLine();
  gdi.addSelection("Predefined", 150, 200, MultiCB, "Fördefinierade tävlingsformer:").ignore(true);
  oe->fillPredefinedCmp(gdi, "Predefined");
  gdi.selectItemByData("Predefined", oEvent::PRelay);//Classic relay
  
  gdi.fillRight();
  gdi.addInput("NStage", storedNStage, 4, MultiCB, "Antal sträckor:").ignore(true);
 
  gdi.addInput("StartTime",  storedStart, 6, MultiCB, "Starttid (HH:MM:SS):");
  gdi.popX();
  gdi.fillRight();
  gdi.dropLine(3);
  gdi.addButton("SetNStage", "Verkställ", MultiCB);
  gdi.fillDown();
  gdi.addButton("Cancel", "Avbryt", ClassesCB);
  
  gdi.popX();
}


void TabClass::multiCourse(gdioutput &gdi, int nLeg)
{
  currentStage=-1;
	gdi.disableInput("MultiCourse");
	gdi.setRestorePoint();
  gdi.fillDown();
 	gdi.newColumn();
	
	//gdi.dropLine();
	gdi.addString("", 2, "Flera banor / stafett / patrull / banpool");
  gdi.addString("", 0, "Låt klassen ha mer än en bana eller sträcka");
  gdi.dropLine();
  gdi.setRestorePoint("RelaySetup");

  if (nLeg==0) 
    legSetup(gdi);
  else {
    gdi.pushX();
    gdi.fillRight();
    gdi.addButton("ChangeLeg", "Ändra klassinställningar...", MultiCB, "Starta en guide som hjälper dig göra klassinställningar");

    gdi.fillDown();
    gdi.popX();
    gdi.dropLine(2);
    
    gdi.dropLine(0.5);  
    int headYPos=gdi.getCY();
    gdi.dropLine(1.2);
      
    for (int k=0;k<nLeg;k++) {
      char legno[10];
      int headXPos[10];
      sprintf_s(legno, "%d", k);
      gdi.fillRight();

      headXPos[0]=gdi.getCX();
      gdi.addInput(legno, "", 2);
      gdi.setText(legno, k+1);
      gdi.disableInput(legno);

      headXPos[1]=gdi.getCX();
      string legType(string("LegType")+legno);
      gdi.addSelection(legType, 100, 200, MultiCB);
      oClass::fillLegTypes(gdi, legType);

      headXPos[2]=gdi.getCX();
      string startType(string("StartType")+legno);    
      gdi.addSelection(startType, 90, 200, MultiCB);
      oClass::fillStartTypes(gdi, startType);

      headXPos[3]=gdi.getCX();
      gdi.addInput(string("StartData")+legno, "", 5, MultiCB);

      string multir(string("MultiR")+legno);
      headXPos[4]=gdi.getCX();
      gdi.addSelection(multir, 60, 200, MultiCB);
      for (int j=0;j<nLeg;j++) {
        char bf[16];
        sprintf_s(bf, lang.tl("Str. %d").c_str(), j+1);
        gdi.addItem(multir, bf, j);
      }

      headXPos[5]=gdi.getCX();
      gdi.addInput(string("RestartRope")+legno, "", 5, MultiCB);

      headXPos[6]=gdi.getCX();
      gdi.addInput(string("Restart")+legno, "", 5, MultiCB);

      gdi.dropLine(-0.1);
      gdi.addButton(string("@Course")+legno, "Banor...", MultiCB);

      gdi.fillDown();
      gdi.popX();
      gdi.dropLine(2.1);

      if (k==0) { //Add headers
        gdi.addString("", headYPos, headXPos[0], 0, "Str.");
        gdi.addString("", headYPos, headXPos[1], 0, "Sträcktyp");
        gdi.addString("", headYPos, headXPos[2], 0, "Starttyp");
        gdi.addString("", headYPos, headXPos[3], 0, "Starttid");
        gdi.addString("", headYPos, headXPos[4], 0, "Löpare");
        gdi.addString("", headYPos, headXPos[5], 0, "Rep");
        gdi.addString("", headYPos, headXPos[6], 0, "Omstart");
      }
    }

    if(nLeg==1)
      gdi.sendCtrlMessage("@Course0");

    gdi.addCheckbox("CoursePool", "Använd banpool", 0, false, 
                      "Knyt löparna till banor från en pool vid målgång.");
  }
  gdi.refresh();
}

bool TabClass::checkClassSelected(const gdioutput &gdi) const
{
  if (ClassId<=0) {
		gdi.alert("Ingen klass vald.");
		return false;
	}
  else return true;
}

void TabClass::save(gdioutput &gdi)
{
	DWORD cid=ClassId;

  pClass pc;			
	string name=gdi.getText("Name");
	
  if (cid==0 && name.empty()) 
    return;
  
  if (name.empty()) 
    throw std::exception("Klassen måste ha ett namn.");
	
	bool create=false;

	if (cid>0)
		pc=oe->getClass(cid);
	else {
		pc=oe->addClass(name);
		create=true;
	}

  if (!pc)
    throw std::exception("Class not found.");

	ClassId=pc->getId();

	pc->setName(name);
  pc->setStart(gdi.getText("StartName"));
  pc->setType(gdi.getText("ClassType"));
  pc->setBlock(gdi.getTextNo("StartBlock"));

  pc->setCoursePool(gdi.isChecked("CoursePool"));

  pc->setAllowQuickEntry(gdi.isChecked("AllowQuickEntry"));
  pc->setNoTiming(gdi.isChecked("NoTiming"));
	ListBoxInfo lbi;
	gdi.getSelectedItem("Courses", &lbi);

	if (lbi.data==0)	{
		//Skapa ny bana...
		pCourse pcourse=oe->addCourse("Bana "+name);
		pc->setCourse(pcourse);				
		pc->synchronize();
		return;
	}
	else if(lbi.data==-2)
		pc->setCourse(0);
	else if(signed(lbi.data)>0)
		pc->setCourse(oe->getCourse(lbi.data));

  if(pc->hasMultiCourse()) {
		int nstage=pc->getNumStages();

    for (int k=0;k<nstage;k++) {
      char legno[10];
      sprintf_s(legno, "%d", k);

      if (!gdi.hasField(string("LegType")+legno))
        continue;

      ListBoxInfo lbi;
      gdi.getSelectedItem(string("LegType")+legno, &lbi);
      pc->setLegType(k, LegTypes(lbi.data));

      gdi.getSelectedItem(string("StartType")+legno, &lbi);
      pc->setStartType(k, StartTypes(lbi.data));

      pc->setStartData(k, gdi.getText(string("StartData")+legno));      

      pc->setRestartTime(k, gdi.getText(string("Restart")+legno));      
      pc->setRopeTime(k, gdi.getText(string("RestartRope")+legno));      

      gdi.getSelectedItem(string("MultiR")+legno, &lbi);
      pc->setLegRunner(k, lbi.data);    
    }
  }

	pc->synchronize();
  oe->reCalculateLeaderTimes(pc->getId());
	oe->reEvaluateClass(pc->getId(), true);					
	oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
	EditChanged=false;
  ClassId = 0;
  selectClass(gdi, pc->getId());
  
  // Check/warn that starts blocks are set up correctly
  vector<int> b;
  vector<string> s;
  oe->getStartBlocks(b, s);

  oe->sanityCheck(gdi, false);
}

bool TabClass::loadPage(gdioutput &gdi)
{
  oe->checkDB();
	gdi.selectTab(tabId);
	gdi.clearPage(true);
  int xp=gdi.getCX();
	
  const int button_w=gdi.scaleLength(90);
  string switchMode;
  switchMode=tableMode ? "Formulärläge" : "Tabelläge";
  gdi.addButton(2, 2, button_w, "SwitchMode", switchMode, 
    ClassesCB, "Välj vy", false, false).fixedCorner();

  if(tableMode) {
    Table *tbl=oe->getClassTB();
    gdi.addTable(tbl, xp, 30);
    return true;
  }
  gdi.addString("", boldLarge, "Klasser");

	gdi.fillDown();
 	gdi.addListBox("Classes", 200, 420, ClassesCB, "").isEdit(false).ignore(true);
  gdi.setTabStops("Classes", 185);
	oe->fillClasses(gdi, "Classes", oEvent::extraDrawn, oEvent::filterNone);
	
	gdi.newColumn();
	gdi.dropLine(2);		

	gdi.fillRight();
  gdi.pushX();
  gdi.addInput("Name", "", 14, ClassesCB, "Klassnamn:");
  gdi.addCombo("ClassType", 100, 300, 0, "Typ:");
  oe->fillClassTypes(gdi, "ClassType");

  gdi.dropLine(3);
  gdi.popX();
  gdi.addSelection("Courses", 120, 400, ClassesCB, "Bana:");
	oe->fillCourses(gdi, "Courses", true);
	gdi.addItem("Courses", lang.tl("Ingen bana"), -2);
	
  gdi.dropLine(0.9);
	gdi.addButton("MultiCourse", "Flera banor/stafett...", ClassesCB);
  gdi.disableInput("MultiCourse");
  gdi.popX();
  gdi.dropLine(3);
  
  
  gdi.addCombo("StartName", 120, 300, 0, "Startnamn:");
  oe->fillStarts(gdi, "StartName");

  gdi.addSelection("StartBlock", 80, 300, 0, "Startblock:");

  for (int k=1;k<=100;k++) {
    char bf[16];
    sprintf_s(bf, "%d", k);
    gdi.addItem("StartBlock", bf, k);
  }
  
  gdi.popX();
	gdi.dropLine(3);
  gdi.addCheckbox("AllowQuickEntry", "Tillåt direktanmälan", 0);
  gdi.addCheckbox("NoTiming", "Utan tidtagning", 0);

  gdi.dropLine(2);
  gdi.popX();
  gdi.fillDown();
  gdi.addString("", 1, "Funktioner");
  gdi.dropLine(0.3);
  gdi.pushX();
  gdi.fillRight();
	gdi.addButton("Draw", "Lotta...", ClassesCB).isEdit(true);
	gdi.addButton("Bibs", "Nummerlappar...", ClassesCB).isEdit(true);
	gdi.addButton("Restart", "Omstart...", ClassesCB).isEdit(true);

  gdi.popX();
  gdi.dropLine(2.5);
  gdi.addButton("Merge", "Slå ihop...",  ClassesCB).isEdit(true);
	gdi.addButton("Split", "Dela...", ClassesCB).isEdit(true);

  gdi.popX();
	gdi.dropLine(3.5);
	
	gdi.addButton("DrawMode", "Lotta flera klasser", ClassesCB);

  gdi.fillDown();
  gdi.addButton("QuickSettings", "Snabbinställningar", ClassesCB);
  
  gdi.popX();
  gdi.addButton("RemoveVacant", "Radera vakanser", ClassesCB);

  gdi.popX();
  gdi.dropLine(3);
	gdi.fillRight();	
  gdi.addButton("Save", "Spara", ClassesCB).setDefault();
	gdi.disableInput("Save");
  gdi.addButton("Remove", "Radera", ClassesCB);
  gdi.disableInput("Remove");
  gdi.addButton("Add", "Ny klass", ClassesCB);


	gdi.setOnClearCb(ClassesCB);
	gdi.setRestorePoint(); 

  gdi.setCX(xp);
  gdi.setCY(gdi.getHeight());

  gdi.addString("", 10, "help:26963");
                 
  selectClass(gdi, ClassId);

	EditChanged=false;
	gdi.refresh();

  return true;
}

static int classSettingsCB(gdioutput *gdi, int type, void *data)
{
  static string lastStart = "Start 1";
  if (type==GUI_INPUT) {
		InputInfo ii=*(InputInfo *)data;

    if (ii.id.substr(0,4) == "Strt") {
      lastStart = ii.text;
    }
  }
  else if (type == GUI_FOCUS) {
    InputInfo ii=*(InputInfo *)data;
    if (ii.id.substr(0,4) == "Strt") {
      if (ii.text.empty()) {
        gdi->setText(ii.id, lastStart);
        gdi->setInputFocus(ii.id, true);
      }
    }
  }
  else if (type == GUI_BUTTON) {
    ButtonInfo bi = *(ButtonInfo*)data;
    if (bi.id == "SaveCS") {
      pEvent oe = pEvent(bi.getExtra());
      oe->saveClassSettingsTable(*gdi);
      gdi->sendCtrlMessage("Cancel");
    }
  }
  return 0;
}


void TabClass::prepareForDrawing(gdioutput &gdi)
{
  gdi.clearPage(false);
  gdi.addString("", 2, "Klassinställningar");
  gdi.addString("", 10, "help:59395");
  
  gdi.dropLine();
  oe->getClassSettingsTable(gdi, classSettingsCB);

  gdi.dropLine();
  gdi.fillRight();
  gdi.addButton("SaveCS", "Spara", classSettingsCB).setExtra(LPVOID(oe));
  gdi.addButton("Cancel", "Avbryt", ClassesCB);

  gdi.refresh();
}
