/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2017 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
    Eksoppsvägen 16, SE-75646 UPPSALA, Sweden

************************************************************************/

#include "stdafx.h"
#include "meos_util.h"
#include "oEvent.h"
#include <assert.h>
#include <algorithm>
#include "meosdb/sqltypes.h"
#include "Table.h"
#include "localizer.h"
#include "meosException.h"
#include "gdioutput.h"

oTeam::oTeam(oEvent *poe): oAbstractRunner(poe, false)
{
  Id=oe->getFreeTeamId();
  getDI().initData();
  correctionNeeded = false;
  StartNo=0;
}

oTeam::oTeam(oEvent *poe, int id): oAbstractRunner(poe, true) {
  Id=id;
  oe->qFreeTeamId = max(id, oe->qFreeTeamId);
  getDI().initData();
  correctionNeeded = false;
  StartNo=0;
}

oTeam::~oTeam(void) {
  /*for(unsigned i=0; i<Runners.size(); i++) {
    if (Runners[i] && Runners[i]->tInTeam==this) {
      assert(Runners[i]->tInTeam!=this);
      Runners[i]=0;
    }
  }*/
}

void oTeam::prepareRemove()
{
  for(unsigned i=0; i<Runners.size(); i++)
    setRunnerInternal(i, 0);
}

bool oTeam::write(xmlparser &xml)
{
  if (Removed) return true;

  xml.startTag("Team");
  xml.write("Id", Id);
  xml.write("StartNo", StartNo);
  xml.write("Updated", Modified.getStamp());
  xml.write("Name", sName);
  xml.write("Start", startTime);
  xml.write("Finish", FinishTime);
  xml.write("Status", status);
  xml.write("Runners", getRunners());

  if (Club) xml.write("Club", Club->Id);
  if (Class) xml.write("Class", Class->Id);

  xml.write("InputPoint", inputPoints);
  if (inputStatus != StatusOK)
    xml.write("InputStatus", itos(inputStatus)); //Force write of 0
  xml.write("InputTime", inputTime);
  xml.write("InputPlace", inputPlace);


  getDI().write(xml);
  xml.endTag();

  return true;
}

void oTeam::set(const xmlobject &xo)
{
  xmlList xl;
  xo.getObjects(xl);
  xmlList::const_iterator it;

  for(it=xl.begin(); it != xl.end(); ++it){
    if (it->is("Id")){
      Id=it->getInt();
    }
    else if (it->is("Name")){
      sName=it->getw();
    }
    else if (it->is("StartNo")){
      StartNo=it->getInt();
    }
    else if (it->is("Start")){
      tStartTime = startTime = it->getInt();
    }
    else if (it->is("Finish")){
      FinishTime=it->getInt();
    }
    else if (it->is("Status")) {
      unsigned rawStatus = it->getInt();
      tStatus = status=RunnerStatus(rawStatus < 100u ? rawStatus : 0);
    }
    else if (it->is("Class")){
      Class=oe->getClass(it->getInt());
    }
    else if (it->is("Club")){
      Club=oe->getClub(it->getInt());
    }
    else if (it->is("Runners")){
      vector<int> r;
      decodeRunners(it->getRaw(), r);
      importRunners(r);
    }
    else if (it->is("InputTime")) {
      inputTime = it->getInt();
    }
    else if (it->is("InputStatus")) {
      unsigned rawStatus = it->getInt();
      inputStatus = RunnerStatus(rawStatus < 100u ? rawStatus : 0);
    }
    else if (it->is("InputPoint")) {
      inputPoints = it->getInt();
    }
    else if (it->is("InputPlace")) {
      inputPlace = it->getInt();
    }
    else if (it->is("Updated")){
      Modified.setStamp(it->getRaw());
    }
    else if (it->is("oData")) {
      getDI().set(*it);
    }
  }
}

string oTeam::getRunners() const
{
  string str="";
  char bf[16];
  unsigned m=0;

  for(m=0;m<Runners.size();m++){
    if (Runners[m]){
      sprintf_s(bf, 16, "%d;", Runners[m]->Id);
      str+=bf;
    }
    else str+="0;";
  }
  return str;
}

void oTeam::decodeRunners(const string &rns, vector<int> &rid)
{
  const char *str=rns.c_str();
  rid.clear();

  int n=0;

  while (*str) {
    int cid=atoi(str);

    while(*str && (*str!=';' && *str!=',')) str++;
    if (*str==';'  || *str==',') str++;

    rid.push_back(cid);
    n++;
  }
}

void oTeam::importRunners(const vector<int> &rns)
{
  Runners.resize(rns.size());
  for (size_t n=0;n<rns.size(); n++) {
    if (rns[n]>0)
      Runners[n] = oe->getRunner(rns[n], 0);
    else
      Runners[n] = 0;
  }
}

void oTeam::importRunners(const vector<pRunner> &rns)
{
  // Unlink old runners
  for (size_t k = rns.size(); k<Runners.size(); k++) {
    if (Runners[k] && Runners[k]->tInTeam == this) {
      Runners[k]->tInTeam = 0;
      Runners[k]->tLeg = 0;
    }
  }

  Runners.resize(rns.size());
  for (size_t n=0;n<rns.size(); n++) {
    if (Runners[n] && rns[n] != Runners[n]) {
      if (Runners[n]->tInTeam == this) {
        Runners[n]->tInTeam = 0;
        Runners[n]->tLeg = 0;
      }
    }
    Runners[n] = rns[n];
  }
}


void oEvent::removeTeam(int Id)
{
  oTeamList::iterator it;
  for (it = Teams.begin(); it != Teams.end(); ++it) {
    if (it->getId() == Id) {
      if (HasDBConnection && !it->isRemoved())
        msRemove(&*it);
      dataRevision++;
      it->prepareRemove();
      Teams.erase(it);
      teamById.erase(Id);
      updateTabs();
      return;
    }
  }
}

void oTeam::setRunner(unsigned i, pRunner r, bool sync)
{
  if (i>=Runners.size()) {
    if (i>=0 && i<100)
      Runners.resize(i+1);
    else
      throw std::exception("Bad runner index");
  }

  if (Runners[i]==r)
    return;

  int oldRaceId = 0;
  if (Runners[i]) {
    oldRaceId = Runners[i]->getDCI().getInt("RaceId");
    Runners[i]->getDI().setInt("RaceId", 0);
  }
  setRunnerInternal(i, r);

  if (r) {
    if (tStatus == StatusDNS)
      setStatus(StatusUnknown, true, false);
    r->getDI().setInt("RaceId", oldRaceId);
    r->tInTeam=this;
    r->tLeg=i;
    r->createMultiRunner(true, sync);
  }

  if (Class) {
    int index=1; //Set multi runners
    for (unsigned k=i+1;k<Class->getNumStages(); k++) {
      if (Class->getLegRunner(k)==i) {
        if (r!=0) {
          pRunner mr=r->getMultiRunner(index++);

          if (mr) {
            mr->setName(r->getName(), true);
            mr->synchronize();
            setRunnerInternal(k, mr);
          }
        }
        else setRunnerInternal(k, 0);
      }
    }
  }
}

void oTeam::setRunnerInternal(int k, pRunner r)
{
  if (r==Runners[k]) {
    if (r) {
      r->tInTeam = this;
      r->tLeg = k;
    }
    return;
  }

  if (Runners[k]) {
    assert(Runners[k]->tInTeam == 0 || Runners[k]->tInTeam == this);
    Runners[k]->tInTeam = 0;
    Runners[k]->tLeg = 0;
  }

  // Reset old team
  if (r && r->tInTeam) {
    if (r->tInTeam->Runners[r->tLeg] != 0) {
      r->tInTeam->Runners[r->tLeg] = 0;
      r->tInTeam->updateChanged();
      if (r->tInTeam != this)
        r->tInTeam->synchronize(true);
    }
  }

  Runners[k]=r;

  if (Runners[k]) {
    Runners[k]->tInTeam = this;
    Runners[k]->tLeg = k;
    if (Class && Class->getLegType(k) != LTGroup)
      Runners[k]->setClassId(getClassId(false), false);
  }
  updateChanged();
}

wstring oTeam::getLegFinishTimeS(int leg) const
{
  if (leg==-1)
    leg=Runners.size()-1;

  if (unsigned(leg)<Runners.size() && Runners[leg])
    return Runners[leg]->getFinishTimeS();
  else return L"-";
}

int oTeam::getLegToUse(int leg) const {
  if (Runners.empty())
    return 0;
  if (leg==-1)
    leg=Runners.size()-1;
  int oleg = leg;
  if (Class && !Runners[leg]) {
    LegTypes lt = Class->getLegType(leg);
    while (leg>=0 && (lt == LTParallelOptional || lt == LTExtra || lt == LTIgnore) && !Runners[leg]) {
      if (leg == 0)
        return oleg; //Suitable leg not found
      leg--;
      lt = Class->getLegType(leg);
    }
  }
  return leg;
}

int oTeam::getLegFinishTime(int leg) const
{
  leg = getLegToUse(leg);
  //if (leg==-1)
  //leg=Runners.size()-1;

  if (Class) {
    pClass pc = Class;
    LegTypes lt = pc->getLegType(leg);
    while (leg> 0  && (lt == LTIgnore ||
              (lt == LTExtra && (!Runners[leg] || Runners[leg]->getFinishTime() <= 0)) ) ) {
      leg--;
      lt = pc->getLegType(leg);
    }
  }

  if (unsigned(leg)<Runners.size() && Runners[leg]) {
    int ft = Runners[leg]->getFinishTime();
    if (Class) {
      bool extra = Class->getLegType(leg) == LTExtra ||
                   Class->getLegType(leg+1) == LTExtra; 

      bool par = Class->isParallel(leg) ||
                 Class->isParallel(leg+1); 

      if (extra) {
        ft = 0;
        // Minimum over extra legs
        int ileg = leg;
        while (ileg > 0 && Class->getLegType(ileg) == LTExtra)
          ileg--;

        while (size_t(ileg) < Class->getNumStages()) {
          int ift = 0;
          if (Runners[ileg]) {
            ift = Runners[ileg]->getFinishTimeAdjusted();
          }

          if (ift > 0) {
            if (ft == 0)
              ft = ift;
            else
              ft = min(ft, ift);
          }

          ileg++;
          if (Class->getLegType(ileg) != LTExtra)
            break;
        }
      }
      else if (par) {
        ft = 0;
        // Maximum over parallel legs
        int ileg = leg;
        while (ileg > 0 && Class->isParallel(ileg))
          ileg--;

        while (size_t(ileg) < Class->getNumStages()) {
          int ift = 0;
          if (Runners[ileg]) {
            ift = Runners[ileg]->getFinishTimeAdjusted();
          }

          if (ift > 0) {
            if (ft == 0)
              ft = ift;
            else
              ft = max(ft, ift);
          }

          ileg++;
          if (!Class->isParallel(ileg))
            break;
        }
      }
    }
    return ft;
  }
  else return 0;
}

int oTeam::getRunningTime() const {
  return getLegRunningTime(-1, false);
}

int oTeam::getLegRunningTime(int leg, bool multidayTotal) const {
  return getLegRunningTimeUnadjusted(leg, multidayTotal) + getTimeAdjustment();
}

int oTeam::getLegRestingTime(int leg) const {
  if (!Class)
    return 0;

  int rest = 0;
  int R = min<int>(Runners.size(), leg+1);
  for (int k = 1; k < R; k++) {
    if (Class->getStartType(k) == STHunting && !Class->isParallel(k) &&
        Runners[k] && Runners[k-1]) {
         
      int ft = getLegRunningTimeUnadjusted(k-1, false) + tStartTime;
      int st = Runners[k]->getStartTime();

      if (ft > 0 && st > 0)
        rest += st - ft;      
    }
  }
  return rest;
}
  

int oTeam::getLegRunningTimeUnadjusted(int leg, bool multidayTotal) const {
  leg = getLegToUse(leg);

  int addon = 0;
  if (multidayTotal)
    addon = inputTime;

  if (unsigned(leg)<Runners.size() && Runners[leg]) {
    if (Class) {
      pClass pc=Class;

      LegTypes lt = pc->getLegType(leg);
      LegTypes ltNext = pc->getLegType(leg+1);
      if (ltNext == LTParallel || ltNext == LTParallelOptional || ltNext == LTExtra) // If the following leg is parallel, then so is this.
        lt = ltNext;

      switch(lt) {
        case LTNormal:
          if (Runners[leg]->prelStatusOK()) {
            int dt = leg>0 ? getLegRunningTimeUnadjusted(leg-1, false)+Runners[leg]->getRunningTime():0;
            return addon + max(Runners[leg]->getFinishTimeAdjusted()-(tStartTime + getLegRestingTime(leg)), dt);
          }
          else return 0;
        break;

        case LTParallelOptional:
        case LTParallel: //Take the longest time of this runner and the previous
          if (Runners[leg]->prelStatusOK()) {
            int pt=leg>0 ? getLegRunningTimeUnadjusted(leg-1, false) : 0;
            int rest = getLegRestingTime(leg);
            int finishT = Runners[leg]->getFinishTimeAdjusted();
            return addon + max(finishT-(tStartTime + rest), pt);
          }
          else return 0;
        break;

        case LTExtra: //Take the best time of this runner and the previous
          if (leg==0)
            return addon + max(Runners[leg]->getFinishTime()-tStartTime, 0);
          else {
            int baseLeg = leg;
            while (baseLeg > 0 && pc->getLegType(baseLeg) == LTExtra)
              baseLeg--;
            int baseTime = 0;
            if (baseLeg > 0)
              baseTime = getLegRunningTimeUnadjusted(baseLeg-1, multidayTotal);
            else 
              baseTime = addon;
        
            int cLeg = baseLeg;
            int legTime = 0;
            bool bad = false;
            do {
              if (Runners[cLeg] && Runners[cLeg]->getFinishTime() > 0) {
                int rt = Runners[cLeg]->getRunningTime();
                if (legTime == 0 || rt < legTime) {
                  bad = !Runners[cLeg]->prelStatusOK();
                  legTime = rt;
                }
              }
              cLeg++;
            }
            while (pc->getLegType(cLeg) == LTExtra);

            if (bad || legTime == 0)
              return 0;
            else 
              return baseTime + legTime;
          }
        break;

        case LTSum:
          if (Runners[leg]->prelStatusOK()) {
            if (leg==0)
              return addon + Runners[leg]->getRunningTime();
            else {
              int prev = getLegRunningTimeUnadjusted(leg-1, multidayTotal);
              if (prev == 0)
                return 0;
              else
                return Runners[leg]->getRunningTime() + prev;
            }
          }
          else return 0;

        case LTIgnore:
          if (leg==0)
            return 0;
          else
            return getLegRunningTimeUnadjusted(leg-1, multidayTotal);

        break;

        case LTGroup:
          return 0;
      }
    }
    else {
      int dt=addon + max(Runners[leg]->getFinishTime()-tStartTime, 0);
      int dt2=0;

      if (leg>0)
        dt2=getLegRunningTimeUnadjusted(leg-1, multidayTotal)+Runners[leg]->getRunningTime();

      return max(dt, dt2);
    }
  }
  return 0;
}

wstring oTeam::getLegRunningTimeS(int leg, bool multidayTotal) const
{
  if (leg==-1)
    leg = Runners.size()-1;

  int rt=getLegRunningTime(leg, multidayTotal);
  const wstring &bf = formatTime(rt);
  if (rt>0) {
    if ((unsigned(leg)<Runners.size() && Runners[leg] &&
      Class && Runners[leg]->getStartTime()==Class->getRestartTime(leg)) || getNumShortening(leg)>0)
      return L"*" + bf;
  }
  return bf;
}


RunnerStatus oTeam::getLegStatus(int leg, bool multidayTotal) const
{
  if (leg==-1)
    leg=Runners.size()-1;

  if (unsigned(leg)>=Runners.size())
    return StatusUnknown;

  if (multidayTotal) {
    RunnerStatus s = getLegStatus(leg, false);
    if (s == StatusUnknown && inputStatus != StatusNotCompetiting)
      return StatusUnknown;
    if (inputStatus == StatusUnknown)
      return StatusDNS;
    return max(inputStatus, s);
  }

  //Let the user specify a global team status disqualified.
  if (leg == (Runners.size()-1) && tStatus==StatusDQ)
    return tStatus;

  leg = getLegToUse(leg); // Ignore optional runners

  int s=0;

  if (!Class)
    return StatusUnknown;

  while(leg>0 && Class->getLegType(leg)==LTIgnore)
    leg--;

  for (int i=0;i<=leg;i++) {
    // Ignore runners to be ignored
    while(i<leg && Class->getLegType(i)==LTIgnore)
      i++;

    int st=Runners[i] ? Runners[i]->getStatus(): StatusDNS;
    int bestTime=Runners[i] ? Runners[i]->getFinishTime():0;

    //When Type Extra is used, the runner with the best time
    //is used for change. Then the status of this runner
    //should be carried forward.
    if (Class) while( (i+1) < int(Runners.size()) && Class->getLegType(i+1)==LTExtra) {
      i++;

      if (Runners[i]) {
        if (bestTime==0 || (Runners[i]->getFinishTime()>0 &&
                         Runners[i]->getFinishTime()<bestTime) ) {
          st=Runners[i]->getStatus();
          bestTime = Runners[i]->getFinishTime();
        }
      }
    }

    if (st==0)
      return RunnerStatus(s==StatusOK ? 0 : s);

    s=max(s, st);
  }

  // Allow global status DNS
  if (s==StatusUnknown && tStatus==StatusDNS)
    return tStatus;

  return RunnerStatus(s);
}

const wstring &oTeam::getLegStatusS(int leg, bool multidayTotal) const
{
  return oe->formatStatus(getLegStatus(leg, multidayTotal));
}

int oTeam::getLegPlace(int leg, bool multidayTotal) const {
  if (Class) {
    while(size_t(leg) < Class->legInfo.size() && Class->legInfo[leg].legMethod == LTIgnore)
      leg--;
  }
  if (!multidayTotal)
    return leg>=0 && leg<maxRunnersTeam ? _places[leg].p:_places[Runners.size()-1].p;
  else
    return leg>=0 && leg<maxRunnersTeam ? _places[leg].totalP:_places[Runners.size()-1].totalP;
}


wstring oTeam::getLegPlaceS(int leg, bool multidayTotal) const
{
  int p=getLegPlace(leg, multidayTotal);
  wchar_t bf[16];
  if (p>0 && p<10000){
    swprintf_s(bf, L"%d", p);
    return bf;
  }
  return _EmptyWString;
}

wstring oTeam::getLegPrintPlaceS(int leg, bool multidayTotal, bool withDot) const
{
  int p=getLegPlace(leg, multidayTotal);
  wchar_t bf[16];
  if (p>0 && p<10000){
    if (withDot) {
      swprintf_s(bf, L"%d.", p);
      return bf;
    }
    else
      return itow(p);
  }
  return _EmptyWString;
}

bool oTeam::compareResult(const oTeam &a, const oTeam &b)
{
  if (a.Class != b.Class) {
    if (a.Class) {
      if (b.Class) return a.Class->tSortIndex < b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id);
      else return true;
    }
    else return false;
  }
  else if (a._sortStatus!=b._sortStatus)
    return a._sortStatus<b._sortStatus;
  else if (a._sortTime!=b._sortTime)
    return a._sortTime<b._sortTime;

  const wstring &as = a.getBib();
  const wstring &bs = b.getBib();
  if (as != bs) {
    return compareBib(as, bs);
  }

  int aix = a.getDCI().getInt("SortIndex");
  int bix = b.getDCI().getInt("SortIndex");
  if (aix != bix)
    return aix < bix;

  return CompareString(LOCALE_USER_DEFAULT, 0,
                       a.sName.c_str(), a.sName.length(),
                       b.sName.c_str(), b.sName.length()) == CSTR_LESS_THAN;
}

bool oTeam::compareStartTime(const oTeam &a, const oTeam &b)
{
  if (a.Class != b.Class) {
    if (a.Class) {
      if (b.Class)
        return a.Class->tSortIndex<b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id);
      else return true;
    }
  }
  else if (a.tStartTime != b.tStartTime)
    return a.tStartTime < b.tStartTime;

  const wstring &as = a.getBib();
  const wstring &bs = b.getBib();
  if (as != bs) {
    return compareBib(as, bs);
  }

  int aix = a.getDCI().getInt("SortIndex");
  int bix = b.getDCI().getInt("SortIndex");
  if (aix != bix)
    return aix < bix;

  return CompareString(LOCALE_USER_DEFAULT, 0,
                       a.sName.c_str(), a.sName.length(),
                       b.sName.c_str(), b.sName.length()) == CSTR_LESS_THAN;
}

bool oTeam::compareSNO(const oTeam &a, const oTeam &b) {
  const wstring &as = a.getBib();
  const wstring &bs = b.getBib();

  if (as != bs) {
    return compareBib(as, bs);
  }
  else if (a.Class != b.Class) {
    if (a.Class) {
      if (b.Class) 
        return a.Class->tSortIndex < b.Class->tSortIndex || (a.Class->tSortIndex == b.Class->tSortIndex && a.Class->Id < b.Class->Id);
      else return true;
    }
  }

  return CompareString(LOCALE_USER_DEFAULT, 0,
                       a.sName.c_str(), a.sName.length(),
                       b.sName.c_str(), b.sName.length()) == CSTR_LESS_THAN;
}


bool oTeam::isRunnerUsed(int Id) const
{
  for(unsigned i=0;i<Runners.size(); i++) {
    if (Runners[i] && Runners[i]->Id==Id)
      return true;
  }
  return false;
}

void oTeam::setTeamNoStart(bool dns, RunnerStatus dnsStatus)
{
  if (dns) {
    assert(dnsStatus == StatusCANCEL || dnsStatus == StatusDNS);
    setStatus(dnsStatus, true, false);
    for(unsigned i=0;i<Runners.size(); i++) {
      if (Runners[i] && Runners[i]->getStatus()==StatusUnknown) {
        Runners[i]->setStatus(dnsStatus, true, false);
      }
    }
  }
  else {
    setStatus(StatusUnknown, true, false);
    for(unsigned i=0;i<Runners.size(); i++) {
      if (Runners[i] && (Runners[i]->getStatus()==StatusDNS || Runners[i]->getStatus() == StatusCANCEL)) {
        Runners[i]->setStatus(StatusUnknown, true, false);
      }
    }
  }
  // Do not sync here
}

static void compressStartTimes(vector<int> &availableStartTimes, int finishTime)
{
  for (size_t j=0; j<availableStartTimes.size(); j++)
    finishTime = max(finishTime, availableStartTimes[j]);

  availableStartTimes.resize(1);
  availableStartTimes[0] = finishTime;
}

static void addStartTime(vector<int> &availableStartTimes, int finishTime)
{
  for (size_t k=0; k<availableStartTimes.size(); k++)
    if (finishTime >= availableStartTimes[k]) {
      availableStartTimes.insert(availableStartTimes.begin()+k, finishTime);
      return;
    }

  availableStartTimes.push_back(finishTime);
}

static int getBestStartTime(vector<int> &availableStartTimes) {
  if (availableStartTimes.empty())
    return 0;
  int t = availableStartTimes.back();
  availableStartTimes.pop_back();
  return t;
}

void oTeam::quickApply() {
  if (unsigned(status) >= 100)
    status = StatusUnknown; // Enforce valid
  
  if (Class && Runners.size()!=size_t(Class->getNumStages())) {
    for (size_t k = Class->getNumStages(); k < Runners.size(); k++) {
      if (Runners[k] && Runners[k]->tInTeam) {
        Runners[k]->tInTeam = 0;
        Runners[k]->tLeg = 0;
        Runners[k]->tLegEquClass = 0;
        if (Runners[k]->Class == Class)
          Runners[k]->Class = 0;
        Runners[k]->updateChanged();
      }
    }
    Runners.resize(Class->getNumStages()); 
  }

  for (size_t i = 0; i < Runners.size(); i++) {
    if (Runners[i]) {
      if (Runners[i]->tInTeam && Runners[i]->tInTeam!=this) {
        Runners[i]->tInTeam->correctRemove(Runners[i]);
      }
      Runners[i]->tInTeam=this;
      Runners[i]->tLeg=i;
    }
  }
}



bool oTeam::apply(bool sync, pRunner source, bool setTmpOnly) {
  if (unsigned(status) >= 100)
    status = StatusUnknown; // Enforce correct status
  
  int lastStartTime = 0;
  RunnerStatus lastStatus = StatusUnknown;
  bool freeStart = Class ? Class->hasFreeStart() : false;
  int extraFinishTime = -1;

  if (Class && Runners.size()!=size_t(Class->getNumStages())) {
    for (size_t k = Class->getNumStages(); k < Runners.size(); k++) {
      if (Runners[k] && Runners[k]->tInTeam) {
        Runners[k]->tInTeam = 0;
        Runners[k]->tLeg = 0;
        Runners[k]->tLegEquClass = 0;
        if (Runners[k]->Class == Class)
          Runners[k]->Class = 0;
        Runners[k]->updateChanged();
      }
    }
    Runners.resize(Class->getNumStages());
  }
  const wstring &bib = getBib();
  BibMode bibMode = (BibMode)-1;
  tNumRestarts = 0;
  vector<int> availableStartTimes;
  for (size_t i=0;i<Runners.size(); i++) {
    if (!sync && i>0 && source!=0 && Runners[i-1] == source)
      return true;
    if (!Runners[i] && Class) {
       unsigned lr = Class->getLegRunner(i);

       if (lr<i && Runners[lr]) {
         Runners[lr]->createMultiRunner(false, sync);
         int dup=Class->getLegRunnerIndex(i);
         Runners[i]=Runners[lr]->getMultiRunner(dup);
       }
    }

    if (sync && Runners[i] && Class) {
      unsigned lr = Class->getLegRunner(i);
      if (lr == i && Runners[i]->tParentRunner) {
        pRunner parent = Runners[i]->tParentRunner;
        for (size_t kk = 0; kk<parent->multiRunner.size(); ++kk) {
          if (parent->multiRunner[kk] == Runners[i]) {
            parent->multiRunner.erase(parent->multiRunner.begin() + kk);
            Runners[i]->tParentRunner = 0;
            Runners[i]->tDuplicateLeg = 0;
            parent->markForCorrection();
            parent->updateChanged();
            Runners[i]->markForCorrection();
            Runners[i]->updateChanged();
            break;
          }
        }
      }
    }
    // Avoid duplicates, same runner running more
    // than one leg
    //(note: quadric complexity, assume total runner count is low)
    if (Runners[i]) {
      for (size_t k=0;k<i; k++)
        if (Runners[i] == Runners[k])
          Runners[i] = 0;
    }

    if (Runners[i]) {
      pClass actualClass = Runners[i]->getClassRef(true);
      if (Runners[i]->tInTeam && Runners[i]->tInTeam!=this) {
        Runners[i]->tInTeam->correctRemove(Runners[i]);
      }
      //assert(Runners[i]->tInTeam==0 || Runners[i]->tInTeam==this);
      Runners[i]->tInTeam = this;
      Runners[i]->tLeg = i;
      if (Class) {
        int unused;
        Class->splitLegNumberParallel(i, Runners[i]->tLegEquClass, unused);
      }
      else {
        Runners[i]->tLegEquClass = i;
      }

      if (actualClass == Class)
        Runners[i]->setStartNo(StartNo, setTmpOnly);
      if (!bib.empty() && Runners[i]->isChanged()) {
        if (bibMode == -1 && Class)
          bibMode = Class->getBibMode();
        if (bibMode == BibSame)
          Runners[i]->setBib(bib, 0, false, setTmpOnly);
        else if (bibMode == BibAdd) {
          wchar_t pattern[32], bf[32];
          int ibib = oClass::extractBibPattern(bib, pattern) + i;
          swprintf_s(bf, pattern, ibib);
          Runners[i]->setBib(bf, 0, false, setTmpOnly);
        }
        else if (bibMode == BibLeg) {
          wstring rbib = bib + L"-" + Class->getLegNumber(i);
          Runners[i]->setBib(rbib, 0, false, setTmpOnly);
        }
      }
      LegTypes legType = Class ? Class->getLegType(i) : LTIgnore;

      if (Runners[i]->Class!=Class && legType != LTGroup) {
        Runners[i]->Class=Class;
        Runners[i]->updateChanged();
      }

      Runners[i]->tNeedNoCard=false;
      if (Class) {
        pClass pc=Class;

        //Ignored runners need no SI-card (used by SI assign function)
        if (legType == LTIgnore) {
          Runners[i]->tNeedNoCard=true;
          if (lastStatus != StatusUnknown) {
            Runners[i]->setStatus(max(Runners[i]->tStatus, lastStatus), false, setTmpOnly);
          }
        }
        else
          lastStatus = Runners[i]->getStatus();

        StartTypes st = actualClass == pc ? pc->getStartType(i) : actualClass->getStartType(0);
        LegTypes lt = legType;

        if ((lt==LTParallel || lt==LTParallelOptional) && i==0) {
          pc->setLegType(0, LTNormal);
          throw std::exception("Första sträckan kan inte vara parallell.");
        }
        if (lt==LTIgnore || lt==LTExtra) {
          if (st != STDrawn)
            Runners[i]->setStartTime(lastStartTime, false, setTmpOnly);
          Runners[i]->tUseStartPunch = (st == STDrawn);
        }
        else { //Calculate start time.
          switch (st) {
            case STDrawn: //Do nothing
              if (lt==LTParallel || lt==LTParallelOptional) {
                Runners[i]->setStartTime(lastStartTime, false, setTmpOnly);
                Runners[i]->tUseStartPunch=false;
              }
              else
                lastStartTime = Runners[i]->getStartTime();

              break;

            case STTime: {
              bool prs = false;
              if (Runners[i] && Runners[i]->Card && freeStart) {
                pCourse crs = Runners[i]->getCourse(false);
                int startType = crs ? crs->getStartPunchType() : oPunch::PunchStart;
                oPunch *pnc = Runners[i]->Card->getPunchByType(startType);
                if (pnc && pnc->getAdjustedTime() > 0) {
                  prs = true;
                  lastStartTime = pnc->getAdjustedTime();
                }
              }
              if (!prs) {
                if (lt == LTNormal || lt == LTSum || lt == LTGroup) {
                  if (actualClass == pc)
                    lastStartTime = pc->getStartData(i);
                  else
                    lastStartTime = actualClass->getStartData(0); // Qualification/final classes
                }
                Runners[i]->setStartTime(lastStartTime, false, setTmpOnly);
                Runners[i]->tUseStartPunch=false;
              }
            }
            break;

            case STChange: {
              int probeIndex = 1;
              int startData = pc->getStartData(i);

              if (startData < 0) {
                // A specified leg
                probeIndex = -startData;
              }
              else {
                // Allow for empty slots when ignore/extra
                while ((i-probeIndex)>=0 && !Runners[i-probeIndex]) {
                  LegTypes tlt = pc->getLegType(i-probeIndex);
                  if (tlt == LTIgnore || tlt==LTExtra || tlt == LTGroup)
                    probeIndex++;
                  else
                    break;
                }
              }

              if ( (i-probeIndex)>=0 && Runners[i-probeIndex]) {
                int z = i-probeIndex;
                LegTypes tlt = pc->getLegType(z);
                int ft = 0;
                if (availableStartTimes.empty() || startData < 0) {

                  if (!availableStartTimes.empty()) {
                    // Parallel, but there is a specification. Take one from parallel anyway.
                    ft = getBestStartTime(availableStartTimes);
                  }

                  //We are not involved in parallel legs
                  ft = (tlt != LTIgnore) ? Runners[z]->getFinishTime() : 0;

                  // Take the best time for extra runners
                  while (z>0 && (tlt==LTExtra || tlt==LTIgnore)) {
                    tlt = pc->getLegType(--z);
                    if (Runners[z] && Runners[z]->getStatus() == StatusOK) {
                      int tft = Runners[z]->getFinishTime();
                      if (tft>0 && tlt != LTIgnore)
                        ft = ft>0 ? min(tft, ft) : tft;
                    }
                  }
                }
                else {
                  ft = getBestStartTime(availableStartTimes);
                }

                if (ft<=0)
                  ft=0;

                int restart=pc->getRestartTime(i);
                int rope=pc->getRopeTime(i);

                if ((restart>0 && rope>0 && (ft==0 || ft>rope)) || (ft == 0 && restart>0)) {
                  ft=restart; //Runner in restart
                  tNumRestarts++;
                }

                if (ft > 0)
                  Runners[i]->setStartTime(ft, false, setTmpOnly);
                Runners[i]->tUseStartPunch=false;
                lastStartTime=ft;
              }
              else {//The else below should only be run by mistake (for an incomplete team)
                Runners[i]->setStartTime(Class->getRestartTime(i), false, setTmpOnly);
                Runners[i]->tUseStartPunch=false;
              }
            }
            break;

            case STHunting: {
              bool setStart = false;
              if (i>0 && Runners[i-1]) {
                if (lt == LTNormal || lt == LTSum || availableStartTimes.empty()) {
                  int rt = getLegRunningTimeUnadjusted(i-1, false);
                
                  if (rt>0)
                    setStart = true;
                  int leaderTime = pc->getTotalLegLeaderTime(i-1, false);
                  int timeAfter = leaderTime > 0 ? rt - leaderTime : 0;

                  if (rt>0 && timeAfter>=0)
                    lastStartTime=pc->getStartData(i)+timeAfter;

                  int restart=pc->getRestartTime(i);
                  int rope=pc->getRopeTime(i);

                  RunnerStatus hst = getLegStatus(i-1, false);
                  if (hst != StatusUnknown && hst != StatusOK) {
                    setStart = true;
                    lastStartTime = restart;
                  }

                  if (restart>0 && rope>0 && (lastStartTime>rope)) {
                    lastStartTime=restart; //Runner in restart
                    tNumRestarts++;
                  }
                  if (!availableStartTimes.empty()) {
                    // Single -> to parallel pursuit
                    if (setStart)
                      fill(availableStartTimes.begin(), availableStartTimes.end(), lastStartTime);
                    else
                      fill(availableStartTimes.begin(), availableStartTimes.end(), 0);

                    availableStartTimes.pop_back(); // Used one
                  }
                }
                else if (lt == LTParallel || lt == LTParallelOptional) {
                  lastStartTime = getBestStartTime(availableStartTimes);
                  setStart = true;
                }

                if (Runners[i]->getFinishTime()>0) {
                  setStart = true;
                  if (lastStartTime == 0)
                    lastStartTime = pc->getRestartTime(i);
                }
                if (!setStart)
                  lastStartTime=0;
              }
              else
                lastStartTime=0;

              Runners[i]->tUseStartPunch=false;
              Runners[i]->setStartTime(lastStartTime, false, setTmpOnly);
            }
            break;
          }
        }

        size_t nextNonPar = i+1;
        while (nextNonPar < Runners.size() && pc->isOptional(nextNonPar) && Runners[nextNonPar] == 0)
          nextNonPar++;
        
        int nextBaseLeg = nextNonPar;
        while (nextNonPar < Runners.size() && pc->isParallel(nextNonPar))
          nextNonPar++;

        // Extra finish time is used to split extra legs to parallel legs
        if (lt == LTExtra || pc->getLegType(i+1) == LTExtra) {
          if (Runners[i]->getFinishTime()>0) {
            if (extraFinishTime <= 0)
              extraFinishTime =  Runners[i]->getFinishTime();
            else
              extraFinishTime = min(extraFinishTime, Runners[i]->getFinishTime());
          }
        }
        else
          extraFinishTime = -1;

        //Add available start times for parallel
        if (nextNonPar < Runners.size()) {
          st=pc->getStartType(nextNonPar);
          int finishTime = Runners[i]->getFinishTime();
          if (lt == LTExtra)
            finishTime = extraFinishTime;

          if (st==STDrawn || st==STTime)
            availableStartTimes.clear();
          else if (finishTime>0) {
            int nRCurrent = pc->getNumParallel(i);
            int nRNext = pc->getNumParallel(nextBaseLeg);
            if (nRCurrent>1 || nRNext>1) {
              if (nRCurrent<nRNext) {
                // Going from single leg to parallel legs
                for (int j=0; j<nRNext/nRCurrent; j++)
                  availableStartTimes.push_back(finishTime);
              }
              else if (nRNext==1)
                compressStartTimes(availableStartTimes, finishTime);
              else
                addStartTime(availableStartTimes, finishTime);
            }
            else
              availableStartTimes.clear();
          }
        }
      }
      if (sync)
        Runners[i]->synchronize(true);
    }
  }

  if (!Runners.empty() && Runners[0]) {
    if (setTmpOnly)
      setStartTime(Runners[0]->tmpStore.startTime, false, setTmpOnly);
    else
      setStartTime(Runners[0]->getStartTime(), false, setTmpOnly);
  }
  else if (Class && Class->getStartType(0) != STDrawn)
    setStartTime(Class->getStartData(0), false, setTmpOnly);

  setFinishTime(getLegFinishTime(-1));
  setStatus(getLegStatus(-1, false), false, setTmpOnly); //XXX Maybe getLegStatus needs to work agains tmp store?

  if (sync)
    synchronize(true);
  return true;
}

void oTeam::evaluate(bool sync)
{
  for(unsigned i=0;i<Runners.size(); i++) {
    if (Runners[i])
      Runners[i]->resetTmpStore();
  }
  resetTmpStore();

  apply(false, 0, true);
  vector<int> mp;
  for(unsigned i=0;i<Runners.size(); i++) {
    if (Runners[i])
      Runners[i]->evaluateCard(false, mp);
  }
  apply(false, 0, true);

  for(unsigned i=0;i<Runners.size(); i++) {
    if (Runners[i]) {
      Runners[i]->setTmpStore();
      if (sync)
        Runners[i]->synchronize(true);
    }
  }
  setTmpStore();
  if (sync)
    synchronize(true);
}

void oTeam::correctRemove(pRunner r) {
  for(unsigned i=0;i<Runners.size(); i++)
    if (r!=0 && Runners[i]==r) {
      Runners[i] = 0;
      r->tInTeam = 0;
      r->tLeg = 0;
      r->tLegEquClass = 0;
      correctionNeeded = true;
      r->correctionNeeded = true;
    }
}

void oTeam::speakerLegInfo(int leg, int specifiedLeg, int courseControlId,
                           int &missingLeg, int &totalLeg, 
                           RunnerStatus &status, int &runningTime) const {
  missingLeg = 0;
  totalLeg = 0;
  bool extra = false, firstExtra = true;
  for (int i = leg; i <= specifiedLeg; i++) {
    LegTypes lt=Class->getLegType(i);
    if (lt == LTExtra)
      extra = true;

    if (lt == LTSum || lt == LTNormal || lt == LTParallel || lt == LTParallelOptional || lt == LTExtra) {
      int lrt = 0;
      RunnerStatus lst = StatusUnknown;
      if (Runners[i]) {
        Runners[i]->getSplitTime(courseControlId, lst, lrt);
        totalLeg++;
      }
      else if (lt == LTParallelOptional) {
        lst = StatusOK; // No requrement
      }
      else if (!extra) {
        totalLeg++; // will never reach...
      }

      if (extra) {
        // Extra legs, take best result
        if (firstExtra && i > 0 && !Runners[i-1]) {
          missingLeg = 0;
          totalLeg = 0;
        }
        firstExtra = false;
        if (lrt>0 && (lrt < runningTime || runningTime == 0)) {
          runningTime = lrt;
          status = lst; // Take status/time from best runner
        }
        if (Runners[i] && lst == StatusUnknown)
          missingLeg++;
      }
      else {
        if (lst > StatusOK) {
          status = lst;
          break;
        }
        else if (lst == StatusUnknown) {
          missingLeg++;
        }
        else {
          runningTime = max(lrt, runningTime);
        }
      }
    }
  }

  if (missingLeg == 0 && status == StatusUnknown)
    status = StatusOK;
}

void oTeam::fillSpeakerObject(int leg, int courseControlId, int previousControlCourseId,
                              bool totalResult, oSpeakerObject &spk) const {
  if (leg==-1)
    leg = Runners.size()-1;

  spk.club = getName();
  spk.missingStartTime = true;
  //Defaults (if early return)

  if (totalResult && inputStatus > StatusOK)
    spk.status = spk.finishStatus = inputStatus;
  else
    spk.status = spk.finishStatus = StatusUnknown;

  if (!Class || unsigned(leg) >= Runners.size())
    return;

  // Ignore initial optional and not used legs
  while (leg > 0 && !Runners[leg]) {
    Class->isOptional(leg);
    leg--;
  }

  if (!Runners[leg])
    return;

  // Get many names for paralell legs
  int firstLeg = leg;
  int requestedLeg = leg;
  LegTypes lt=Class->getLegType(firstLeg--);
  while(firstLeg>=0 && (lt==LTIgnore || lt==LTParallel || lt==LTParallelOptional || lt==LTExtra) )
    lt=Class->getLegType(firstLeg--);

  spk.names.clear();
  for(int k=firstLeg+1;k<=leg;k++) {
    if (Runners[k]) {
      const wstring &n = Runners[k]->getName();
      spk.names.push_back(n);
    }
  }
  // Add start number
  spk.bib = getBib();

  if (courseControlId == 2) {
    unsigned nextLeg = leg + 1;
    while (nextLeg < Runners.size()) {
      if (Runners[nextLeg])
        spk.outgoingnames.push_back(Runners[nextLeg]->getName());

      nextLeg++;
      if (nextLeg < Runners.size()) {
        LegTypes lt=Class->getLegType(nextLeg);
        if (!(lt==LTIgnore || lt==LTParallel || lt==LTParallelOptional || lt==LTExtra))
          break;
      }
    }
  }

  int specifiedLeg = leg;
  leg = firstLeg+1; //This does not work well with parallel or extra...
  while (!Runners[leg]) // Ensure the leg is set
    leg++;
  
  int missingLeg = 0;

  int timeOffset=0;
  RunnerStatus inheritStatus = StatusUnknown;
  if (firstLeg>=0) {
    timeOffset = getLegRunningTime(firstLeg, totalResult);
    inheritStatus = getLegStatus(firstLeg, totalResult);
  }
  else if (totalResult) {
    timeOffset = getInputTime();
    inheritStatus = getInputStatus();
  }

  speakerLegInfo(leg, specifiedLeg, courseControlId,
                  missingLeg, spk.runnersTotalLeg, spk.status, spk.runningTimeLeg.time);

  spk.runnersFinishedLeg = spk.runnersTotalLeg - missingLeg;
  if (spk.runnersTotalLeg > 1) {
    spk.parallelScore = (spk.runnersFinishedLeg * 100) / spk.runnersTotalLeg;
  }
  if (previousControlCourseId > 0 && spk.status <= 1 && Class->getStartType(0) == STTime) {
    spk.useSinceLast = true;
    RunnerStatus pStat = StatusUnknown;
    int lastTime = 0;
    int dummy;
    speakerLegInfo(leg, specifiedLeg, previousControlCourseId,
                   missingLeg, dummy, pStat, lastTime);

    if (pStat == StatusOK) {
      if (spk.runningTimeLeg.time > 0) {
        spk.runningTimeSinceLast.time = spk.runningTimeLeg.time - lastTime;
        spk.runningTimeSinceLast.preliminary = spk.runningTimeSinceLast.time;
      }
      else if (spk.runningTimeLeg.time == 0) {
        spk.runningTimeSinceLast.preliminary = oe->getComputerTime() - lastTime;
        //string db = Name + " " + itos(lastTime) + " " + itos(spk.runningTimeSinceLast.preliminary) +"\n";
        //OutputDebugString(db.c_str());
      }
    }
  }

  spk.timeSinceChange = oe->getComputerTime() - (spk.runningTimeLeg.time + Runners[leg]->tStartTime);

  spk.owner=Runners[leg];
  spk.finishStatus=getLegStatus(specifiedLeg, totalResult);

  spk.missingStartTime = false;
  int stMax = 0;
  for (int i = leg; i <= requestedLeg; i++) {
    if (!Runners[i])
      continue;
    if (Class->getLegType(i) == LTIgnore || Class->getLegType(i) == LTGroup)
      continue;
    
    int st=Runners[i]->getStartTime();
    if (st <= 0)
      spk.missingStartTime = true;
    else {
      if (st > stMax) {
        spk.startTimeS = Runners[i]->getStartTimeCompact();
        stMax = st;
      }
    }
  }
  
  map<int, int>::iterator mapit=Runners[leg]->priority.find(courseControlId);
  if (mapit!=Runners[leg]->priority.end())
    spk.priority=mapit->second;
  else
    spk.priority=0;

  spk.runningTimeLeg.preliminary = 0;
  for (int i = leg; i <= requestedLeg; i++) {
    if (!Runners[i])
      continue;
    int pt = Runners[i]->getPrelRunningTime();
    if (Class->getLegType(i) == LTParallel)
      spk.runningTimeLeg.preliminary = max(spk.runningTimeLeg.preliminary, pt);
    else if (Class->getLegType(i) == LTExtra) {
      if (spk.runningTimeLeg.preliminary == 0)
        spk.runningTimeLeg.preliminary = pt;
      else if (pt > 0)
        spk.runningTimeLeg.preliminary = min(spk.runningTimeLeg.preliminary, pt);
    }
    else
      spk.runningTimeLeg.preliminary = pt;
  }

  if (inheritStatus>StatusOK)
    spk.status=inheritStatus;

  if (spk.status==StatusOK) {
    if (courseControlId == 2)
      spk.runningTime.time = getLegRunningTime(requestedLeg, totalResult); // Get official time

    if (spk.runningTime.time == 0)
      spk.runningTime.time = spk.runningTimeLeg.time + timeOffset; //Preliminary time

    spk.runningTime.preliminary = spk.runningTime.time;
    spk.runningTimeLeg.preliminary = spk.runningTimeLeg.time;
  }
  else if (spk.status==StatusUnknown && spk.finishStatus==StatusUnknown) {
    spk.runningTime.time = spk.runningTimeLeg.preliminary + timeOffset;
    spk.runningTime.preliminary = spk.runningTimeLeg.preliminary + timeOffset;
    spk.runningTimeLeg.time = spk.runningTimeLeg.preliminary;
  }
  else if (spk.status==StatusUnknown)
    spk.status=StatusMP;

  if (totalResult && inputStatus != StatusOK)
    spk.status = spk.finishStatus;
}

int oTeam::getTimeAfter(int leg) const
{
  if (leg==-1)
    leg=Runners.size()-1;

  if (!Class || Class->tLeaderTime.size()<=unsigned(leg))
    return -1;

  int t=getLegRunningTime(leg, false);

  if (t<=0)
    return -1;

  return t-Class->getTotalLegLeaderTime(leg, false);
}

int oTeam::getLegStartTime(int leg) const
{
  if (leg==0)
    return tStartTime;
  else if (unsigned(leg)<Runners.size() && Runners[leg])
    return Runners[leg]->getStartTime();
  else return 0;
}

wstring oTeam::getLegStartTimeS(int leg) const
{
  int s=getLegStartTime(leg);
  if (s>0)
    return oe->getAbsTime(s);
  else 
    return makeDash(L"-");
}

wstring oTeam::getLegStartTimeCompact(int leg) const
{
  int s=getLegStartTime(leg);
  if (s>0)
    if (oe->useStartSeconds())
      return oe->getAbsTime(s);
    else
      return oe->getAbsTimeHM(s);

  else return makeDash(L"-");
}

void oTeam::setBib(const wstring &bib, int bibnumerical, bool updateStartNo, bool setTmpOnly) {
  if (updateStartNo)
    updateStartNo = !Class || !Class->lockedForking();

  if (setTmpOnly) {
    tmpStore.bib = bib;
    if (updateStartNo)
      setStartNo(bibnumerical, true);
    return;
  }

  if (getDI().setString("Bib", bib)) {
    if (oe)
      oe->bibStartNoToRunnerTeam.clear();
  }

  if (updateStartNo) {
    setStartNo(bibnumerical, false);
  }
}

oDataContainer &oTeam::getDataBuffers(pvoid &data, pvoid &olddata, pvectorstr &strData) const {
  data = (pvoid)oData;
  olddata = (pvoid)oDataOld;
  strData = 0;
  return *oe->oTeamData;
}

pRunner oTeam::getRunner(unsigned leg) const
{
  if (leg==-1)
    leg=Runners.size()-1;

  return leg<Runners.size() ? Runners[leg]: 0;
}

int oTeam::getRogainingPoints(bool multidayTotal) const {
  int pt = 0;
  bool simpleSum = false;
  if (simpleSum) {
    for (size_t k = 0; k < Runners.size(); k++) {
      if (Runners[k])
        pt += Runners[k]->tRogainingPoints;
    }
  }
  else {
    std::set<int> rogainingControls;
    for (size_t k = 0; k < Runners.size(); k++) {
      if (Runners[k]) {
        pCard c = Runners[k]->getCard();
        pCourse crs = Runners[k]->getCourse(true);
        if (c && crs) {
          for (oPunchList::const_iterator it = c->punches.begin(); it != c->punches.end();++it) {
            if (rogainingControls.count(it->tMatchControlId) == 0) {
              rogainingControls.insert(it->tMatchControlId);
              pt += it->tRogainingPoints;
            }
          }
        }
      }
    }
  }
  pt = max(pt - getRogainingReduction(), 0);
  // Manual point adjustment
  pt = max(0, pt + getPointAdjustment());

  if (multidayTotal)
    return pt + inputPoints;
  return pt;
}

int oTeam::getRogainingOvertime() const {
  int overTime = 0;
  for (size_t k = 0; k < Runners.size(); k++) {
    if (Runners[k]) {
      overTime += Runners[k]->tRogainingOvertime;
    }
  }
  return overTime;
}

int oTeam::getRogainingPointsGross() const {
  int gross = 0;
  for (size_t k = 0; k < Runners.size(); k++) {
    if (Runners[k]) {
      gross += Runners[k]->tRogainingPointsGross;
    }
  }
  return gross;
}
 

int oTeam::getRogainingReduction() const {
  pCourse sampleCourse = 0;
  int overTime = 0;
  for (size_t k = 0; k < Runners.size(); k++) {
    if (Runners[k]) {
      if (sampleCourse == 0 &&(Runners[k]->tRogainingPoints > 0 || Runners[k]->tReduction > 0)) 
        sampleCourse = Runners[k]->getCourse(false);
      overTime += Runners[k]->tRogainingOvertime; 
    }
  }
  return sampleCourse->calculateReduction(overTime);
}
 
void oTeam::remove()
{
  if (oe)
    oe->removeTeam(Id);
}

bool oTeam::canRemove() const
{
  return true;
}

wstring oTeam::getDisplayName() const {
  if (!Class)
    return sName;

  ClassType ct = Class->getClassType();
  if (ct == oClassIndividRelay || ct == oClassPatrol) {
    if (Club) {
      wstring cname = getDisplayClub();
      if (!cname.empty())
        return cname;
    }
  }
  return sName;
}

wstring oTeam::getDisplayClub() const {
  vector<pClub> clubs;
  if (Club)
    clubs.push_back(Club);
  for (size_t k = 0; k<Runners.size(); k++) {
    if (Runners[k] && Runners[k]->Club) {
      if (count(clubs.begin(), clubs.end(), Runners[k]->Club) == 0)
        clubs.push_back(Runners[k]->Club);
    }
  }
  if (clubs.size() == 1)
    return clubs[0]->getDisplayName();

  wstring res;
  for (size_t k = 0; k<clubs.size(); k++) {
    if (k == 0)
      res = clubs[k]->getDisplayName();
    else
      res += L" / " + clubs[k]->getDisplayName();
  }
  return res;
}

void oTeam::setInputData(const oTeam &t) {
  inputTime = t.getTotalRunningTime();
  inputStatus = t.getTotalStatus();
  inputPoints = t.getRogainingPoints(true);
  int tp = t.getTotalPlace();
  inputPlace = tp < 99000 ? tp : 0;

  oDataInterface dest = getDI();
  oDataConstInterface src = t.getDCI();

  dest.setInt("TransferFlags", src.getInt("TransferFlags"));
  
  dest.setString("Nationality", src.getString("Nationality"));
  dest.setString("Country", src.getString("Country"));
  dest.setInt("Fee", src.getInt("Fee"));
  dest.setInt("Paid", src.getInt("Paid"));
  dest.setInt("Taxable", src.getInt("Taxable"));
}

RunnerStatus oTeam::getTotalStatus() const {
  if (tStatus == StatusUnknown)
    return StatusUnknown;
  else if (inputStatus == StatusUnknown)
    return StatusDNS;

  return max(tStatus, inputStatus);
}

void oEvent::getTeams(int classId, vector<pTeam> &t, bool sort) {
  if (sort) {
    synchronizeList(oLTeamId);
    //sortTeams(SortByName);
  }
  t.clear();
  if (Classes.size() > 0)
    t.reserve((Teams.size()*min<size_t>(Classes.size(), 4)) / Classes.size());

  for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
    if (it->isRemoved())
      continue;

    if (classId == 0 || it->getClassId(false) == classId)
      t.push_back(&*it);
  }
}

wstring oTeam::getEntryDate(bool dummy) const {
  oDataConstInterface dci = getDCI();
  int date = dci.getInt("EntryDate");
  if (date == 0 && !isVacant()) {
    auto di = (const_cast<oTeam *>(this)->getDI());
    di.setDate("EntryDate", getLocalDate());
    di.setInt("EntryTime", getLocalAbsTime());
  }
  return dci.getDate("EntryDate");
}

unsigned static nRunnerMaxStored = -1;

Table *oEvent::getTeamsTB() //Table mode
{

  vector<pClass> cls;
  oe->getClasses(cls, true);
  unsigned nRunnerMax = 0;
  for (size_t k = 0; k < cls.size(); k++) {
    nRunnerMax = max(nRunnerMax, cls[k]->getNumStages());
  }

  bool forceUpdate = nRunnerMax != nRunnerMaxStored;

  if (forceUpdate && tables.count("team")) {
    tables["team"]->releaseOwnership();
    tables.erase("team");
  }

  if (tables.count("team") == 0) {

    oCardList::iterator it;

    Table *table=new Table(this, 20, L"Lag(flera)", "teams");

    table->addColumn("Id", 70, true, true);
    table->addColumn("Ändrad", 70, false);

    table->addColumn("Namn", 200, false);
    table->addColumn("Klass", 120, false);
    table->addColumn("Klubb", 120, false);

    table->addColumn("Start", 70, false, true);
    table->addColumn("Mål", 70, false, true);
    table->addColumn("Status", 70, false);
    table->addColumn("Tid", 70, false, true);

    table->addColumn("Plac.", 70, true, true);
    table->addColumn("Start nr.", 70, true, false);

    for (unsigned k = 0; k < nRunnerMax; k++) {
      table->addColumn("Sträcka X#" + itos(k+1), 200, false, false);
      table->addColumn("Bricka X#" + itos(k+1), 70, true, true);
    }
    nRunnerMaxStored = nRunnerMax;

    oe->oTeamData->buildTableCol(table);

    table->addColumn("Tid in", 70, false, true);
    table->addColumn("Status in", 70, false, true);
    table->addColumn("Poäng in", 70, true);
    table->addColumn("Placering in", 70, true);

    tables["team"] = table;
    table->addOwnership();
  }

  tables["team"]->update();
  return tables["team"];
}


void oEvent::generateTeamTableData(Table &table, oTeam *addTeam)
{
  vector<pClass> cls;
  oe->getClasses(cls, true);
  unsigned nRunnerMax = 0;

  for (size_t k = 0; k < cls.size(); k++) {
    nRunnerMax = max(nRunnerMax, cls[k]->getNumStages());
  }

  if (nRunnerMax != nRunnerMaxStored)
    throw meosException("Internal table error: Restart MeOS");

  if (addTeam) {
    addTeam->addTableRow(table);
    return;
  }

  synchronizeList(oLTeamId);
  oTeamList::iterator it;
  table.reserve(Teams.size());
  for (it=Teams.begin(); it != Teams.end(); ++it){
    if (!it->skip()){
      it->addTableRow(table);
    }
  }
}

void oTeam::addTableRow(Table &table) const {
  oRunner &it = *pRunner(this);
  table.addRow(getId(), &it);

  int row = 0;
  table.set(row++, it, TID_ID, itow(getId()), false);
  table.set(row++, it, TID_MODIFIED, getTimeStamp(), false);

  table.set(row++, it, TID_NAME, getName(), true);
  table.set(row++, it, TID_CLASSNAME, getClass(true), true, cellSelection);
  table.set(row++, it, TID_CLUB, getClub(), true, cellCombo);

  table.set(row++, it, TID_START, getStartTimeS(), true);
  table.set(row++, it, TID_FINISH, getFinishTimeS(), true);
  table.set(row++, it, TID_STATUS, getStatusS(), true, cellSelection);
  table.set(row++, it, TID_RUNNINGTIME, getRunningTimeS(), false);

  table.set(row++, it, TID_PLACE, getPlaceS(), false);
  table.set(row++, it, TID_STARTNO, itow(getStartNo()), true);

  for (unsigned k = 0; k < nRunnerMaxStored; k++) {
    pRunner r = getRunner(k);
    if (r) {
      table.set(row++, it, 100+2*k, r->getUIName(), r->getRaceNo() == 0);
      table.set(row++, it, 101+2*k, itow(r->getCardNo()), true);
    }
    else {
      table.set(row++, it, 100+2*k, L"", Class && Class->getLegRunner(k) == k);
      table.set(row++, it, 101+2*k, L"", false);
    }
  }

  row = oe->oTeamData->fillTableCol(it, table, true);

  table.set(row++, it, TID_INPUTTIME, getInputTimeS(), true);
  table.set(row++, it, TID_INPUTSTATUS, getInputStatusS(), true, cellSelection);
  table.set(row++, it, TID_INPUTPOINTS, itow(inputPoints), true);
  table.set(row++, it, TID_INPUTPLACE, itow(inputPlace), true);
}

bool oTeam::inputData(int id, const wstring &input,
                        int inputId, wstring &output, bool noUpdate)
{
  int s,t;
  synchronize(false);

  if (id>1000) {
    return oe->oTeamData->inputData(this, id, input,
                                    inputId, output, noUpdate);
  }
  else if (id >= 100) {

    bool isName = (id&1) == 0;
    size_t ix = (id-100)/2;
    if (ix < Runners.size()) {
      if (Runners[ix]) {
        if (isName) {
          if (input.empty()) {
            removeRunner(oe->gdibase, false, ix);
          }
          else {
            Runners[ix]->setName(input, true);
            Runners[ix]->synchronize(true);
            output = Runners[ix]->getName();
          }
        }
        else {
          Runners[ix]->setCardNo(_wtoi(input.c_str()), true);
          Runners[ix]->synchronize(true);
          output = itow(Runners[ix]->getCardNo());
        }
      }
      else {
        if (isName && !input.empty() && Class) {
          pRunner r = oe->addRunner(input, getClubId(), getClassId(false), 0, 0, false);
          setRunner(ix, r, true);
          output = r->getName();
        }

      }
    }
  }

  switch(id) {

    case TID_NAME:
      setName(input, true);
      synchronize(true);
      output = getName();
      break;

    case TID_START:
      setStartTimeS(input);
      if (getRunner(0))
        getRunner(0)->setStartTimeS(input);
      t=getStartTime();
      apply(false, 0, false);
      s=getStartTime();
      if (s!=t)
        throw std::exception("Starttiden är definerad genom klassen eller löparens startstämpling.");
      synchronize(true);
      output = getStartTimeS();
      return true;
    break;

    case TID_CLUB:
      {
        pClub pc = 0;
        if (inputId > 0)
          pc = oe->getClub(inputId);
        else
          pc = oe->getClubCreate(0, input);

        setClub(pc ? pc->getName() : L"");
        synchronize(true);
        output = getClub();
      }
      break;

    case TID_CLASSNAME:
      setClassId(inputId, true);
      synchronize(true);
      output = getClass(true);
      break;

    case TID_STATUS: {
      RunnerStatus sIn = RunnerStatus(inputId);
      if (sIn != StatusDNS && sIn != StatusCANCEL) {
        if ((getStatus() == StatusDNS || getStatus() == StatusCANCEL) && sIn == StatusUnknown)
          setTeamNoStart(false, StatusUnknown);
        else
          setStatus(sIn, true, false);
      }
      else if (getStatus() != sIn)
        setTeamNoStart(true, sIn);

      apply(true, 0, false);
      RunnerStatus sOut = getStatus();
      if (sOut != sIn)
        throw meosException("Status matchar inte deltagarnas status.");
      output = getStatusS();
    }
    break;

    case TID_STARTNO:
      setStartNo(_wtoi(input.c_str()), false);
      synchronize(true);
      output = itow(getStartNo());
      break;

    case TID_INPUTSTATUS:
      setInputStatus(RunnerStatus(inputId));
      synchronize(true);
      output = getInputStatusS();
      break;

    case TID_INPUTTIME:
      setInputTime(input);
      synchronize(true);
      output = getInputTimeS();
      break;

    case TID_INPUTPOINTS:
      setInputPoints(_wtoi(input.c_str()));
      synchronize(true);
      output = itow(getInputPoints());
      break;

    case TID_INPUTPLACE:
      setInputPlace(_wtoi(input.c_str()));
      synchronize(true);
      output = itow(getInputPlace());
      break;

  }
  return false;
}

void oTeam::fillInput(int id, vector< pair<wstring, size_t> > &out, size_t &selected)
{
  if (id>1000) {
    oe->oRunnerData->fillInput(oData, id, 0, out, selected);
    return;
  }
  else if (id==TID_CLASSNAME) {
    oe->fillClasses(out, oEvent::extraNone, oEvent::filterOnlyMulti);
    out.push_back(make_pair(lang.tl(L"Ingen klass"), 0));
    selected = getClassId(true);
  }
  else if (id==TID_CLUB) {
    oe->fillClubs(out);
    out.push_back(make_pair(lang.tl(L"Klubblös"), 0));
    selected = getClubId();
  }
  else if (id==TID_STATUS || id==TID_INPUTSTATUS) {
    oe->fillStatus(out);
    selected = getStatus();
  }
}

void oTeam::removeRunner(gdioutput &gdi, bool askRemoveRunner, int i) {
  pRunner p_old = getRunner(i);
  setRunner(i, 0, true);

  //Remove multi runners
  if (Class) {
    for (unsigned k = i+1;k < Class->getNumStages(); k++) {
      if (Class->getLegRunner(k)==i)
        setRunner(k, 0, true);
    }
  }

  //No need to delete multi runners. Disappears when parent is gone.
  if (p_old && !oe->isRunnerUsed(p_old->getId())){
    if (!askRemoveRunner || gdi.ask(L"Ska X raderas från tävlingen?#" + p_old->getName())){
      vector<int> oldR;
      oldR.push_back(p_old->getId());
      oe->removeRunner(oldR);
    }
    else {
      p_old->getDI().setInt("RaceId", 0); // Clear race id.
      p_old->setClassId(0, false); // Clear class
      p_old->synchronize(true);
    }
  }
}

int oTeam::getTeamFee() const {
  int f = getDCI().getInt("Fee");
  for (size_t k = 0; k < Runners.size(); k++) {
    if (Runners[k])
      f += Runners[k]->getDCI().getInt("Fee");
  }
  return f;
}

void oTeam::markClassChanged(int controlId) {
  if (Class)
    Class->markSQLChanged(-1, controlId);
}

void oTeam::resetResultCalcCache() const {
  resultCalculationCache.resize(RCCLast);
  for (int k = 0; k < RCCLast; k++)
    resultCalculationCache[k].resize(Runners.size());
}

vector< vector<int> > &oTeam::getResultCache(ResultCalcCacheSymbol symb) const {
  return resultCalculationCache[symb];
}

void oTeam::setResultCache(ResultCalcCacheSymbol symb, int leg, vector<int> &data) const {
  if (!resultCalculationCache.empty() && size_t(leg) < resultCalculationCache[symb].size())
    resultCalculationCache[symb][leg].swap(data);
}

int oTeam::getNumShortening() const {
  return getNumShortening(-1);
}

int oTeam::getNumShortening(int leg) const {
  int ns = 0;
  if (Class) {
    for (size_t k = 0; k < Runners.size() && k <= size_t(leg); k++) {
      if (Runners[k] && !Class->isOptional(k))
        ns += Runners[k]->getNumShortening();
    }
  }
  else {
   for (size_t k = 0; k < Runners.size() && k <= size_t(leg); k++) {
      if (Runners[k])
        ns += Runners[k]->getNumShortening();
    }
  }
  return ns;
}

bool oTeam::checkValdParSetup() {
  if (!Class)
    return false;
  bool cor = false;
  for (size_t k = 0; k < Runners.size(); k++) {
    if (!Class->isOptional(k) && !Class->isParallel(k) && !Runners[k]) {
      int m = 1;
      while((m+k) < Runners.size() && (Class->isOptional(k+m) || Class->isParallel(k+m))) {
        if (Runners[m+k]) {
          // Move to where a runner is needed
          Runners[k] = Runners[k+m];
          Runners[k]->tLeg = k;          
          Runners[k+m] = 0;
          updateChanged();
          cor = true;
          k+=m;
          break;
        }
        else 
          m++;
      }
    }
  }
  return cor;
}


int oTeam::getRanking() const {
  for (size_t k = 0; k < Runners.size(); k++) {
    if (Runners[k]) {
      return Runners[k]->getRanking();
    }
  }
  return MaxRankingConstant;
}