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

// oEvent.cpp: implementation of the oEvent class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include <vector>
#include "oEvent.h"
#include "gdioutput.h"
#include "oDataContainer.h"
#include "csvparser.h"
#include "oFreeImport.h"

#include "random.h"
#include "SportIdent.h"
#include "RunnerDB.h"

#include "meos_util.h"
#include "meos.h"

#include <io.h>
#include <fcntl.h>
#include "localizer.h"

string conv_is(int i)
{
	char bf[256];
//	if(i==0)
//		return "";
	//else
	 if(_itoa_s(i, bf, 10)==0)
		return bf;
	 return "";
}


int ConvertStatusToOE(int i)
{
	switch(i)
	{
	    case StatusOK:
			return 0;	    	
	    case StatusDNS:  // Ej start
			return 1;	    	
	    case StatusDNF:  // Utg.
			return 2;	    	
	    case StatusMP:  // Felst.
			return 3;
	    case StatusDQ: //Disk
			return 4;  	    	
	    case StatusMAX: //Maxtid 
			return 5;
	} 
	return 1;//Ej start...?!
}


string &getFirst(string &inout) {
  int s = inout.size();
  for (int k = 0;k<s; k++) {
    if (inout[k] == ' ' && k>3) {
      inout[k] = 0;
      return inout;
    }
  }
  return inout;
}

/*
	enum {OEstno=0, OEcard=1, OEid=2, OEsurname=3, OEfirstname=4,
			OEbirth=5, OEsex=6, OEstart=9,  OEfinish=10, OEstatus=12,
			OEclubno=13, OEclub=15, OEnat=16, OEclassno=17, OEclass=18,
			OErent=35, OEfee=36, OEpaid=37, OEcourseno=38, OEcourse=39,
			OElength=40};
*/

bool oEvent::exportOECSV(const char *file)
{
	csvparser csv;

	if(!csv.openOutput(file))
		return false;
	
	calculateResults(false);

	oRunnerList::iterator it;

	csv.OutputRow("Startnr;Bricka;Databas nr.;Efternamn;Förnamn;År;K;Block;ut;Start;Mål;Tid;Status;Klubb nr.;Namn;Ort;Land;Klass nr.;Kort;Lång;Num1;Num2;Num3;Text1;Text2;Text3;Adr. namn;Gata;Rad 2;Post nr.;Ort;Tel;Fax;E-post;Id/Club;Hyrd;Startavgift;Betalt;Bana nr.;Bana;km;Hm;Bana kontroller;Pl");
	
	char bf[256];
	for(it=Runners.begin(); it != Runners.end(); ++it){		
		vector<string> row;
		row.resize(44);
		oDataInterface di=it->getDI();

		row[0]=conv_is(it->getId());
		row[1]=conv_is(it->getCardNo());
    row[2]=conv_is(int(it->getExtIdentifier()));
		row[3]=it->getFamilyName();
		row[4]=it->getGivenName();
		row[5]=conv_is(di.getInt("BirthYear") % 100);
		row[6]=di.getString("Sex");
		
		row[9]=it->getStartTimeS();
		if(row[9]=="-") row[9]="";

		row[10]=it->getFinishTimeS();
		if(row[10]=="-") row[10]="";

		row[11]=it->getRunningTimeS(); 
		if(row[11]=="-") row[11]="";

		row[12]=conv_is(ConvertStatusToOE(it->getStatus()));
		row[13]=conv_is(it->getClubId());
		
		row[15]=it->getClub();
		row[16]=di.getString("Nationality");
		row[17]=conv_is(it->getClassId());
		row[18]=it->getClass();
		row[19]=it->getClass(); 

		row[35]=conv_is(di.getInt("CardFee"));
		row[36]=conv_is(di.getInt("Fee"));
		row[37]=conv_is(di.getInt("Paid"));
		
		pCourse pc=it->getCourse();
		if(pc){
			row[38]=conv_is(pc->getId());
			row[39]=pc->getName();
			if(pc->getLength()>0){
				sprintf_s(bf, "%d.%d", pc->getLength()/1000, pc->getLength()%1000);
				row[40]=bf;
			}
			row[41]=conv_is(pc->getDI().getInt("Climb"));

			row[42]=conv_is(pc->nControls);
		}
    row[43] = it->getPlaceS();

		csv.OutputRow(row);
	}

	csv.closeOutput();

	return true;
}


bool oEvent::importXML_EntryData(gdioutput &gdi, const char *file)
{
	xmlparser xml;
	if(xml.read(file)) {

	  xmlobject xo = xml.getObject("EntryList");

	  if (xo) {			
      
      gdi.addString("", 0, "Importerar anmälningar (IOF-XML)");
      gdi.refreshFast();
		  xmlList xl;
      xo.getObjects(xl);
      int ent = 0, fail = 0;
		  xmlList::const_iterator it;

		  for(it=xl.begin(); it != xl.end(); ++it){
			  if(it->is("ClubEntry")){
          xmlList entries;
          //xmlobject xentry=it->getObject("Entry");
				  int ClubId = 0;
          
          xmlobject club = it->getObject("Club");

          if (club) {
            addXMLClub(club, false);
            ClubId = club.getObjectInt("ClubId");
          }
          else
            ClubId = it->getObjectInt("ClubId");
          
          it->getObjects("Entry", entries);          
          for (size_t k = 0; k<entries.size(); k++) {
            if(addXMLEntry(entries[k], ClubId))
              ent++;
            else
              fail++;
          }
			  }
		  }
      gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(ent));
      if (fail>0)
        gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed);
      gdi.dropLine();
      gdi.refreshFast();
	  }

	  xo=xml.getObject("ClassData");

	  if (xo) {
      gdi.addString("", 0, "Importerar klasser (IOF-XML)");
      gdi.refreshFast();
      int imp = 0, fail = 0;

	    xmlList xl;
      xo.getObjects(xl);

		  xmlList::const_iterator it;

		  for (it=xl.begin(); it != xl.end(); ++it) {
			  if (it->is("Class")) {						
				  if (addXMLClass(*it))
            imp++;
          else fail++;
			  }
		  }
      
      gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp));
      if (fail>0)
        gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed);
      gdi.dropLine();
      gdi.refreshFast();
	  }

	  xo=xml.getObject("ClubList");

	  if (xo) {
      gdi.addString("", 0, "Importerar klubbar (IOF-XML)");
      gdi.refreshFast();
      int imp = 0, fail = 0;

		  xmlList xl;
      xo.getObjects(xl);

		  xmlList::const_iterator it;

		  for(it=xl.begin(); it != xl.end(); ++it){
			  if(it->is("Club")){
				  if (addXMLClub(*it, false))
            imp++;
          else
            fail++;
			  }
		  }
      gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp));
      if (fail>0)
        gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed);
      gdi.dropLine();
      gdi.refreshFast();
	  }

	  xo=xml.getObject("RankList");

	  if (xo) {
      gdi.addString("", 0, "Importerar ranking (IOF-XML)");
      gdi.refreshFast();
      int imp = 0, fail = 0;

		  xmlList xl;
      xo.getObjects(xl);

		  xmlList::const_iterator it;

		  for(it=xl.begin(); it != xl.end(); ++it){
			  if(it->is("Competitor")){						
				  if (addXMLRank(*it))
            imp++;
          else
            fail++;
			  }
		  }

      gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp));
      if (fail>0)
        gdi.addString("", 0, "Antal ignorerade: X#" + itos(fail));
      gdi.dropLine();
      gdi.refreshFast();
	  }

	  xo=xml.getObject("CourseData");

	  if (xo) {
      gdi.addString("", 0, "Importerar banor (IOF-XML)");
      gdi.refreshFast();
      int imp = 0, fail = 0;

		  xmlList xl;
      xo.getObjects(xl);

		  xmlList::const_iterator it;

		  for(it=xl.begin(); it != xl.end(); ++it){
			  if(it->is("Course")){						
				  if (addXMLCourse(*it))
            imp++;
          else
            fail++;
			  }
        else if(it->is("Control")){						
          addXMLControl(*it, 0);
        }
        else if(it->is("StartPoint")){						
          addXMLControl(*it, 1);
        }
        else if(it->is("FinishPoint")){						
          addXMLControl(*it, 2);
        }
		  }

      gdi.addString("", 0, "Klart. Antal importerade: X#" + itos(imp));
      if (fail>0)
        gdi.addString("", 0, "Antal misslyckade: X#" + itos(fail)).setColor(colorRed);
      gdi.dropLine();
      gdi.refreshFast();
	  }


    xo=xml.getObject("EventList");

	  if (xo) {
      gdi.addString("", 0, "Importerar tävlingsdata (IOF-XML)");
      gdi.refreshFast();

  	  xmlList xl;
      xo.getObjects(xl);

		  xmlList::const_iterator it;

		  for(it=xl.begin(); it != xl.end(); ++it){
			  if(it->is("Event")){						
				  addXMLEvent(*it);
          gdi.addString("", 0, "Tävlingens namn: X#" + getName());
          gdi.dropLine();
          gdi.refreshFast();
          break;
			  }
		  }
    }
		return true;
	}
	else return false;
}

bool oEvent::addXMLCompetitorDB(const xmlobject &xentry, int clubId)
{
	if(!xentry) return false;
	
	xmlobject person=xentry.getObject("Person");	
	if(!person) return false;

	int pid = person.getObjectInt("PersonId");

	xmlobject pname=person.getObject("PersonName");
	if(!pname) return false;

  int cardno=0;
  string tmp;

	xmlList cards;
  xentry.getObjects("CCard", cards);
  
  for (size_t k= 0; k<cards.size(); k++) {
    xmlobject &card = cards[k];
    if(card) {
      xmlobject psys = card.getObject("PunchingUnitType");
      if (!psys || psys.getObjectString("value", tmp) == "SI") {
        cardno = card.getObjectInt("CCardId");
        break;
      }
    }
  }

  //if(!cardno)
  //  return false;

  string given;
  pname.getObjectString("Given", given);
  getFirst(given);
  string family;
  pname.getObjectString("Family", family);

  if(given.empty() || family.empty())
    return false;

	string name(given+" "+family);

  char sex[2];
	person.getObjectString("sex", sex, 2);
  
  xmlobject bd=person.getObject("BirthDate");
  int birth = bd ? bd.getObjectInt("Date") : 0;
  
	xmlobject nat=person.getObject("Nationality");
	
  char national[4]={0,0,0,0};
  if (nat) {
    xmlobject natId = nat.getObject("CountryId");
    if (natId)
      natId.getObjectString("value", national, 4);
  }

  RunnerDBEntry *rde = runnerDB->getRunnerById(pid);
    
  if (!rde) {
    rde = runnerDB->getRunnerByCard(cardno);
   
    if (rde && rde->getExtId()!=0)
      rde = 0; //Other runner, same card 

    if (!rde)
      rde = runnerDB->addRunner(name.c_str(), pid, clubId, cardno);
  }

  if (rde) {
    rde->setExtId(pid);
    rde->setName(name.c_str());
    rde->clubNo = clubId;
    rde->birthYear = extendYear(birth);
    rde->sex = sex[0];
    memcpy(rde->national, national, 3);
  }
	return true;
}

bool oEvent::addXMLEntry(const xmlobject &xentry, int ClubId)
{
	if(!xentry) return false;

	char bf[256];
	
	xmlobject person = xentry.getObject("Person");	
	if(!person) return false;

	xmlobject pname = person.getObject("PersonName");
	if(!pname) return false;

	int pid = person.getObjectInt("PersonId");

	pRunner r = 0;

	if(pid)
		r=getRunner(pid, 0);
	
	if (!r) {
    if ( pid > 0) {
      oRunner or(this, pid);
		  r = addRunner(or);
    }
    else {
      oRunner or(this);
		  r = addRunner(or);
    }
	}	

  string given, family;
	r->setName(getFirst(pname.getObjectString("Given", given))+" "+pname.getObjectString("Family", family));
  r->setExtIdentifier(pid);

	oDataInterface DI=r->getDI();
  string tmp;

	r->setSex(person.getObjectString("sex", tmp));
	xmlobject bd=person.getObject("BirthDate");

  if(bd) r->setBirthYear(extendYear(bd.getObjectInt("Date")));


  xmlList cards;
  xentry.getObjects("CCard", cards);
  
  for (size_t k= 0; k<cards.size(); k++) {
    xmlobject &card = cards[k];
    if(card) {
      xmlobject psys = card.getObject("PunchingUnitType");
      if (!psys || psys.getObjectString("value", tmp) == "SI") {
        int cardno = card.getObjectInt("CCardId");
        r->setCardNo(cardno, false);
        break;
      }
    }
  }

	xmlobject nat=person.getObject("Nationality");

  if(nat) {
    char national[4];
    xmlobject natId = nat.getObject("CountryId");
    if (natId)
      r->setNationality(natId.getObjectString("value", national, 4));
  }

	xmlobject eclass=xentry.getObject("EntryClass");

	if(eclass){
		int cid = eclass.getObjectInt("ClassId");
    pClass pc = getClass(cid);
    if ( pc == 0 && cid>0) {
		  sprintf_s(bf, "Klass %d", cid);
      oClass cls(this, cid);
      cls.Name = bf;
      pc = addClass(cls);//Create class if not found
    }
    r->Class=pc;
	}

	if (ClubId) {
		pClub club = getClub(ClubId);
    
    if (!club) {
      pClub dbClub = runnerDB->getClub(ClubId);
      if (dbClub) {
        club = addClub(*dbClub);
      }

      if (!club) {
        sprintf_s(bf, "Klubb %d", ClubId);
	      club = addClub(bf, ClubId);
      }
    }
    r->Club = club;
	}

	xmlobject edate=xentry.getObject("EntryDate");

	if(edate) DI.setDate("EntryDate", edate.getObjectString("Date", tmp));

  r->synchronize();
	return true;
}

bool addXMLPerson(const xmlobject &xentry, oWordList &givenNames, 
                                           oWordList &familyNames)
{
	if(!xentry) return false;
	
	xmlobject person=xentry.getObject("Person");	
	if(!person) return false;

	xmlobject pname=person.getObject("PersonName");
	if(!pname) return false;

  char bf[256];
  givenNames.insert(pname.getObjectString("Given", bf, sizeof(bf)));
  familyNames.insert(pname.getObjectString("Family",  bf, sizeof(bf)));

	return true;
}

void importXMLNames(const xmlobject &xml, oWordList &givenNames, 
                                  oWordList &familyNames)
{
  if(!xml)
    return;

	xmlobject xo = xml.getObject("CompetitorList");

	if(xo){			
    xmlList xl;
    xo.getObjects(xl);

		xmlList::const_iterator it;

		for(it=xl.begin(); it != xl.end(); ++it){
			if(it->is("Competitor")){
        addXMLPerson(*it, givenNames, familyNames);					
			}
		}
	}
}

void addXMLClub(const xmlobject *xclub, oWordList &clubs)
{
	if(!xclub)
		return;


	string Name;
  xclub->getObjectString("Name", Name);
	
	if(Name.empty() || !IsCharAlphaNumeric(Name[0]))
		return;

  clubs.insert(Name.c_str());
	return;
}

void importXMLAllClubs(const xmlobject &xml, oWordList &clubs)
{
  if(!xml)
    return;
	xmlobject xo=xml.getObject("ClubList");

	if(xo){			
    xmlList xl;
    xo.getObjects(xl);

		xmlList::const_iterator it;

		for(it=xl.begin(); it != xl.end(); ++it){
			if(it->is("Club")){
        addXMLClub(&*it, clubs);					
			}
		}
	}
}

bool oEvent::importXMLNames(const char *file,
                            oFreeImport &fi, string &info) const
{
  xmlparser xml;
	info.clear();
  DWORD tc=GetTickCount(), t;
  
  if(!xml.read(file)){
    throw std::exception(xml.getError());
	  return false;
  }

  char bf[128];
  t=GetTickCount()-tc;
  sprintf_s(bf, "XML read: %d.%ds ", t/1000, (t/100)%10);
  info+=bf;
  tc=GetTickCount();
  if(xml.getObject("meosdata")) {
    oEvent oNew(gdibase);
    oNew.open(xml);

    fi.init(oNew.Runners, oNew.Clubs, oNew.Classes);
  }
  else {
    ::importXMLNames(xml.getObject("CompetitorList"), fi.givenDB, fi.familyDB);
    ::importXMLAllClubs(xml.getObject("ClubList"), fi.clubDB);
  }

  t=GetTickCount()-tc;
  sprintf_s(bf, "Import: %d.%ds ", t/1000, (t/100)%10);
  info+=bf;

	return true;
}

void oEvent::importXML_IOF_Data(const char *clubfile,
                              const char *competitorfile, bool clear) 
{
  if (clubfile && clubfile[0]) {
    xmlparser xml_club;
    xml_club.setProgress(gdibase.getHWND());
    
    if (clear)
      runnerDB->clearClubs();

    gdibase.addString("",0,"Läser klubbar...");
    gdibase.refresh();

    if(!xml_club.read(clubfile))
      throw std::exception(xml_club.getError());


    gdibase.addString("",0,"Lägger till klubbar...");
    gdibase.refresh();
    
    const xmlobject xo=xml_club.getObject("ClubList");
    xmlList xl;
    if(xo)
      xo.getObjects(xl);


    xmlList::const_iterator it;

    int clubCount = 0;

	  for (it=xl.begin(); it != xl.end(); ++it)
      if (it->is("Club")) {
			  if(addXMLClub(*it, true))
          clubCount++;
      }

      gdibase.addStringUT(0, lang.tl("Antal importerade: ") + itos(clubCount));
  }

  if (competitorfile && competitorfile[0]) {
    xmlparser xml_cmp;
    xml_cmp.setProgress(gdibase.getHWND());
    gdibase.dropLine();
    gdibase.addString("",0,"Läser löpare...");
    gdibase.refresh();
    
    if (!xml_cmp.read(competitorfile))
      throw std::exception(xml_cmp.getError());

    if (clear) {
      runnerDB->clearRunners();
    }


    gdibase.addString("",0,"Lägger till löpare...");
    gdibase.refresh();

	  xmlobject xo=xml_cmp.getObject("CompetitorList");

    xmlList xl;
    if (xo)
      xo.getObjects(xl);

    xmlList::const_iterator it;

    int personCount = 0;
	  for (it=xl.begin(); it != xl.end(); ++it) {
		  if(it->is("Competitor")){
			  int ClubId=it->getObjectInt("ClubId");
        if (addXMLCompetitorDB(*it, ClubId))
          personCount++;
		  }
	  }

    gdibase.addStringUT(0, lang.tl("Antal importerade: ") + itos(personCount));
    gdibase.refresh();

    setProperty("DatabaseUpdate", getRelativeDay());
  }

  saveRunnerDatabase("database", true);
  
  if(HasDBConnection && msUploadRunnerDB) {
    gdibase.addString("", 0, "Uppdaterar serverns databas...");
    gdibase.refresh();

    msUploadRunnerDB(this);
    gdibase.addString("", 0, "Klart");
    gdibase.refresh();
  }
}


/*
 <Class lowAge="1" highAge="18" sex="K" actualForRanking="Y">
    <ClassId type="nat" idManager="SWE">6</ClassId>
    <Name>D18 Elit</Name>
    <ClassShortName>D18 Elit</ClassShortName>
    <ClassTypeId>E</ClassTypeId>
    <SubstituteClass>
      <ClassId> 600 </ClassId> 
    </SubstituteClass>
    <NotQualifiedSubstituteClass> 
      <ClassId> 600 </ClassId> 
    </NotQualifiedSubstituteClass>
    <EntryFee>
      <EntryFeeId> A </EntryFeeId>
      <Name> Adult </Name>
      <Amount currency="SEK"> 65 </Amount> 
    </EntryFee>
    <ModifyDate> 
      <Date> 2001-10-18 </Date> 
    </ModifyDate>
  </Class>
*/

bool oEvent::addXMLEvent(const xmlobject &xevent)
{
	if(!xevent)
		return false;
	
  int id = xevent.getObjectInt("EventId");
  string name;
  xevent.getObjectString("Name", name);
  
	xmlobject date = xevent.getObject("StartDate");

  if (id>0)
    setExtIdentifier(id);

  setName(name);

  if (date) {
    string dateStr;
    date.getObjectString("Date", dateStr);
    setDate(dateStr);
  }

  synchronize();
  return true;
}

/*
<Course>
  <CourseName> Bana 02 </CourseName>
  <CourseId> 1 </CourseId>
  <ClassShortName> D21E </ClassShortName>
  <CourseVariation>
    <CourseVariationId> 0 </CourseVariationId>
    <CourseLength> 8500 </CourseLength>
    <CourseClimb> 0 </CourseClimb>
    <StartPointCode> S1 </StartPointCode>
  
    <CourseControl>
    <Sequence> 1 </Sequence>
    <ControlCode> 104 </ControlCode>
    <LegLength> 310 </LegLength>
    </CourseControl>
    
    <CourseControl>
    <Sequence> 2 </Sequence>
    <ControlCode> 102 </ControlCode>
    <LegLength> 360 </LegLength>
    </CourseControl>
    
    <FinishPointCode> M1 </FinishPointCode>
    <DistanceToFinish> 157 </DistanceToFinish>
  </CourseVariation>
</Course>

*/
int getLength(const xmlobject &xlen) {
  if (xlen.isnull())
    return 0;
  return xlen.getInt();
}

int getStartIndex(int sn) {
  return sn + 211100;
}

int getFinishIndex(int sn) {
  return sn + 311100;
}

string getStartName(const string &start) {
  int num = getNumberSuffix(start);
  if (num == 0 && start.length()>0) 
    num = int(start[start.length()-1])-'0';
  if (num > 0 && num < 30)
    return lang.tl("Start ") + itos(num);
  else
    return start;
}
/*
<Control>
 <ControlCode> 120 </ControlCode>
 <MapPosition x="-1349.6" y="862.7"/>
</Control>
*/
bool oEvent::addXMLControl(const xmlobject &xcontrol, int type)
{
  // type: 0 control, 1 start, 2 finish
	if(!xcontrol)
		return false;
  
  xmlobject pos = xcontrol.getObject("MapPosition");

  int xp = 0, yp = 0;
  if (pos) {
    string x,y;
    pos.getObjectString("x", x);
    pos.getObjectString("y", y);
    xp = int(10.0 * atof(x.c_str()));
    yp = int(10.0 * atof(y.c_str()));
  }

  if (type == 0) {
    int code = xcontrol.getObjectInt("ControlCode");
    if (code>=30 && code<1024) {
      pControl pc = getControl(code, true);
      pc->getDI().setInt("xpos", xp);
      pc->getDI().setInt("ypos", yp);
      pc->synchronize();
    }
  }
  else if (type == 1) {
    string start;
    xcontrol.getObjectString("StartPointCode", start);
    start = getStartName(trim(start));
    int num = getNumberSuffix(start);
    if (num == 0 && start.length()>0) 
      num = int(start[start.length()-1])-'0';    
    pControl pc = getControl(getStartIndex(num), true);
    pc->setNumbers("");
    pc->setName(start);
    pc->setStatus(oControl::StatusStart);
    pc->getDI().setInt("xpos", xp);
    pc->getDI().setInt("ypos", yp);
  }
  else if (type == 2) {
    string finish;
    xcontrol.getObjectString("FinishPointCode", finish);
    finish = trim(finish);
    int num = getNumberSuffix(finish);
    if (num == 0 && finish.length()>0) 
      num = int(finish[finish.length()-1])-'0';
    if (num > 0) 
      finish = lang.tl("Mål ") + itos(num);
    pControl pc = getControl(getFinishIndex(num), true);
    pc->setNumbers("");
    pc->setName(finish);
    pc->setStatus(oControl::StatusFinish);
    pc->getDI().setInt("xpos", xp);
    pc->getDI().setInt("ypos", yp);
  }

  return true;
}

bool oEvent::addXMLCourse(const xmlobject &xcrs)
{
	if(!xcrs)
		return false;
	
  int cid = xcrs.getObjectInt("CourseId");
  
  string name;
  xcrs.getObjectString("CourseName", name);
  name = trim(name);
  vector<string> cls;
  xmlList obj;
  xcrs.getObjects("ClassShortName", obj);
  for (size_t k = 0; k<obj.size(); k++) {
    string c;
    obj[k].getObjectString(0, c);
    cls.push_back(trim(c));
  }
  
  //int clsId = xevent.getObjectInt("ClassId");
  vector<pCourse> courses;
  xcrs.getObjects("CourseVariation", obj);
  for (size_t k = 0; k<obj.size(); k++) {
    int variationId = obj[k].getObjectInt("CourseVariationId");
    //int len = obj[k].getObjectInt("CourseLength");
    xmlobject lenObj = obj[k].getObject("CourseLength");
    int len = getLength(lenObj);
    int climb = getLength(obj[k].getObject("CourseClimb"));
    string start; 
    obj[k].getObjectString("StartPointCode", start);
    start = getStartName(trim(start));    
    
    xmlList ctrl;
    obj[k].getObjects("CourseControl", ctrl);
    vector<int> ctrlCode(ctrl.size());
    vector<int> legLen(ctrl.size());
    for (size_t i = 0; i<ctrl.size(); i++) {
      unsigned seq = ctrl[i].getObjectInt("Sequence");
      int index = i;
      if (seq>0 && seq<=ctrl.size())
        index = seq-1;

      ctrlCode[index] = ctrl[i].getObjectInt("ControlCode");
      legLen[index] = getLength(ctrl[i].getObject("LegLength"));
    }

    legLen.push_back(getLength(obj[k].getObject("DistanceToFinish")));
    int actualId = ((cid+1)*100) + variationId;

    pCourse pc = getCourseCreate(actualId);
    string cname = obj.size() > 1 ? name + itos(k+1) : name;
    pc->setName(cname);
    pc->setLength(len);
    pc->importControls("");
    for (size_t i = 0; i<ctrlCode.size(); i++) {
      if (ctrlCode[i]>30 && ctrlCode[i]<1000)
        pc->addControl(ctrlCode[i]);
    }
    if (pc->getNumControls() + 1 == legLen.size())
      pc->setLegLengths(legLen);
    pc->getDI().setInt("Climb", climb);
    pc->setStart(start, true);
    pc->synchronize();
    
    string finish; 
    obj[k].getObjectString("FinishPointCode", finish);
    finish = trim(finish);
    
    pc->synchronize();
    
    courses.push_back(pc);
  }

  for (size_t k = 0; k<cls.size(); k++) {
    pClass pCls = getClassCreate(0, cls[k]);
    if (pCls) {
      if (courses.size()==1) {
        pCls->setCourse(courses[0]);
      }
      else {
        for (size_t i = 0; i<courses.size(); i++)
          pCls->addStageCourse(0, courses[i]->getId());
      }
    }
  }

  return true;
}


bool oEvent::addXMLClass(const xmlobject &xclass)
{
	if(!xclass)
		return false;
	
	int classid=xclass.getObjectInt("ClassId");
	string Name;
  xclass.getObjectString("Name", Name);

	pClass pc=0;

  if (classid) {
		pc = getClass(classid);

    if (!pc) {
      oClass c(this, classid);
      pc = addClass(c);
    }
  }
  else
    pc = addClass(Name);

	pc->setName(Name);
	oDataInterface DI=pc->getDI();

  string tmp;
	DI.setInt("LowAge", xclass.getObjectInt("lowAge"));
	DI.setInt("HighAge", xclass.getObjectInt("highAge"));
	DI.setString("Sex", xclass.getObjectString("sex", tmp));
	DI.setString("ClassType", xclass.getObjectString("ClassTypeId", tmp));

  pc->synchronize();
	return true;
}


bool oEvent::addXMLClub(const xmlobject &xclub, bool savetoDB)
{
	if(!xclub)
		return false;
	
	int clubid=xclub.getObjectInt("ClubId");
	string Name, shortName;
  xclub.getObjectString("Name", Name);
  xclub.getObjectString("ShortName", shortName);
  
  if (!shortName.empty() && shortName.length() < Name.length())
    swap(Name, shortName);

  int district = xclub.getObjectInt("OrganisationId");

	if(Name.length()==0 || !IsCharAlphaNumeric(Name[0]))
		return false;

	xmlobject address=xclub.getObject("Address");

	if(!address) return false;

  string str;
  string co;
	if(address.getObjectString("street", str).length()==0 &&
     address.getObjectString("careOf", co).length()==0)
		return false;

	pClub pc=0;

  if ( !savetoDB ) {
	  if(clubid)
		  pc = getClubCreate(clubid, Name);

    if(!pc) return false;
  }
  else {
    pc = new oClub(this);
    pc->Id = clubid;
  }

	pc->setName(Name);	

  pc->setExtIdentifier(clubid);

	oDataInterface DI=pc->getDI();

  string tmp;
	DI.setString("CareOf", co);
	DI.setString("Street", str);
	DI.setString("City", address.getObjectString("city", tmp));
	DI.setString("ZIP", address.getObjectString("zipCode", tmp));
  DI.setInt("District", district);

	xmlobject tele=xclub.getObject("Tele");

	if(tele){
		DI.setString("EMail", tele.getObjectString("mailAddress", tmp));
		DI.setString("Phone", tele.getObjectString("phoneNumber", tmp));	
	}

	xmlobject country=xclub.getObject("Country");

  if(country) {
    xmlobject natId = country.getObject("CountryId");
    char national[4];
    if (natId)
      DI.setString("Nationality", natId.getObjectString("value", national, 4));
  }

  if (savetoDB) {
    runnerDB->importClub(*pc, false);
    delete pc;
  }
  else {
    pc->synchronize();
  }

	return true;
}


bool oEvent::addXMLRank(const xmlobject &xrank)
{
	if(!xrank)
		return false;
	
	xmlobject person;//xrank->getObject("Person");
	xmlobject club;//xrank->getObject("Club");
	xmlobject rank;
	xmlobject vrank;

  xmlList x;
  xrank.getObjects(x);
	
	xmlList::const_iterator cit=x.begin();

  string tmp;
	while(cit!=x.end()){

		if(cit->is("Person"))
			person=*cit;
		else if(cit->is("Club"))
			club=*cit;
		else if(cit->is("Rank")){
			if(cit->getObjectString("Name", tmp)=="Swedish Ranking List")
				rank=*cit;
			else if(cit->getObjectString("Name", tmp)=="Swedish Vacancy List")
				vrank=*cit;
		}
		++cit;
	}

	if(!person) return false;

	pRunner r=getRunner(person.getObjectInt("PersonId"), 0);

	if(!r){
		xmlobject pname=person.getObject("PersonName");

		if(!pname) return false;

    string given, family;
		string name=getFirst(pname.getObjectString("Given", given))+" "+pname.getObjectString("Family", family);
		
		if(!club)
			r=getRunnerByName(name);
    else {
      string cn, cns;
      club.getObjectString("ShortName", cns);
      club.getObjectString("Name", cn);
      
      if (cns.empty())
        cns = cn;

      if (!cn.empty() && cn.length()<cns.length())
        swap(cn, cns);

			r=getRunnerByName(name, cns);

    }
	}

	if(!r) return false; //No runner here!

	oDataInterface DI=r->getDI();

	if(rank)
		DI.setInt("Rank", rank.getObjectInt("RankPosition"));

	if(vrank)
		DI.setInt("VacRank", vrank.getObjectInt("RankPosition"));

  r->synchronize();

	return true;
}

void oEvent::exportIOFEventList(xmlparser &xml) 
{
  xml.startTag("EventList");
  xml.write("IOFVersion", "version", "2.0.3");

  exportIOFEvent(xml); 

  xml.endTag(); //EventList
}

void oEvent::exportIOFEvent(xmlparser &xml) 
{
  // (IndSingleDay|IndMultiDay|teamSingleDay|teamMultiDay|relay)
  xml.startTag("Event", "eventForm", "IndSingleDay");
  
  xml.write("EventId", itos(getExtIdentifier()));
  xml.write("Name", getName());

  xml.write("EventClassificationId", "type", "other", "MeOS");

  {
    xml.startTag("StartDate"); 
    xml.write("Date", "dateFormat", "YYYY-MM-DD", getDate());
    xml.write("Clock", "clockFormat", "HH:MM:SS", getZeroTime());
    xml.endTag(); // StartDate
  }

  string url = getDCI().getString("Homepage");
  if (!url.empty())
    xml.write("WebURL", url);

  string account = getDCI().getString("Account");
  if (!account.empty())
    xml.write("Account", "type", "other", account);

  xml.endTag(); //Event
}

void oEvent::exportIOFClass(xmlparser &xml) 
{
  xml.startTag("ClassData");
  
  set<string> cls;
  for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) {
    if (!it->getType().empty())
      cls.insert(it->getType());
  }

  int id = 1;
  map<string, int> idMap;
  for (set<string>::iterator it = cls.begin(); it != cls.end(); ++it) {
    xml.startTag("ClassType");
    idMap[*it] = id;
    xml.write("ClassTypeId", id++);
    xml.write("Name", *it);
    xml.endTag();
  }

  for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) {
    vector<string> pv;
    
    pClass pc = &*it;
    int low = 0;
    int high = 0;
    pc->getAgeLimit(low, high);

    if (low>0) {
      pv.push_back("lowAge");
      pv.push_back(itos(low));
    }
    if (high>0) {
      pv.push_back("highAge");
      pv.push_back(itos(high));
    }

    string sex = pc->getSex();
    if(sex=="K" || sex=="W")
      sex="F";

    if (sex.empty())
      sex = "B";

    pv.push_back("sex");
    pv.push_back(sex);

    if (pc->getNumStages()>1) {
      pv.push_back("numberInTeam");
      pv.push_back(itos(pc->getNumStages()));
    }

    if (pc->getClassType() == oClassRelay) {
      pv.push_back("teamEntry");
      pv.push_back("Y");
    }

    if (pc->getNoTiming()) {
      pv.push_back("timePresentation");
      pv.push_back("N");
    }
      
    xml.startTag("Class", pv);

    xml.write("ClassId", itos(pc->getId()));

    xml.write("ClassShortName", pc->getName());

    if (!pc->getType().empty())
      xml.write("ClassTypeId", itos(idMap[pc->getType()]));

    xml.endTag();
  }
  xml.endTag();
}

void oEvent::exportIOFClublist(xmlparser &xml) 
{
  xml.startTag("ClubList");
  xml.write("IOFVersion", "version", "2.0.3");

  for (oClubList::iterator it = Clubs.begin(); it!=Clubs.end(); ++it) {
    it->exportIOFClub(xml, true);
  }  
  
  xml.endTag();
}

void cWrite(xmlparser &xml, const char *tag, const string &value) {
  if (!value.empty()) {
    xml.write(tag, value);
  }
}

void oClub::exportClubOrId(xmlparser &xml) const
{
  if (getExtIdentifier()>0)
    xml.write("ClubId", itos(getExtIdentifier()));
  else {
    exportIOFClub(xml, true);
  }
}

void oClub::exportIOFClub(xmlparser &xml, bool compact) const
{  
  xml.startTag("Club");
  xml.write("ClubId", itos(getExtIdentifier()));

  xml.write("ShortName", getName());

  if (compact) {
    xml.endTag();
    return;
  }

  string country = getDCI().getString("Nationality");
  if (!country.empty())
    xml.write("CountryId", "value", country);

  int district = getDCI().getInt("District");
  if (district>0)
    xml.write("OrganisationId", itos(district));

  vector<string> pv;
  
  // Address
  string co = getDCI().getString("CareOf");
  string street = getDCI().getString("Street");
  string city = getDCI().getString("City");
  string zip = getDCI().getString("ZIP");
  
  if (!co.empty()) {
    pv.push_back("careOf");
    pv.push_back(co);
  }
  if (!street.empty()) {
    pv.push_back("street");
    pv.push_back(street);
  }
  if (!city.empty()) {
    pv.push_back("city");
    pv.push_back(city);
  }
  if (!zip.empty()) {
    pv.push_back("zipCode");
    pv.push_back(zip);
  }
  if (!pv.empty()) {
    xml.startTag("Address", pv);
    xml.endTag();
  }
  pv.clear();

  //Tele
  string mail = getDCI().getString("EMail");
  string phone = getDCI().getString("Phone");
  
  if (!mail.empty()) {
    pv.push_back("mailAddress");
    pv.push_back(mail);
  }
  if (!phone.empty()) {
    pv.push_back("phoneNumber");
    pv.push_back(phone);
  }
  if (!pv.empty()) {
    xml.startTag("Tele", pv);
    xml.endTag();
  }

  //Club
  xml.endTag();
}

void oEvent::exportIOFStartlist(xmlparser &xml) 
{
  xml.startTag("StartList");
	xml.write("IOFVersion", "version", "2.0.3");

  exportIOFEvent(xml);

  for (oClassList::iterator it = Classes.begin(); it!=Classes.end(); ++it) {
    xml.startTag("ClassStart");
    //xml.write("ClassId", itos(it->getId()));
    xml.write("ClassShortName", it->getName());
    it->exportIOFStart(xml);
    xml.endTag();
  }  
  xml.endTag();
}

void oClass::exportIOFStart(xmlparser &xml) {

  if (getClassType() == oClassIndividual || getClassType() == oClassIndividRelay) {
    for (oRunnerList::iterator it = oe->Runners.begin(); it!=oe->Runners.end(); ++it) {
      if (it->getClassId() != getId())
        continue;
     
      xml.startTag("PersonStart");
      
      it->exportIOFRunner(xml, true);

      if (it->getClubId()>0) 
        it->Club->exportClubOrId(xml);

      int rank = it->getDCI().getInt("Rank");
      if(rank>0) {
        //Ranking
        xml.startTag("Rank");
        xml.write("Name", "MeOS");
        xml.write("RankPosition", itos(rank));
        xml.write("RankValue", itos(rank));
        xml.endTag();
      }

      int multi = it->getNumMulti();
      if (multi==0) 
        it->exportIOFStart(xml);
      else {
        xml.startTag("RaceStart");
        xml.write("EventRaceId", "1");
        it->exportIOFStart(xml);
        xml.endTag();
        for (int k = 0; k < multi; k++) {
          pRunner r = it->getMultiRunner(k+1);
          if (r) {
            xml.startTag("RaceStart");
            xml.write("EventRaceId", itos(k+2));
            r->exportIOFStart(xml);
            xml.endTag();
          }
        }
      }
      xml.endTag();
    }
  }
  else if (getClassType() == oClassRelay || getClassType() == oClassPatrol) {
    for (oTeamList::iterator it = oe->Teams.begin(); it!=oe->Teams.end(); ++it) {
      if (it->getClassId() != getId())
        continue;
      
      xml.startTag("TeamStart");

      xml.write("TeamName", it->getName());

      string nat = it->getDCI().getString("Nationality");
      if (!nat.empty())
        xml.write("CountryId", "value", nat);

      for (size_t k=0; k<it->Runners.size(); k++) {
        if (it->Runners[k]) {
          xml.startTag("PersonStart");
          
          pRunner parent = it->Runners[k]->getMultiRunner(0);
          if (parent != 0)
            parent->exportIOFRunner(xml, true);
            //xml.write("PersonId", itos(parent->getId()));
          if (it->getClubId()>0) {
            it->Club->exportClubOrId(xml);
          }
          
          it->Runners[k]->exportIOFStart(xml);
          xml.endTag();
        }
      }

      xml.endTag();
    }
  }
}

void oRunner::exportIOFStart(xmlparser &xml) 
{
  xml.startTag("Start");
  int sno = getStartNo();
  if (sno>0)
    xml.write("StartNumber", itos(sno));

  xml.startTag("StartTime");
  xml.write("Clock", "clockFormat", "HH:MM:SS", 
              formatTimeIOF(getStartTime(), oe->ZeroTime));
  xml.endTag();

  int bib = getBib();
  if (bib>0)
    xml.write("BibNumber", itos(bib));

  xml.write("CardId", itos(getCardNo()));

  int len = 0;
  if (getCourse()) {
    len = getCourse()->getLength();
    if (len>0)
      xml.write("CourseLength", "unit", "m", itos(len));

    string start = getCourse()->getStart();
    if (!start.empty())
      xml.write("StartId", start);
  }
 
  xml.endTag();
}

void oRunner::exportIOFRunner(xmlparser &xml, bool compact) 
{
  string sex = getSex();
  if(sex=="K" || sex=="W")
    sex="F";

  if (sex.length()==1)
    xml.startTag("Person", "sex", sex);
  else
    xml.startTag("Person");

  xml.write("PersonId", itos(getExtIdentifier()));
	
  xml.startTag("PersonName");
  xml.write("Family", getFamilyName());
	xml.write("Given", "sequence", "1", getGivenName());					
	xml.endTag();

  int year = getBirthYear();

  if (year>0 && !compact) {
    xml.startTag("BirthDate");
    xml.write("Date", "dateFormat", "YYYY", itos(extendYear(year)));
    xml.endTag();
  }

  xml.endTag();
}

void oEvent::exportIOFResults(xmlparser &xml, bool selfContained, const set<int> &classes, int leg)
{
  xml.startTag("ResultList");

	xml.write("IOFVersion", "version", "2.0.3");
  
  exportIOFEvent(xml);

	bool ClassStarted=false;
	int Id=-1;
  bool skipClass=false;

	for (oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) {				
    if (it->isRemoved())
      continue;
		if (it->Runners.size()>=2 && it->Runners[0] && it->Runners[1]) {
			if(it->getClassId()!=Id) {
				if(ClassStarted) xml.endTag();
				
        if(!it->Class || it->Class->getClassType()!=oClassPatrol) {
          skipClass=true;
          ClassStarted=false;
          continue;
        }
        
        if ((!classes.empty() && classes.count(it->getClassId()) == 0) || leg != -1) {
          skipClass=true;
          ClassStarted=false;
          continue;
        }

        skipClass=false;
				xml.startTag("ClassResult");
				ClassStarted=true;
				Id=it->getClassId();

				xml.write("ClassShortName", it->getClass());
			}

      if(skipClass)
        continue;

			xml.startTag("PersonResult");
        it->Runners[0]->exportIOFRunner(xml, true);

				xml.startTag("Club");
					xml.write("ClubId", 0);
          xml.write("ShortName", it->Runners[1]->getName());
					xml.write("CountryId", "value", it->getDI().getString("Nationality"));
				xml.endTag();

				xml.startTag("Result");
					xml.startTag("StartTime");
						xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getStartTime(), ZeroTime));
					xml.endTag();
					xml.startTag("FinishTime");
            xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getLegFinishTime(-1), ZeroTime));
					xml.endTag();
				
					xml.write("Time", "timeFormat", "HH:MM:SS", formatTimeIOF(it->getLegRunningTime(-1), 0));
					xml.write("ResultPosition", it->getLegPlaceS(-1));

					xml.write("CompetitorStatus", "value", it->Runners[0]->getIOFStatusS());
				
          const vector<int> &sp=it->Runners[0]->getSplitTimes();

					pCourse pc=it->Runners[0]->getCourse();
					if(pc)	xml.write("CourseLength", "unit", "m", pc->getLengthS());

					pCourse pcourse=pc;
					if(pcourse && it->getLegStatus(-1)>0 && it->getLegStatus(-1)!=StatusDNS) {
            int no = 1;
            for (int k=0;k<pcourse->nControls;k++)	{
              if (pcourse->Controls[k]->isRogaining())
                continue;
							xml.startTag("SplitTime", "sequence", itos(no++));
							xml.write("ControlCode", pcourse->Controls[k]->Numbers[0]);
							if(unsigned(k)<sp.size() && sp[k]>0)
								xml.write("Time", "clockFormat", "HH:MM:SS", getAbsTime((sp[k]-it->StartTime)-ZeroTime));
							else
							  xml.write("Time", "--:--:--");
	
							xml.endTag();
						}
					}
				xml.endTag();
			xml.endTag();
		}
  }

  if(ClassStarted) {
    xml.endTag();
    ClassStarted = false;
  }

  if (leg == -1)
    exportTeamSplits(xml, classes);

  skipClass=false;
  Id=-1;

	for (oRunnerList::iterator it=Runners.begin(); 
        it != Runners.end(); ++it) {

    if (it->isRemoved() || (leg != -1 && it->tLeg != leg))
      continue;

		if(it->getClassId()!=Id) {
			if(ClassStarted) xml.endTag();
	
      if(!it->Class) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

      ClassType ct = it->Class->getClassType();

      if(leg == -1 && (ct == oClassPatrol || ct ==oClassRelay || ct == oClassIndividRelay) ) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

      if ( (!classes.empty() && classes.count(it->getClassId()) == 0) ) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

			xml.startTag("ClassResult");
			ClassStarted=true;
      skipClass=false;
			Id=it->getClassId();

			xml.write("ClassShortName", it->getClass());
		}
    
    if(skipClass)
      continue;

		xml.startTag("PersonResult");

      it->exportIOFRunner(xml, true);

      if (it->Club)
        it->Club->exportIOFClub(xml, true);

			xml.startTag("Result");
				xml.startTag("StartTime");
        xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getStartTime(), ZeroTime));
				xml.endTag();
				xml.startTag("FinishTime");
        xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getFinishTime(), ZeroTime));
				xml.endTag();
			
        xml.write("Time", "timeFormat", "HH:MM:SS", formatTimeIOF(it->getRunningTime(),0));
				xml.write("ResultPosition", it->getPlaceS());

				xml.write("CompetitorStatus", "value", it->getIOFStatusS());
			
        const vector<int> &sp=it->getSplitTimes();
				pCourse pc=it->getCourse();
				if(pc)	xml.write("CourseLength", "unit", "m", pc->getLengthS());

				pCourse pcourse=it->getCourse();
				if (pcourse && it->getStatus()>0 && it->getStatus()!=StatusDNS) {
          int no = 1;
          for(int k=0;k<pcourse->nControls;k++)	{
            if (pcourse->Controls[k]->isRogaining())
              continue;
						xml.startTag("SplitTime", "sequence", itos(no++));
						xml.write("ControlCode", pcourse->Controls[k]->Numbers[0]);
						if(unsigned(k)<sp.size() && sp[k]>0)
							xml.write("Time", "timeFormat", "HH:MM:SS", getAbsTime((sp[k]-it->StartTime)-ZeroTime));
						else
						  xml.write("Time", "--:--:--");

						xml.endTag();
					}
				}
			xml.endTag();
		xml.endTag();
  }

  if(ClassStarted) {
    xml.endTag();
    ClassStarted = false;
  }
	
  xml.endTag();
}

void oEvent::exportTeamSplits(xmlparser &xml, const set<int> &classes)
{
  bool ClassStarted=false;
	int Id=-1;
  bool skipClass=false;

  sortTeams(ClassResult, -1);
  for(oTeamList::iterator it=Teams.begin(); it != Teams.end(); ++it) {				
    if (it->isRemoved())
      continue;
		if(it->getClassId()!=Id) {
			if(ClassStarted) xml.endTag();
			
      if(!it->Class) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

      ClassType ct = it->Class->getClassType();
      if(ct != oClassRelay && ct != oClassIndividRelay) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

      if (!classes.empty() && classes.count(it->getClassId()) == 0) {
        skipClass=true;
        ClassStarted=false;
        continue;
      }

      skipClass=false;
			xml.startTag("ClassResult");
			ClassStarted=true;
			Id=it->getClassId();

			xml.write("ClassShortName", it->getClass());
		}

    if(skipClass)
      continue;

    xml.startTag("TeamResult"); {
      /*    
    <TeamName>Sundsvalls OK</TeamName>
    <BibNumber></BibNumber>
    <StartTime>
     <Clock clockFormat="HH:MM:SS">10:00:00</Clock>
    </StartTime>
    <FinishTime>
     <Clock clockFormat="HH:MM:SS">11:45:52</Clock>
    </FinishTime>
    <Time>
     <Time timeFormat="HH:MM:SS">01:45:52</Time>
    </Time>
    <ResultPosition>1</ResultPosition>
    <TeamStatus value="OK"></TeamStatus>
      */
      string nat = it->getDCI().getString("Nationality");
      if (!nat.empty())
        xml.write("CountryId", "value", nat);

      xml.write("TeamName", it->getName());
      xml.write("BibNumber", it->getStartNo());

		  xml.startTag("StartTime");
			  xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getStartTime(), ZeroTime));
		  xml.endTag();
		  xml.startTag("FinishTime");
      xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(it->getFinishTime(), ZeroTime));
		  xml.endTag();
		
      xml.write("Time", "timeFormat", "HH:MM:SS", formatTimeIOF(it->getRunningTime(), 0));
		  xml.write("ResultPosition", it->getPlaceS());
		  xml.write("TeamStatus", "value", it->getIOFStatusS());
		
      for (size_t k=0;k<it->Runners.size();k++) {
        if(!it->Runners[k])
          continue;
        pRunner r=it->Runners[k];

        xml.startTag("PersonResult"); {
          
          r->exportIOFRunner(xml, true);

          if (r->Club)
            r->Club->exportIOFClub(xml, true);

          xml.startTag("Result"); {
            xml.write("TeamSequence", k+1);
			      xml.startTag("StartTime");
				      xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(r->getStartTime(), ZeroTime));
			      xml.endTag();
			      xml.startTag("FinishTime");
            xml.write("Clock", "clockFormat", "HH:MM:SS", formatTimeIOF(r->getFinishTime(), ZeroTime));
			      xml.endTag();
    			
            xml.write("Time", "timeFormat", "HH:MM:SS", formatTimeIOF(r->getRunningTime(), 0));
			      xml.write("ResultPosition", r->getPlaceS());

			      xml.write("CompetitorStatus", "value", r->getIOFStatusS());
    			
            const vector<int> &sp=r->getSplitTimes();

			      pCourse pc=r->getCourse();

            if(pc) {
              xml.startTag("CourseVariation");
              xml.write("CourseVariationId", pc->getId());
              xml.write("CourseLength", "unit", "m", pc->getLengthS());
              xml.endTag();
            }
			      pCourse pcourse=pc;
			      if(pcourse && r->getStatus()>0 && r->getStatus()!=StatusDNS) {
              int no = 1;
              for (int k=0;k<pcourse->nControls;k++){
		            if (pcourse->Controls[k]->isRogaining())
                  continue;
					      xml.startTag("SplitTime", "sequence", itos(no++));
					      xml.write("ControlCode", pcourse->Controls[k]->Numbers[0]);
					      if(unsigned(k)<sp.size() && sp[k]>0)
                  xml.write("Time", "clockFormat", "HH:MM:SS", getAbsTime((sp[k]-r->StartTime)-ZeroTime));
					      else
					        xml.write("Time", "--:--:--");

					      xml.endTag();
              } //Loop over splits
			      }
          } xml.endTag();
        } xml.endTag();
      } //Loop over team members
      
    }  xml.endTag();    
  }
}


bool oEvent::exportIOFSplits(const char *file, const set<int> &classes, int leg)
{
	xmlparser xml;

	if(!xml.openOutput(file, false))
    throw std::exception((string("Kan inte öppna: ")+file).c_str());

  reEvaluateAll(true);
	calculateResults(false);
  calculateTeamResults();

  exportIOFResults(xml, true, classes, leg);

	xml.closeOut();
	return true;
}

bool oEvent::exportIOFStartlist(const char *file)
{
	xmlparser xml;

	if(!xml.openOutput(file, false))
    throw std::exception((string("Kan inte öppna: ")+file).c_str());

  exportIOFStartlist(xml);
 
	xml.closeOut();
	return true;
}
