/********************i****************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2024 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 "oListInfo.h"
#include "oEvent.h"
#include "gdioutput.h"
#include "meos_util.h"
#include <cassert>
#include <cmath>
#include "Localizer.h"
#include "metalist.h"
#include <algorithm>
#include "gdifonts.h"
#include "generalresult.h"
#include "meosexception.h"
#include "gdiimpl.h"
#include "image.h"

struct PrintPostInfo {
  PrintPostInfo(gdioutput &gdi, const oListParam &par) : 
              gdi(gdi), par(par), keepToghether(false) {}

  gdioutput &gdi;
  const oListParam &par;
  oCounter counter;
  bool keepToghether;
  void reset() {keepToghether = false;}
private:
  PrintPostInfo &operator=(const PrintPostInfo &a) {}
};

oListInfo::oListInfo() {
  listType=EBaseTypeRunner;
  listSubType=EBaseTypeRunner;
  calcResults=false;
  calcTotalResults = false;
  rogainingResults = false;
  calculateLiveResults = false;
  calcCourseClassResults = false;
  calcCourseResults = false;
  listPostFilter.resize(_EFilterMax+1, 0);
  listPostSubFilter.resize(_ESubFilterMax+1, 0);
  fixedType = false;
  largeSize = false;
  supportClasses = true;
  supportLegs = false;
  supportParameter = false;
  supportLarge = false;
  supportTo = false;
  supportFrom = false;
  supportCustomTitle = true;
  
  resType = Classwise;

  supportSplitAnalysis = true;
  supportInterResults = true;
  supportPageBreak = true;
  supportClassLimit = true;
  
  needPunches = PunchMode::NoPunch;
}

oListInfo::~oListInfo(void) {
}

void oListInfo::replaceType(EPostType find, EPostType replace, bool onlyFirst) {
  for (auto blp : { &head, &subHead, &listPost, &subListPost }) {
    for (auto &pp : *blp) {
      if (pp.type == replace && onlyFirst)
        return;
      if (pp.type == find) {
        pp.type = replace;
        if (onlyFirst)
          return;
      }
    }
  }
}

wstring oListParam::getContentsDescriptor(const oEvent &oe) const {
  wstring cls;
  vector<pClass> classes;
  oe.getClasses(classes, false);
  if (classes.size() == selection.size() || selection.empty())
    cls = oe.getName(); // All classes
  else {
    for (pClass c : classes) {
      if (selection.count(c->getId())) {
        if (!cls.empty())
          cls += L", ";
        cls += c->getName();
      }
    }
  }
  if (legNumber != -1)
    return lang.tl(L"Sträcka X#" + getLegName()) + L": " + cls;
  else
    return cls;
}

oPrintPost::oPrintPost() : type(lString), dx(0), dy(0), format(0) {
  legIndex = -1;
  linearLegIndex = true;
}

oPrintPost::oPrintPost(EPostType type, const wstring& text,
                       int format, int dx, int dy,
                       pair<int, bool> index) : type(type), text(text),
                                                format(format), dx(dx), dy(dy) {
  legIndex = index.first;
  linearLegIndex = index.second;
}

oPrintPost::oPrintPost(const wstring& image,
                       int style, int dx, int dy,
                       int width, int height) : type(lImage), text(image),
                                                format(style), dx(dx), dy(dy),
                                                fixedWidth(width), fixedHeight(height) {
  legIndex = -1;
  linearLegIndex = true;
}


bool oListInfo::needRegenerate(const oEvent &oe) const {
  for(oClassList::const_iterator it = oe.Classes.begin(); it != oe.Classes.end(); ++it) {
    if (it->isRemoved())
      continue;

    if (!lp.selection.empty() && lp.selection.count(it->getId()) == 0 )
      continue; // Not our class

    int legToCheck = -1;
    if (needPunches == PunchMode::SpecificPunch) {
      int to = oControl::getIdIndexFromCourseControlId(lp.useControlIdResultTo).first;
      int from = oControl::getIdIndexFromCourseControlId(lp.useControlIdResultFrom).first;

      if (it->wasSQLChanged(legToCheck, to) ||
          it->wasSQLChanged(legToCheck, from) )
        return true;
    }
    else if (needPunches == PunchMode::AnyPunch) {
      if (it->wasSQLChanged(legToCheck, -2))
        return true;
    }
    else {
      if (it->wasSQLChanged(legToCheck, -1))
        return true;
    }
  }

  return false;
}

static void generateNBestHead(const oListParam &par, oListInfo &li, int ypos) {
  if (par.filterMaxPer > 0)
    li.addHead(oPrintPost(lString, lang.tl(L"Visar de X bästa#" + itow(par.filterMaxPer)), normalText, 0, ypos));
}

extern gdioutput *gdi_main;

static pair<wstring, bool> getControlName(const oEvent &oe, int courseContolId) {
  pair<int, int> idt = oControl::getIdIndexFromCourseControlId(courseContolId);
  pControl to = oe.getControl(idt.first);
  wstring toS;
  bool name = false;
  if (to) {
    if (to->hasName()) {
      toS = to->getName();
      name = true;
    }
    else if (to->getFirstNumber()>0)
      toS = itow(to->getFirstNumber());
    else
      toS = itow(idt.first);

    if (to->getNumberDuplicates() > 0)
      toS += L"-" + itow(idt.second + 1);
  }
  else
    toS = itow(idt.first);

  return make_pair(toS, name);
}

static wstring getFullControlName(const oEvent &oe, int ctrl) {
  pair<wstring, bool> toS = getControlName(oe, ctrl);
  if (toS.second)
    return toS.first;
  else
    return lang.tl(L"Kontroll X#" + toS.first);
}

static void getResultTitle(const oEvent &oe, const oListParam &lp, wstring &title) {
  if (lp.useControlIdResultTo <= 0 && lp.useControlIdResultFrom <= 0)
    title = lang.tl(L"Resultat - %s");
  else if (lp.useControlIdResultTo>0 && lp.useControlIdResultFrom<=0){
    pair<wstring, bool> toS = getControlName(oe, lp.useControlIdResultTo);
    if (toS.second)
      title = lang.tl(L"Resultat - %s") + L", " + toS.first;
    else
      title = lang.tl(L"Resultat - %s") + L", " + lang.tl(L"vid kontroll X#" + toS.first);
  }
  else {
    wstring fromS = lang.tl(L"Start"), toS = lang.tl(L"Mål");
    if (lp.useControlIdResultTo>0) {
      toS = getControlName(oe, lp.useControlIdResultTo).first;
    }
    if (lp.useControlIdResultFrom>0) {
      fromS = getControlName(oe, lp.useControlIdResultFrom).first;
    }
    title = lang.tl(L"Resultat mellan X och Y#" + fromS + L"#" + toS);
  }
}

static double adjustmentFactor(double par, double target) {
  double k = (1.0 - target)/10;
  double val = max(target, 1.0 - (par * k));
  return val;
}


template<typename T, int size> class WordMeasure {
  multimap<int, T> words;
public:
  WordMeasure() {}

  void add(const T &word) {
    if (words.size() > size) {
      size_t blen = words.begin()->first;
      if (word.length() <= blen)
        return;
      else {
        words.erase(words.begin());
        words.insert(make_pair(word.length(), word));
      }
    }
    else {
      words.insert(make_pair(word.length(), word));
    }
  }

  int measure(const gdioutput &gdi, 
              gdiFonts font,
              const wchar_t *fontFace,
              T &longest) {   

    int w = 0;
    TextInfo ti;
    HDC hDC = GetDC(gdi.getHWNDTarget());
    
    for (auto it = words.begin(); it != words.end(); ++it) {
      ti.xp = 0;
      ti.yp = 0;
      ti.format = font;
      ti.text = it->second;
      ti.font = fontFace != 0 ? fontFace : L"";
      gdi.calcStringSize(ti, hDC);
      if (ti.textRect.right > w) {
        w = ti.textRect.right;
        longest = ti.text;
      }
    }

    ReleaseDC(gdi.getHWNDTarget(), hDC);
    return w;
  }

};

int oListInfo::getMaxCharWidth(const oEvent *oe,
                               const gdioutput &gdi,
                               const set<int> &clsSel,
                               const vector< pair<EPostType, wstring> > &typeFormats,
                               gdiFonts font,
                               const wchar_t *fontFace,
                               bool large, int minSize) const {
  vector<oPrintPost> pps;
  for (size_t k = 0; k < typeFormats.size(); k++) {
    pps.push_back(oPrintPost());
    pps.back().text = typeFormats[k].second;
    pps.back().type = typeFormats[k].first;
  }

  oListParam par;
  par.setLegNumberCoded(0);
  oCounter c;
  vector< WordMeasure<wstring, 32> > extras(pps.size());

  for (size_t k = 0; k < pps.size(); k++) {
    wstring extra;
    switch (pps[k].type) {
      case lResultModuleNumber:
      case lResultModuleNumberTeam:
        if (pps[k].text.length() > 1 && pps[k].text[0] == '@') {
          wstring tmp;
          int miLen = 0;
          for (int j = 0; j < 10; j++) {
            tmp = MetaList::fromResultModuleNumber(pps[k].text.substr(1), j, tmp);
            if (tmp.length() > miLen / 2) {
              miLen = tmp.length();
              extras[k].add(tmp);
            }
          }
        }
        else
          extra = L"999";
        break;
      case lRunnerCardVoltage:
        extra = L"3.00 V";
        break;
      case lPunchName:
      case lControlName:
      case lPunchNamedTime:
      case lPunchTeamTotalNamedTime: {
        wstring maxcn = lang.tl("Mål");
        vector<pControl> ctrl;
        oe->getControls(ctrl, false);
        for (pControl c : ctrl) {
          wstring cn = c->getName();
          if (cn.length() > maxcn.length())
            maxcn.swap(cn);
        }
        if (pps[k].type == lPunchNamedTime)
          extra = maxcn + L": 50:50 (50:50)";
        if (pps[k].type == lPunchTeamTotalNamedTime)
          extra = maxcn + L": 2:50:50 (50:50)";
        else
          maxcn.swap(extra);
      }
       break;
      case lRunnerFinish:
      case lRunnerCheck:
      case lPunchAbsTime:
      case lRunnerStart:
      case lTeamStart:
        extra = L"10:10:00";
      break;
      case lRunnerTotalTimeAfter:
      case lRunnerClassCourseTimeAfter:
      case lRunnerTimeAfterDiff:
      case lRunnerTempTimeAfter:
      case lRunnerTimeAfter:
      case lRunnerLostTime:
      case lTeamTimeAfter:
      case lTeamLegTimeAfter:
      case lTeamTotalTimeAfter:
      case lTeamTimeAdjustment:
      case lRunnerTimeAdjustment:
      case lRunnerGeneralTimeAfter:
      case lPunchTotalTimeAfter:
      case lPunchTeamTotalTimeAfter:
        extra = L"+10:00";
        break;
      case lTeamRogainingPointOvertime:
      case lRunnerRogainingPointOvertime:
      case lResultModuleTime:
      case lResultModuleTimeTeam:  
      case lTeamTime:
      case lTeamGrossTime:
      case lTeamTotalTime:
      case lTeamTotalTimeStatus:
      case lTeamLegTimeStatus:
      case lTeamTimeStatus:
      case lRunnerTempTimeStatus:
      case lRunnerTotalTimeStatus:
      case lRunnerTotalTime:
      case lClassStartTime:
      case lRunnerTime:
      case lRunnerGrossTime:
      case lRunnerTimeStatus:
      case lRunnerStageTime:
      case lRunnerStageTimeStatus:
      case lRunnerStageStatus:
      case lRunnerTimePlaceFixed:
      case lPunchLostTime:
      case lPunchTimeSinceLast:
      case lPunchSplitTime:
      case lPunchNamedSplit:
        extra = L"50:50";
        break;
      case lPunchTotalTime:
      case lPunchTeamTotalTime:
        extra = L"1:50:50";
        break;
      case lRunnerGeneralTimeStatus:
      case lClassStartTimeRange:
        extra = L"50:50 (50:50)";
        break;
      case lTeamRogainingPointReduction:
      case lRunnerRogainingPointReduction:
      case lTeamPointAdjustment:
      case lRunnerPointAdjustment:
      case lRunnerRogainingPointGross:
      case lRunnerPlace:
      case lRunnerPlaceDiff:
      case lTeamPlaceDiff:
      case lRunnerTotalPlace:
      case lRunnerClassCoursePlace:
      case lRunnerCoursePlace:
      case lTeamPlace:
      case lTeamTotalPlace:
      case lPunchControlPlace:
      case lPunchControlPlaceAcc:
      case lPunchControlPlaceTeamAcc:
      case lRunnerStagePlace:
      case lRunnerDataA:
      case lRunnerDataB:
      case lTeamDataA:
      case lTeamDataB:
      case lClassDataA:
      case lClassDataB:
      
        extra = L"99.";
        break;
      case lRunnerGeneralPlace:
        extra = L"99. (99.)";
        break;
    }

    EPostType type = pps[k].type;

    if (type == lClubName || type == lRunnerName || type == lTeamName
                          || type == lTeamRunner || type == lTeamClub)
      extras[k].add(L"IK Friskus Varberg");

    if (type == lRunnerGivenName || type == lRunnerFamilyName ||
       type == lRunnerNationality || type == lRunnerLegTeamLeaderName)
      extras[k].add(L"Karl-Gunnar");

    if (type == lRunnerCompleteName || type == lPatrolNameNames || type == lPatrolClubNameNames)
      extras[k].add(L"Karl-Gunnar Alexandersson");

    extras[k].add(extra);
  }
  for (size_t k = 0; k < pps.size(); k++) {
    const oPrintPost &pp = pps[k];
   
    int cardSkip = 0;
    int skip = 0;
    wstring last;
    for (auto crd = oe->Cards.begin(); crd != oe->Cards.end(); ++crd) {
      if (--skip > 0)
        continue;
      skip = cardSkip++;

      for (auto &p : crd->punches) {
        pRunner r = crd->tOwner;
        p.previousPunchTime = 0;
        const wstring &out = oe->formatPunchString(pp, par, r ? r->getTeam() : nullptr, r, &p, c);
        if (last == out)
          break;
        else
          last = out;
        extras[k].add(out);
      }
    }

    for (auto &cls : oe->Classes) {
      if (cls.isRemoved())
        continue;
      if (!clsSel.empty() && clsSel.count(cls.getId()) == 0)
        continue;

      const wstring &out = oe->formatListString(pp, par, 0, 0, 0, pClass(&cls), c);
      extras[k].add(out);
    }

    for (oCourseList::const_iterator it = oe->Courses.begin(); it != oe->Courses.end(); ++it) {
      if (it->isRemoved())
        continue;

      const wstring &out = oe->formatSpecialString(pp, par, 0, 0, pCourse(&*it), 0,  c);
      extras[k].add(out);
    }

    for (oControlList::const_iterator it = oe->Controls.begin(); it != oe->Controls.end(); ++it) {
      if (it->isRemoved())
        continue;

      const wstring &out = oe->formatSpecialString(pp, par, 0, 0, 0, pControl(&*it),  c);
      extras[k].add(out);
    }
  }

  vector<int> row(pps.size(), 0);
  vector<wstring> samples(pps.size());
  wstring totWord = L"";
  for (size_t k = 0; k < pps.size(); k++) {
    extras[k].measure(gdi, font, fontFace, samples[k]);
    totWord += samples[k];
  }
  
  WordMeasure<wstring, 64> totMeasure;
  totMeasure.add(totWord);
  for (oRunnerList::const_iterator it = oe->Runners.begin(); it != oe->Runners.end(); ++it) {
    if (it->isRemoved())
      continue;

    // Case when runner/team has different class
    bool teamOK = it->getTeam() && clsSel.count(it->getTeam()->getClassId(false));

    if (!clsSel.empty() && (!teamOK && clsSel.count(it->getClassId(true)) == 0))
        continue;

    totWord.clear();
    wstring rout;
    for (size_t k = 0; k < pps.size(); k++) {
      oPrintPost &pp = pps[k];
      
      pp.legIndex = it->tLeg;
      pp.linearLegIndex = true;
      int numIter = 1;

      if (pp.type == lPunchNamedTime || pp.type == lPunchTime || pp.type == lPunchTeamTime) {
        row[k] = max(row[k], 10);
        pRunner r = pRunner(&*it);
        numIter = (r && r->getCard()) ? r->getCard()->getNumPunches() + 1 : 1;
      }
      rout.clear();
      while (numIter-- > 0) {
        const wstring &out = oe->formatListString(pp, par, it->tInTeam, pRunner(&*it), it->Club, pClass(it->getClassRef(true)), c);
        //row[k] = max(row[k], int(out.length()));
        if (out.length() > rout.length())
          rout = out;
        if (numIter>0)
          c.level3++;
      }

      if (rout.length() > samples[k].length())
        totWord.append(rout);
      else
        totWord.append(samples[k]);

    }
    totMeasure.add(totWord);
  }

  wstring dummy;
  int w = totMeasure.measure(gdi, font, fontFace, dummy);
  w = max(w, gdi.scaleLength(minSize));
  return int(0.5 + (w + (large ? 5 : 15))/gdi.getScale());
}

const wstring & oEvent::formatListString(EPostType type, const pRunner r) const
{
  oPrintPost pp;
  oCounter ctr;
  oListParam par;
  par.setLegNumberCoded(r->tLeg);
  pp.type = type;
  return formatListString(pp, par, r->tInTeam, r, r->Club, r->getClassRef(true), ctr);
}

const wstring & oEvent::formatListString(EPostType type, const pRunner r, 
                                         const wstring &format) const {
  oPrintPost pp;
  oCounter ctr;
  oListParam par;
  par.setLegNumberCoded(r->tLeg);
  pp.type = type;
  pp.text = format;
  return formatListString(pp, par, r->tInTeam, r, r->Club, r->getClassRef(true), ctr);
}


const wstring &oEvent::formatListString(const oPrintPost &pp, const oListParam &par,
                                        const pTeam t, const pRunner r, const pClub c,
                                        const pClass pc, oCounter &counter) const {
  const oPrintPost *cpp = &pp;
  const wstring *tmp = 0;
  wstring *out = 0;
  while (cpp) {
    if (tmp) {
      if (!out) {
        out = &StringCache::getInstance().wget();
        *out = L"";
      }
      out->append(*tmp);
    }
    tmp = &formatListStringAux(*cpp, par, t, r, c, pc, counter);
    cpp = cpp->mergeWithTmp;
  }

  if (out) {
    out->append(*tmp);
    return *out;
  }
  else
    return *tmp;
}

const wstring &oEvent::formatPunchString(const oPrintPost &pp, const oListParam &par,
                                         const pTeam t, const pRunner r,
                                         const oPunch *punch, oCounter &counter) const {
  const oPrintPost *cpp = &pp;
  const wstring *tmp = 0;
  wstring *out = 0;
  while (cpp) {
    if (tmp) {
      if (!out) {
        out = &StringCache::getInstance().wget();
        *out = L"";
      }
      out->append(*tmp);
    }
    tmp = &formatPunchStringAux(*cpp, par, t, r, punch, counter);
    cpp = cpp->mergeWithTmp;
  }

  if (out) {
    out->append(*tmp);
    return *out;
  }
  else
    return *tmp;
}

const wstring &oEvent::formatSpecialString(const oPrintPost &pp, const oListParam &par, const pTeam t, int legIndex,
                                           const pCourse crs, const pControl ctrl, oCounter &counter) const {
  const oPrintPost *cpp = &pp;
  const wstring *tmp = 0;
  wstring *out = 0;
  while (cpp) {
    if (tmp) {
      if (!out) {
        out = &StringCache::getInstance().wget();
        *out = L"";
      }
      out->append(*tmp);
    }
    tmp = &formatSpecialStringAux(*cpp, par, t, legIndex, crs, ctrl, counter);
    cpp = cpp->mergeWithTmp;
  }

  if (out) {
    out->append(*tmp);
    return *out;
  }
  else
    return *tmp;
}

const wstring &oEvent::formatPunchStringAux(const oPrintPost &pp, const oListParam &par,
                                            const pTeam t, const pRunner r,
                                            const oPunch *punch, oCounter &counterIn) const {
  wchar_t bfw[128];
  const wstring *wsptr = 0;
  bfw[0] = 0;
  pClass pc = r ? r->getClassRef(true) : 0;
  bool invalidClass = pc && pc->getClassStatus() != oClass::ClassStatus::Normal;
  oCounter counter(counterIn);

  static bool reentrantLock = false;
  if (reentrantLock == true) {
    reentrantLock = false;
    throw meosException("Internal list error");
  }
  bool doDefault = false;

  switch (pp.type) {
      case lControlName:
      case lPunchName:
        if (punch) {
          pCourse pc = r ? r->getCourse(false) : nullptr;
          if (punch->isFinish(pc ? pc->getFinishPunchType() : oPunch::PunchFinish)) {
            wsptr = &lang.tl("Mål");
          }
          else {
            pControl ctrl = getControl(punch->getControlId());
            if (!ctrl)
              ctrl = getControlByType(punch->type);

            if (ctrl && ctrl->hasName()) {
              swprintf_s(bfw, L"%s", ctrl->getName().c_str());
            }
          }
        }
        break;
  case lPunchTimeSinceLast:
    if (punch && punch->previousPunchTime && r && !invalidClass) {
      int time = punch->getTimeInt();
      int pTime = punch->previousPunchTime;
      if (pTime > 0 && time > pTime) {
        int t = time - pTime;
        wsptr = &formatTime(t);
      }
    }
    break;
  case lPunchTime:
  case lPunchTeamTime:
  case lPunchControlNumber:
  case lPunchControlCode:
  case lPunchLostTime:
  case lPunchControlPlace:
  case lPunchControlPlaceAcc:
  case lPunchControlPlaceTeamAcc:
  case lPunchSplitTime:
  case lPunchTotalTime:
  case lPunchTeamTotalTime:
  case lPunchAbsTime:
    if (punch && r && !invalidClass) {
      if (punch->tIndex >= 0) {
        // Punch in course
        counter.level3 = punch->tIndex;
        doDefault = true;
        break;
      }
      switch (pp.type) {
        case lPunchTime:
        case lPunchTeamTime: {
          if (punch->hasTime()) {
            int off = 0;
            if (pp.type == lPunchTeamTime && r->getTeam())
              off = r->getTeam()->getTotalRunningTimeAtLegStart(r->getLegNumber(), false);

            swprintf_s(bfw, L"\u2013 (%s)", formatTime(off + punch->getTimeInt() - r->getStartTime(), SubSecond::Off).c_str());
          }
          else {
            wsptr = &makeDash(L"- (-)");
          }
          break;
        }
        case lPunchSplitTime: {
          swprintf_s(bfw, L"\u2013");
          break;
        }
        case lPunchControlNumber: {
          wcscpy_s(bfw, L"\u2013");
          break;
        }
        case lPunchControlCode: {
          swprintf_s(bfw, L"%d", punch->getTypeCode());
          break;
        }
        case lPunchAbsTime: {
          if (punch->hasTime())
            wsptr = &getAbsTime(punch->getTimeInt(), SubSecond::Off);
          break;
        }
        case lPunchTotalTime: {
          if (punch->hasTime())
            wsptr = &formatTime(punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
          break;
        }
        case lPunchTeamTotalTime: {
          if (punch->hasTime()) {
            pTeam t = r->getTeam();
            if (!t || r->getLegNumber() == 0)
              wsptr = &formatTime(punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
            else {
              int input = t->getTotalRunningTimeAtLegStart(r->getLegNumber(), false);
              wsptr = &formatTime(input + punch->getTimeInt() - r->getStartTime(), SubSecond::Off);
            }
          }
          break;
        }
      }
    }
    break;

  default:
    doDefault = true;
  }

  if (doDefault) {
    reentrantLock = true;
    try {
      const wstring &res = formatListStringAux(pp, par, t, r,
                                                r ? r->getClubRef() : 0,
                                                r ? r->getClassRef(true) : 0, counter);
      reentrantLock = false;
      return res;
    }
    catch (...) {
      reentrantLock = false;
      throw;
    }
  }

  if (pp.type != lString && (wsptr == 0 || wsptr->empty()) && bfw[0] == 0)
    return _EmptyWString;
  else if (wsptr) {
    if (pp.text.empty())
      return *wsptr;
    else {
      swprintf_s(bfw, pp.text.c_str(), wsptr->c_str());
      wstring &res = StringCache::getInstance().wget();
      res = bfw;
      return res;
    }
  }
  else {
    if (pp.text.empty()) {
      wstring &res = StringCache::getInstance().wget();
      res = bfw;
      return res;
    }
    else {
      wchar_t bf2[512];
      swprintf_s(bf2, pp.text.c_str(), bfw);
      wstring &res = StringCache::getInstance().wget();
      res = bf2;
      return res;
    }
  }
}

const wstring &oEvent::formatSpecialStringAux(const oPrintPost &pp, const oListParam &par,
                                              const pTeam t, int legIndex,
                                              const pCourse pc, const pControl ctrl, 
                                              const oCounter &counter) const {

  wchar_t bfw[512];
  const wstring *wsptr=0;
  bfw[0] = 0;

  static bool reentrantLock = false;
  if (reentrantLock == true) {
    reentrantLock = false;
    throw meosException("Internal list error");
  }

  switch (pp.type) {
    case lCourseLength:
      if (pc) {
        int len = pc->getLength();
        if (len > 0)
          swprintf_s(bfw, L"%d", len);
      }
    break;

    case lTeamCourseName:
    case lCourseName:
    case lRunnerCourse:
      if (pc) {
        wsptr = &pc->getName();
      }
    break;

    case lCourseNumber:
    case lTeamCourseNumber:
      if (pc) {
        wsptr = &itow(pc->getId());
      }
    break;

    case lRunnerLegNumberAlpha:
      if (t && t->getClassRef(false) && legIndex >= 0) {
        wstring legStr = t->getClassRef(false)->getLegNumber(legIndex);
        wcscpy_s(bfw, legStr.c_str());
      }
      break;

    case lRunnerLegNumber:
      if (t && t->getClassRef(false) && legIndex >= 0) {
         int legNumber, legOrder;
         t->getClassRef(false)->splitLegNumberParallel(legIndex, legNumber, legOrder);
         wsptr = &itow(legNumber+1);
      }
      break;

    case lCourseClimb: {
      int len = pc ? pc->getDCI().getInt("Climb") : 0;
      if (len > 0)
        swprintf_s(bfw, L"%d", len);      
    }
    break;

    case lCourseUsage:
      if (pc) {
        wsptr = &itow(pc->getNumUsedMaps(false));
      }
    break;

    case lCourseUsageNoVacant:
      if (pc) {
        wsptr = &itow(pc->getNumUsedMaps(true));
      }
    break;

    case lCourseNumControls:
      if (pc)
        wsptr = &itow(pc->getNumControls());
    break;

    case lCourseClasses:
      if (pc) {
        vector<pClass> cls;
        pc->getClasses(cls);
        wstring tmp;
        for (size_t k = 0; k < cls.size(); k++) {
          if (k > 0)
            tmp += L", ";
          tmp += cls[k]->getName();
          if (tmp.length() > 100) {
            tmp += L", ...";
            break;
          }
        }
        wcsncpy_s(bfw, tmp.c_str(), 256);
      }
    break;

    case lControlName:
      if (ctrl)
        wcsncpy_s(bfw, ctrl->getName().c_str(), 128);
    break;

    case lControlCourses:
      if (ctrl) {
        vector<pCourse> crs;
        ctrl->getCourses(crs);
        if (crs.size() == Courses.size()) {
          wsptr = &lang.tl("Alla");
          break;
        }

        wstring tmp;
        for (size_t k = 0; k < crs.size(); k++) {
          if (k > 0)
            tmp += L", ";
          tmp += crs[k]->getName();
          if (tmp.length() > 100) {
            tmp += L", ...";
            break;
          }
        }
        wcsncpy_s(bfw, tmp.c_str(), 256);
      }
    break;
  
    case lControlClasses:
      if (ctrl) {
        vector<pClass> cls;
        ctrl->getClasses(cls);
        if (cls.size() == getNumClasses()) {
          wsptr = &lang.tl("Alla");
          break;
        }

        wstring tmp;
        for (size_t k = 0; k < cls.size(); k++) {
          if (k > 0)
            tmp += L", ";
          tmp += cls[k]->getName();
          if (tmp.length() > 100) {
            tmp += L", ...";
            break;
          }
        }
        wcsncpy_s(bfw, tmp.c_str(), 256);
      }
    break;

    case lControlVisitors:
      if (ctrl)
        wsptr = &itow(ctrl->getNumVisitors(false));
    break;

    case lControlPunches:
      if (ctrl)
        wsptr = &itow(ctrl->getNumVisitors(true));
      break;

    case lControlMedianLostTime:
      if (ctrl) 
        wsptr = &formatTime(ctrl->getMissedTimeMedian(), SubSecond::Off); 
    break;

    case lControlMaxLostTime:
      if (ctrl) 
        wsptr = &formatTime(ctrl->getMissedTimeMax(), SubSecond::Off);
      break;
    
    case lControlMistakeQuotient:
      if (ctrl) {
        wstring tmp = itow(ctrl->getMistakeQuotient()); 
        tmp += L"%";
        wcsncpy_s(bfw, tmp.c_str(), 20);
      }
      break;

    case lControlRunnersLeft:
      if (ctrl)
        wsptr = &itow(ctrl->getNumRunnersRemaining());
    break;

    case lControlCodes: 
      if (ctrl) {
        vector<int> numbers;
        ctrl->getNumbers(numbers);
        wstring tmp;
        for (size_t j = 0; j < numbers.size(); j++) {
          if (j > 0)
            tmp += L", ";
          tmp += itow(numbers[j]);
        }
        wcsncpy_s(bfw, tmp.c_str(), 256);
      }
      break;

    default: {
      reentrantLock = true;
      try {
        const wstring &res = formatListStringAux(pp, par, t, t ? t->getRunner(legIndex) : 0, 
                                                                 t ? t->getClubRef() : 0,
                                                                 t ? t->getClassRef(false) : 0, counter);
        reentrantLock = false;
        return res;
      }
      catch(...) {
        reentrantLock = false;
        throw;
      }
    }
  }

  if (pp.type!=lString && (wsptr==0 || wsptr->empty()) && bfw[0]==0)
    return _EmptyWString;
  else if (wsptr) {
    if (pp.text.empty())
      return *wsptr;
    else {
      swprintf_s(bfw, pp.text.c_str(), wsptr->c_str());
      wstring &res = StringCache::getInstance().wget();
      res = bfw;
      return res;
    }
  }
  else {
    if (pp.text.empty()) {
      wstring &res = StringCache::getInstance().wget();
      res = bfw;
      return res;
    }
    else {
      wchar_t bf2[512];
      swprintf_s(bf2, pp.text.c_str(), bfw);
      wstring &res = StringCache::getInstance().wget();
      res = bf2;
      return res;
    }
  }
}

const wstring &oEvent::formatListStringAux(const oPrintPost &pp, const oListParam &par,
                                          const pTeam t, const pRunner r, const pClub c,
                                          const pClass pc, const oCounter &counter) const {

  wchar_t wbf[512];
  const wstring *wsptr=0;
  wbf[0]=0;
  SubSecond mode = useSubSecond() ? SubSecond::On : SubSecond::Auto;

  auto noTimingRunner = [&]() {
    return (pc ? pc->getNoTiming() : false) || (r ? (r->getStatusComputed(true) == StatusNoTiming || r->noTiming()) : false);
  };
  auto noTimingTeam = [&]() {
    return (pc ? pc->getNoTiming() : false) || (t ? (t->getStatusComputed(true) == StatusNoTiming || t->noTiming()): false);
  };
  bool invalidClass = pc && pc->getClassStatus() != oClass::ClassStatus::Normal;
  int legIndex = pp.legIndex;
  if(pc && MetaList::isAllLegType(pp.type)) {
    if (legIndex == -1) {
      if (r)
        legIndex = r->getLegNumber();
      else
        legIndex = pc->getNumStages() - 1;
    }
    else if (legIndex < max<int>(1, pc->getNumStages()))
      legIndex = pc->getLinearIndex(pp.legIndex, pp.linearLegIndex);
  }    
  else if (pc && !MetaList::isResultModuleOutput(pp.type) && !MetaList::isAllStageType(pp.type)) {
    if (legIndex == -1) {
      if (r)
        legIndex = r->getLegNumber();
      else
        legIndex = pc->getNumStages() - 1;
    }
    else
      legIndex = pc->getLinearIndex(pp.legIndex, pp.linearLegIndex);
  }
        
  switch ( pp.type ) {
    case lClassName:
      if (invalidClass)
        swprintf_s(wbf, L"%s (%s)", pc->getName().c_str(), lang.tl("Struken").c_str());
      else
        wsptr=pc ? &pc->getName() : 0;
      break;
    case lResultDescription: {
        wstring title;
        getResultTitle(*this, par, title);
        wcscpy_s(wbf, title.c_str());
      }
      break;
    case lTimingFromName:
      if (par.useControlIdResultFrom > 0)
        wcscpy_s(wbf, getFullControlName(*this, par.useControlIdResultFrom).c_str());
      else
        wsptr = &lang.tl(L"Start");
      break;
    case lTimingToName:
      if (par.useControlIdResultTo > 0)
        wcscpy_s(wbf, getFullControlName(*this, par.useControlIdResultTo).c_str());
      else
        wsptr = &lang.tl(L"Mål");
      break;
    case lClassLength:
      if (pc) {
        wcscpy_s(wbf, pc->getLength(par.relayLegIndex).c_str());
      }
      break;
    case lClassStartName:
      if (pc) wcscpy_s(wbf, pc->getDI().getString("StartName").c_str());
      break;
    case lClassStartTime:
    case lClassStartTimeRange:
      if (pc) {
        int first, last;
        pc->getStartRange(legIndex, first, last);
        if (pc->hasFreeStart() || pc->hasRequestStart())
          wsptr = &lang.tl("Fri starttid");
        else if (first > 0 && first == last) {
          if (oe->useStartSeconds())
            wsptr = &oe->getAbsTime(first);
          else
            wsptr = &oe->getAbsTimeHM(first);
        }
        else if (pp.type == lClassStartTimeRange) {
          wstring range =  oe->getAbsTimeHM(first) + makeDash(L" - ") + oe->getAbsTimeHM(last);
          wcscpy_s(wbf, range.c_str());
        }
      }
      break;
    case lClassResultFraction:
      if (pc && !invalidClass) {
        int total, finished,  dns;
        pc->getNumResults(par.getLegNumber(pc), total, finished, dns);
        swprintf_s(wbf, L"(%d / %d)", finished, total);
      }
      break;
    case lCourseLength:
    case lCourseName:
    case lRunnerCourse:
    case lCourseNumber:
      if (r) {
        pCourse crs = r->getCourse(false);
        return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
      }
    break;

    case lTeamCourseNumber:
    case lTeamCourseName:
      if (r && pc && legIndex < pc->getNumStages()) {
        pCourse crs = r->getCourse(false);
        return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
      }
      else if (t && pc && legIndex < pc->getNumStages()) {
        pCourse crs = pc->getCourse(legIndex, t->getStartNo());
        return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
      }
      break;

    case lTeamLegName: 
      if (pc) {
        if (legIndex < pc->getNumStages())
          wcscpy_s(wbf, pc->getLegNumber(legIndex).c_str());
      }
      break;

    case lClassAvailableMaps:
      if (pc) {
        int n = pc->getNumRemainingMaps(false);
        if (n != numeric_limits<int>::min())
          wsptr = &itow(n);
      }
      break;

    case lClassTotalMaps:
      if (pc) {
        int n = pc->getNumberMaps();
        if (n > 0)
          wsptr = &itow(n);
      }
      break;
    
    case lClassNumEntries:
      if (pc) {
        int n = pc->getNumRunners(true, true, true);
        wsptr = &itow(n);
      }
      break;

    case lClassDataA:
      if (pc)
        wsptr = &itow(pc->getDCI().getInt("DataA"));
      break;

    case lClassDataB:
      if (pc)
        wsptr = &itow(pc->getDCI().getInt("DataB"));
      break;

    case lClassTextA:
      if (pc)
        wsptr = &pc->getDCI().getString("TextA");
      break;

    case lCourseClimb:
    case lCourseUsageNoVacant:
    case lCourseUsage:
      if (r) {
        pCourse crs = r->getCourse(false);
         return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
      }
    break;

    case lCourseNumControls:
      if (r) {
        pCourse crs = r->getCourse(true);
        return formatSpecialStringAux(pp, par, t, 0, crs, 0, counter);
      }
      break;

    case lCourseShortening:
      if (r) {
        int sh = r->getNumShortening();
        if (sh > 0)
          swprintf_s(wbf, L"%d", sh);
      }
      break;
    case lCmpName:
      wsptr = &getName();
      break;
    case lCmpDate:
      wsptr = &getDate();
      break;
    case lCurrentTime:
      wcscpy_s(wbf, getCurrentTimeS().c_str());
      break;
    case lRunnerClub:
      wsptr = (r && r->Club) ? &r->Club->getDisplayName() : 0;
      break;
    case lRunnerFinish:
      if (r && !invalidClass) {
        if (pp.resultModuleIndex == -1)
          wsptr = &r->getFinishTimeS(false, mode);
        else
          wsptr = &r->getTempResult(pp.resultModuleIndex).getFinishTimeS(this, mode);
      }
      break;
    case lRunnerStart:
    case lRunnerStartCond:
    case lRunnerStartZero:
      if (r) {
        if ((pp.type == lRunnerStartCond || pp.type == lRunnerStartZero) && pc && !pc->hasFreeStart()) {
          int fs, ls;
          pc->getStartRange(legIndex, fs, ls);
          if (fs>0 && fs == ls) {
            break; // Common start time, skip
          }
        }
        if (r->getStatus() == StatusCANCEL) {
          wsptr = &oEvent::formatStatus(StatusCANCEL, true);
        }
        else if (r->startTimeAvailable()) {
          if (pp.type != lRunnerStartZero) 
            wsptr = &r->getStartTimeCompact();
          else {
            int st = r->getStartTime();
            wsptr = &formatTimeMS(st-oe->getFirstStart(), true);
          }
        }
        else
          wsptr = &makeDash(L"-");
      }
      break;
    case lRunnerCheck:
      if (r && !invalidClass && r->Card) {
        oPunch *punch = r->Card->getPunchByType(oPunch::PunchCheck);
        if (punch && punch->hasTime())
          wsptr = &getAbsTime(punch->getTimeInt());
        else
          wsptr = &makeDash(L"-"); 
      }
      break;

    case lRunnerName:
      wsptr = r ? &r->getName() : 0;
      break;
    case lRunnerGivenName:
      if (r) wcscpy_s(wbf, r->getGivenName().c_str());
      break;
    case lRunnerFamilyName:
      if (r) wcscpy_s(wbf, r->getFamilyName().c_str());
      break;
    case lRunnerCompleteName:
      if (r) wcscpy_s(wbf, r->getCompleteIdentification().c_str());
      break;
    case lRunnerLegTeamLeaderName: {
      pRunner rr = r;
      if (t && r)
        rr = t->getRunnerBestTimePar(r->getLegNumber());
      else if (t)
        rr = t->getRunnerBestTimePar(legIndex);

      if (rr)
        swprintf_s(wbf, L"%s", rr->formatName(oRunner::NameFormat::InitLast).c_str());
    }
    break;
    case lPatrolNameNames:
      if (t) {
        pRunner r1 = t->getRunner(0);
        pRunner r2 = t->getRunner(1);
        if (r1 && r2 && r2->tParentRunner != r1) {
          swprintf_s(wbf, L"%s / %s", r1->getName().c_str(),r2->getName().c_str());
        }
        else if (r1) {
          wsptr = &r1->getName();
        }
        else if (r2) {
          wsptr = &r2->getName();
        }
      }
      else {
        wsptr = r ? &r->getName() : 0;
      }
      break;
    case lPatrolClubNameNames:
      if (t) {
        pRunner r1 = t->getRunner(0);
        pRunner r2 = t->getRunner(1);
        pClub c1 = r1 ? r1->Club : 0;
        pClub c2 = r2 ? r2->Club : 0;
        if (c1 == c2)
          c2 = 0;
        if (c1 && c2) {
          swprintf_s(wbf, L"%s / %s", c1->getDisplayName().c_str(),c2->getDisplayName().c_str());
        }
        else if (c1) {
          wsptr = &c1->getDisplayName();
        }
        else if (c2) {
          wsptr = &c2->getDisplayName();
        }
      }
      else {
        wsptr = r && r->Club ? &r->Club->getDisplayName() : 0;
      }
      break;
    case lRunnerTime:
      if (r && !invalidClass) {
        if (pp.resultModuleIndex == -1)
          wsptr = &r->getRunningTimeS(true, mode);
        else
          wsptr = &r->getTempResult(pp.resultModuleIndex).getRunningTimeS(0, mode);

        if (r->getNumShortening() > 0) {
          swprintf_s(wbf, L"*%s", wsptr->c_str());
          wsptr = 0;
        }
      }
      break;
    case lRunnerGrossTime:
      if (r && !invalidClass) {
        int tm = r->getRunningTime(true);
        if (tm > 0)
          tm -= r->getTimeAdjustment(true);

        wsptr = &formatTime(tm);
      }
      break;

    case lRunnerTimeStatus:
      if (r) {
        if (invalidClass)
          wsptr = &lang.tl("Struken");
        else if (pp.resultModuleIndex == -1) {
          bool ok = r->prelStatusOK(true, true, true);
          if (ok && !noTimingRunner()) {
            wsptr = &r->getRunningTimeS(true, mode);
            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
            else if (r->getStatusComputed(true) == StatusOutOfCompetition) {
              swprintf_s(wbf, L"(%s)", wsptr->c_str());
              wsptr = 0;
            }
          }
          else {
            if (ok)
              wsptr = &formatStatus(StatusOK, true);
            else
              wsptr = &r->getStatusS(true, true);
          }
        }
        else {
          const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex);
          if (res.isStatusOK() && !noTimingRunner()) {
            wsptr = &res.getRunningTimeS(0, mode);
            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
            else if (res.getStatus() == StatusOutOfCompetition) {
              swprintf_s(wbf, L"(%s)", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &res.getStatusS(StatusOK);
        }
      }
      break;

    case lRunnerGeneralTimeStatus:
      if (r) {
        if (invalidClass)
          wsptr = &lang.tl("Struken");
        else if (pp.resultModuleIndex == -1) {
          if (r->prelStatusOK(true, true, true) && !noTimingRunner()) {
            wstring timeStatus = r->getRunningTimeS(true, mode);
            
            if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) {
              RunnerStatus ts = r->getTotalStatus();
              int rt = r->getTotalRunningTime();
              if (ts == StatusOK || (ts == StatusUnknown && rt > 0)) {
                wstring vts = formatTime(rt) + L" (" + timeStatus + L")";
                swap(vts, timeStatus);
              }
              else {
                wstring vts = formatStatus(ts, true) + L" (" + timeStatus + L")";
                swap(vts, timeStatus);
              }
            }

            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", timeStatus.c_str());
            }
            else {
              wcscpy_s(wbf, timeStatus.c_str());
            }
          }
          else
            wsptr = &r->getStatusS(true, true);
        }
        else {
          const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex);
          if (res.isStatusOK() && !noTimingRunner()) {
            wsptr = &res.getRunningTimeS(0, mode);
            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
            else if (res.getStatus() == StatusOutOfCompetition) {
              swprintf_s(wbf, L"(%s)", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &res.getStatusS(StatusOK);
        }
      }
      break;

      break;

    case lRunnerGeneralTimeAfter:
      if (r && pc && !invalidClass && !noTimingRunner()) {
        if (pp.resultModuleIndex == -1) {

          if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) {
            int tleg = r->tLeg >= 0 ? r->tLeg:0;
            if (r->getTotalStatus()==StatusOK) {
              if ( (t && t->getNumShortening(tleg) == 0) || (!t && r->getNumShortening() == 0)) { 
                int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
                if (after > 0)
                  swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
              }
              else {
                wsptr = &makeDash(L"-");
              }
            }
          }
          else {
            int tleg=r->tLeg>=0 ? r->tLeg:0;
            if (r->tStatus==StatusOK && pc && !noTimingRunner()) {
              if (r->getNumShortening() == 0) {
                int after = r->getRunningTime(true) - pc->getBestLegTime(oClass::AllowRecompute::Yes, tleg, true);
                if (after > 0)
                  swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
              }
              else {
                wsptr = &makeDash(L"-");
              }
            }
          }
        }
        else {
          int after = r->getTempResult(pp.resultModuleIndex).getTimeAfter();
          if (after > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
        }
      }
      break;

    case lRunnerTimePerKM:
      if (r && !invalidClass && r->prelStatusOK(true, true, true)) {
        const pCourse pc = r->getCourse(false);
        if (pc) {
          int t = r->getRunningTime(false);
          int len = pc->getLength();
          if (len > 0 && t > 0) {
            int sperkm = (1000 * t) / len;
            wsptr = &formatTime(sperkm, SubSecond::Off);
          }
        }
      }
      break;
    case lRunnerTotalTime:
      if (r && !invalidClass) {
        if (pp.resultModuleIndex == -1)
          wsptr = &r->getTotalRunningTimeS(mode);
        else
          wsptr = &r->getTempResult(pp.resultModuleIndex).getRunningTimeS(r->getTotalTimeInput(), mode);
      }
      break;
    case lRunnerTotalTimeStatus:
      if (invalidClass)
        wsptr = &lang.tl("Struken");
      else if (r) {
        if (pp.resultModuleIndex == -1) {
          if ((r->getTotalStatus()==StatusOK || (r->getTotalStatus()==StatusUnknown 
            && r->prelStatusOK(true, true, true) && r->getInputStatus() == StatusOK) ) && !noTimingRunner()) {
            wsptr = &r->getTotalRunningTimeS(mode);
            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &r->getTotalStatusS(true);
        }
        else {
          const oAbstractRunner::TempResult &res = r->getTempResult(pp.resultModuleIndex);
          RunnerStatus input = r->getTotalStatusInput();
          if (input == StatusOK && res.getStatus() == StatusOK && !noTimingRunner()) {
            wsptr = &res.getRunningTimeS(r->getTotalTimeInput(), mode);
            if (r->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &res.getStatusS(input);
        }
      }
      break;
    case lRunnerTempTimeStatus:
      if (invalidClass)
          wsptr = &lang.tl("Struken");
      else if (r) {
        if (showResultTime(r->tempStatus, r->tempRT) && !noTimingRunner())
          wcscpy_s(wbf, formatTime(r->tempRT).c_str());
        else
          wcscpy_s(wbf, formatStatus(r->tempStatus, true).c_str() );
      }
      break;

    case lRunnerCardVoltage:
      if (r && r->getCard()) {
        wcscpy_s(wbf, r->getCard()->getCardVoltage().c_str());
      }
      break;

    case lRunnerStageNumber:
      if (pp.legIndex >= 0)
        swprintf_s(wbf, L"%d", pp.legIndex + 1);
      break;

    case lRunnerStageTimeStatus:
      if (invalidClass)
        wsptr = &lang.tl("Struken");
      else if (r) {
        wstring tmp;
        int time, d;
        RunnerStatus st = r->getStageResult(pp.legIndex, time, d, d);
        if (showResultTime(st, time) && !noTimingRunner())
          wcscpy_s(wbf, formatTime(time).c_str());
        else
          wcscpy_s(wbf, formatStatus(st, true).c_str());
      }
      break;

    case lRunnerStageTime:
      if (invalidClass)
        wsptr = &lang.tl("Struken");
      else if (r) {
        wstring tmp;
        int time, d;
        RunnerStatus st = r->getStageResult(pp.legIndex, time, d, d);
        if (time > 0 && !noTimingRunner())
          wcscpy_s(wbf, formatTime(time).c_str());
      }
      break;

    case lRunnerStageStatus:
      if (invalidClass)
        wsptr = &lang.tl("Struken");
      else if (r) {
        wstring tmp;
        int time, d;
        RunnerStatus st = r->getStageResult(pp.legIndex, time, d, d);
        wcscpy_s(wbf, formatStatus(st, true).c_str());
      }
      break;

    case lRunnerStagePoints:
      if (!invalidClass && r) {
        int points, d;
        RunnerStatus st = r->getStageResult(pp.legIndex, d, points, d);
        swprintf_s(wbf, L"%d", points);
      }
      break;

    case lRunnerPlace:
      if (r && !invalidClass && !noTimingRunner()) {
        if (pp.resultModuleIndex == -1)
          wcscpy_s(wbf, r->getPrintPlaceS(pp.text.empty()).c_str() );
        else
          wsptr = &r->getTempResult(pp.resultModuleIndex).getPrintPlaceS(pp.text.empty());
      }
      break;
    case lRunnerTotalPlace:
      if (r && !invalidClass && !noTimingRunner())
        wcscpy_s(wbf, r->getPrintTotalPlaceS(pp.text.empty()).c_str() );
      break;
    case lRunnerStagePlace:
      if (r && !invalidClass && !noTimingRunner()) {
        int d, place;
        wstring tmp;
        r->getStageResult(pp.legIndex, d, d, place);
        if (place > 0) {
          tmp = itow(place);
          if (pp.text.empty())
            tmp += L".";
        }
        wcscpy_s(wbf, tmp.c_str());
      }
      break;
    case lRunnerGeneralPlace:
      if (r && !invalidClass && pc && !noTimingRunner()) {
        if (pp.resultModuleIndex == -1) {
          if (r->hasInputData() || (r->getLegNumber() > 0 && !r->isPatrolMember())) {
            wstring iPlace;
            if (pc->getClassType() != oClassPatrol)
              iPlace = r->getPrintPlaceS(true);

            wstring tPlace = r->getPrintTotalPlaceS(true); 
            wstring v;
            if (iPlace.empty())
              v = tPlace;
            else {
              if (tPlace.empty())
                tPlace = makeDash(L"-");
              v = tPlace + L" (" + iPlace + L")";
            }
            wcscpy_s(wbf, v.c_str());
          }
          else
            wcscpy_s(wbf, r->getPrintPlaceS(pp.text.empty()).c_str() );
        }
        else
          wsptr = &r->getTempResult(pp.resultModuleIndex).getPrintPlaceS(pp.text.empty());
      }
      break;

    case lRunnerClassCoursePlace:
      if (r && !invalidClass && !noTimingRunner()) {
        int p = r->getCoursePlace(true);
        if (p>0 && p<10000)
          swprintf_s(wbf, L"%d.", p);
      }
      break;

    case lRunnerCoursePlace:
      if (r && !invalidClass && !noTimingRunner()) {
        int p = r->getCoursePlace(false);
        if (p>0 && p<10000)
          swprintf_s(wbf, L"%d.", p);
      }
      break;
    case lRunnerPlaceDiff:
      if (r && !invalidClass && !noTimingRunner()) {
        int p = r->getTotalPlace();
        if (r->getTotalStatus() == StatusOK && p > 0 && r->inputPlace>0) {
          int pd = p - r->inputPlace;
          if (pd > 0)
            swprintf_s(wbf, L"+%d", pd);
          else if (pd < 0)
            swprintf_s(wbf, L"-%d", -pd);
        }
      }
      break;
    case lRunnerTimeAfterDiff:
      if (r && pc && !invalidClass) {
        int tleg = r->tLeg >= 0 ? r->tLeg:0;
        if (r->getTotalStatus() == StatusOK && pc && !noTimingRunner()) {
          int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
          int afterOld = r->inputTime - pc->getBestInputTime(oClass::AllowRecompute::Yes, tleg);
          int ad = after - afterOld;
          if (ad > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(ad, false, mode).c_str());
          if (ad < 0)
            wsptr = &formatTimeMS(ad, false, mode);
        }
      }
      break;
    case lRunnerRogainingPoint:
      if (r && !invalidClass) {
        if (pp.resultModuleIndex == -1) 
          swprintf_s(wbf, L"%d", r->getRogainingPoints(true, false));
        else
          swprintf_s(wbf, L"%d", r->getTempResult(pp.resultModuleIndex).getPoints());
      }
      break;

    case lRunnerRogainingPointTotal:
      if (r && !invalidClass) {
        if (pp.resultModuleIndex == -1) 
          swprintf_s(wbf, L"%d", r->getRogainingPoints(true, true));
        else
          swprintf_s(wbf, L"%d", r->getTempResult(pp.resultModuleIndex).getPoints() + r->getInputPoints());
      }
      break;

    case lRunnerRogainingPointReduction:
      if (r && !invalidClass) {
        int red = r->getRogainingReduction(true);
        if (red > 0)
          swprintf_s(wbf, L"-%d", red);
      }
      break;
    
    case lRunnerRogainingPointGross:
      if (r && !invalidClass) {
        int p = r->getRogainingPointsGross(true);
        wsptr = &itow(p);
      }
      break;
    
    case lRunnerPointAdjustment:
      if (r && !invalidClass) {
        int a = r->getPointAdjustment();
        if (a<0)
          wsptr = &itow(a);
        else if (a>0)
          swprintf_s(wbf, L"+%d", a);
      }
      break;
    case lRunnerTimeAdjustment:
      if (r && !invalidClass) {
        int a = r->getTimeAdjustment(true);
        if (a != 0)
          wsptr = &formatTimeMS(a, false);
      }
      break;
    case lRunnerRogainingPointOvertime:
      if (r && !invalidClass) {
        int over = r->getRogainingOvertime(true);
        if (over > 0)
          wsptr = &formatTime(over);
      }
      break;

    case lRunnerTimeAfter:
      if (r && pc && !invalidClass && !noTimingRunner()) {
        int after = 0;
        if (pp.resultModuleIndex == -1) {
          int tleg=r->tLeg>=0 ? r->tLeg:0;
          int brt = pc->getBestLegTime(oClass::AllowRecompute::Yes, tleg, true);
          if (r->prelStatusOK(true, true, true) && brt > 0) {
            after=r->getRunningTime(true) - brt;
          }
        }
        else {
          after = r->getTempResult(pp.resultModuleIndex).getTimeAfter();
        }
  
        if (r->getNumShortening() == 0) {
          if (after > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
        }
        else {
          wsptr = &makeDash(L"-");
        }
      }
      break;
    case lRunnerTotalTimeAfter:
      if (r && pc && !invalidClass) {
        int tleg = r->tLeg >= 0 ? r->tLeg:0;
        if (r->getTotalStatus()==StatusOK &&  pc && !noTimingRunner()) {
          if ( (t && t->getNumShortening(tleg) == 0) || (!t && r->getNumShortening() == 0)) { 
            int after = r->getTotalRunningTime() - pc->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
            if (after > 0)
              swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
          }
          else {
            wsptr = &makeDash(L"-");
          }
        }
      }
      break;
    case lRunnerClassCourseTimeAfter:
      if (r && pc && !invalidClass) {
        pCourse crs = r->getCourse(false);
        if (crs && r->tStatus==StatusOK && !noTimingRunner()) {
          int after = r->getRunningTime(true) - pc->getBestTimeCourse(oClass::AllowRecompute::Yes, crs->getId());
          if (after > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
        }
      }
      break;
    case lRunnerTimePlaceFixed:
      if (r && !invalidClass) {
        int t = r->getTimeWhenPlaceFixed();
        if (t == 0 || (t > 0 && t < getComputerTime())) {
          wcscpy_s(wbf, lang.tl("klar").c_str());
        }
        else if (t == -1)
          wcscpy_s(wbf, L"-");
        else
          wcscpy_s(wbf, oe->getAbsTime(t).c_str());
      }
      break;
    case lRunnerLegNumberAlpha:
    case lRunnerLegNumber:
      if (r)
        return formatSpecialStringAux(pp, par, t, r->getLegNumber(), 0, 0, counter);
      else
        wcscpy_s(wbf, par.getLegName().c_str());
      break;
    case lRunnerLostTime:
      if (r && r->prelStatusOK(true, true, true) && !noTimingRunner() && !invalidClass) {
        wcscpy_s(wbf, r->getMissedTimeS().c_str());
      }
      break;
    case lRunnerTempTimeAfter:
      if (r && pc) {
        if (r->tempStatus == StatusOK && pc && !noTimingRunner()
            && r->tempRT > pc->tLegLeaderTime) {
          int after = r->tempRT - pc->tLegLeaderTime;
          swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
        }
      }
      break;

    case lRunnerCard:
      if (r && r->getCardNo() > 0)
        swprintf_s(wbf, L"%d", r->getCardNo());
      break;
    case lRunnerRank:
      if (r) {
        int rank = r->getRanking(); 
        if (rank>0 && rank < MaxOrderRank)
          wcscpy_s(wbf, formatRank(rank).c_str());
      }
      break;
    case lRunnerRankScore:
      if (r) {
        wcscpy_s(wbf, r->getRankingScore().c_str());
      }
      break;
    case lRunnerBib:
      if (r) {
        const wstring &bib = r->getBib();
        if (!bib.empty())
          wsptr = &bib;
      }
      break;
    case lRunnerRentalCard:
      if (r && r->isRentalCard()) {
        wsptr = &lang.tl("Hyrd");
      }
      break;

    case lRunnerUMMasterPoint:
      if (r) {
        int total, finished, dns;
        pc->getNumResults(par.getLegNumber(pc), total, finished, dns);
        int percent = int(floor(0.5+double((100*(total-dns-r->getPlace()))/double(total-dns))));
        if (r->getStatus()==StatusOK)
          swprintf_s(wbf, L"%d",  percent);
        else
          swprintf_s(wbf, L"0");
      }
      break;
    case lRunnerAge:
      if (r) {
        int y = r->getBirthAge();
        if (y > 0)
          swprintf_s(wbf, L"%d", y);
      }
    break;
    case lRunnerBirthYear:
      if (r) {
        int y = r->getBirthYear();
        if (y > 0)
          swprintf_s(wbf, L"%d", y);
      }
    break;
    case lRunnerBirthDate:
      if (r) {
        wsptr = &r->getBirthDate();
      }
      break;
    case lRunnerSex:
      if (r) {
        PersonSex s = r->getSex();
        if (s == sFemale)
          wsptr = &lang.tl("Kvinna");
        else if (s == sMale)
          wsptr = &lang.tl("Man");
      }
    break;
    case lRunnerPhone:
      if (r) {
        wsptr = &r->getDCI().getString("Phone");
      }
    break;
    case lRunnerFee:
      if (r) {
        wstring s = formatCurrency(r->getDCI().getInt("Fee"));
        wcscpy_s(wbf, s.c_str());
      }
    break;
    case lRunnerExpectedFee:
      if (r) {
        wstring s = formatCurrency(r->getDefaultFee());
        wcscpy_s(wbf, s.c_str());
      }
      break;
    case lRunnerPaid:
      if (r) {
        wstring s = formatCurrency(r->getDCI().getInt("Paid"));
        wcscpy_s(wbf, s.c_str());
      }
      break;
    case lRunnerId:
      if (r) {
        wstring s = r->getExtIdentifierString();
        wcscpy_s(wbf, s.c_str());
      }
      break;
    case lRunnerPayMethod:
      if (r) {
        wsptr = &r->getDCI().formatString(r, "PayMode");
      }
      break;
    case lRunnerEntryDate:
      if (r && r->getDCI().getInt("EntryDate") > 0) {
        wsptr = &r->getDCI().getDate("EntryDate");
      }
      break;
    case lRunnerEntryTime:
      if (r) {
        wsptr = &formatTime(r->getDCI().getInt("EntryTime"));
      }
      break;
    case lTeamFee:
      if (t) {
        wstring s = formatCurrency(t->getTeamFee());
        wcscpy_s(wbf, s.c_str());
      }
    break;
    case lRunnerNationality:
      if (r) {
        wstring nat = r->getNationality();
        wcscpy_s(wbf, nat.c_str());
      }
    break;
    case lTeamName:
      //sptr = t ? &t->Name : 0;
      if (t) {
        wcscpy_s(wbf, t->getDisplayName().c_str());
      }
      break;
    case lTeamStart:
    case lTeamStartCond:
    case lTeamStartZero:
      if (t) {
        if ((pp.type == lTeamStartCond || pp.type == lTeamStartZero) && pc && !pc->hasFreeStart()) {
          int fs, ls;
          pc->getStartRange(legIndex, fs, ls);
          if (fs>0 && fs == ls) {
            break; // Common start time, skip
          }
        }

        if (unsigned(legIndex)<t->Runners.size() && t->Runners[legIndex] && t->Runners[legIndex]->startTimeAvailable()) {
          if (pp.type != lTeamStartZero) 
            wsptr = &t->Runners[legIndex]->getStartTimeCompact();
          else {
            int st = t->Runners[legIndex]->getStartTime();
            wsptr = &formatTimeMS(st-timeConstHour, true);
          }
        }
      }
      break;
    case lTeamStatus:
      if (t && !invalidClass) {
        if (pp.resultModuleIndex == -1)
          wsptr = &t->getLegStatusS(legIndex, true, false);
        else
          wsptr = &t->getTempResult(pp.resultModuleIndex).getStatusS(StatusOK);
      }
      break;
    case lTeamTime:
      if (t && !invalidClass) {
        if (pp.resultModuleIndex == -1)
          wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, false, mode).c_str() );
        else
          wsptr = &t->getTempResult(pp.resultModuleIndex).getRunningTimeS(0, mode);
      }
      break;
    case lTeamGrossTime:
      if (t && !invalidClass) {
        int tm = t->getLegRunningTimeUnadjusted(legIndex, false, false);
        wsptr = &formatTime(tm);
      }
      break;
    case lTeamTimeStatus:
      if (invalidClass)
          wsptr = &lang.tl("Struken");
      else if (t) {
        if (pp.resultModuleIndex == -1) {
          RunnerStatus st = t->getLegStatus(legIndex, true, false);
          if (st == StatusOK || ((st == StatusUnknown || st == StatusOutOfCompetition) && t->getLegRunningTime(legIndex, true, false) > 0)) {
            if (st != StatusOutOfCompetition)
              wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, false, mode).c_str());
            else 
              swprintf_s(wbf, L"(%s)", t->getLegRunningTimeS(legIndex, true, false, mode).c_str());
          }
          else
            wsptr = &t->getLegStatusS(legIndex, true, false);
        }
        else {
          RunnerStatus st = t->getTempResult(pp.resultModuleIndex).getStatus();
          if (st == StatusOK || st == StatusUnknown) {
            wsptr = &t->getTempResult(pp.resultModuleIndex).getRunningTimeS(0, mode);
            if (t->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &t->getTempResult(pp.resultModuleIndex).getStatusS(StatusOK);
        }
      }
      break;
    case lTeamRogainingPoint:
      if (t && !invalidClass) {
        if (pp.resultModuleIndex == -1) 
          swprintf_s(wbf, L"%d", t->getRogainingPoints(true, false));
        else
          swprintf_s(wbf, L"%d", t->getTempResult(pp.resultModuleIndex).getPoints());
      }
      break;
    case lTeamRogainingPointTotal:
      if (t && !invalidClass) {
        if (pp.resultModuleIndex == -1) 
          swprintf_s(wbf, L"%d", t->getRogainingPoints(true, true));
        else
          swprintf_s(wbf, L"%d", t->getTempResult(pp.resultModuleIndex).getPoints() + t->getInputPoints());
      }
      break;

    case lTeamRogainingPointReduction:
      if (t && !invalidClass) {
        int red = t->getRogainingReduction(true);
        if (red > 0)
          swprintf_s(wbf, L"-%d", red);
      }
      break;

    case lTeamRogainingPointOvertime:
      if (t && !invalidClass) {
        int over = t->getRogainingOvertime(true);
        if (over > 0)
          wsptr = &formatTime(over);
      }
      break;

   case lTeamPointAdjustment:
      if (t && !invalidClass) {
        int a = t->getPointAdjustment();
        if (a<0)
          wsptr = &itow(a);
        else if (a>0)
          wprintf_s(wbf, "+%d", a);
      }
      break;

    case lTeamTimeAdjustment:
      if (t && !invalidClass) {
        int a = t->getTimeAdjustment(true);
        if (a != 0)
          wsptr = &formatTimeMS(a, false);
      }
      break;

    case lTeamTimeAfter:
      if (t && !invalidClass) {
        if (pp.resultModuleIndex == -1) {
          if (t->getLegStatus(legIndex, true, false)==StatusOK) {
            if (t->getNumShortening(legIndex) == 0) {
              int ta=t->getTimeAfter(legIndex, true);
              if (ta>0)
                swprintf_s(wbf, L"+%s", formatTimeMS(ta, false, mode).c_str());
            }
            else {
              wsptr = &makeDash(L"-");
            }
          }
        }
        else {
          if (t->getTempResult().getStatus() == StatusOK) {
            int after = t->getTempResult(pp.resultModuleIndex).getTimeAfter();
            if (after > 0)
              swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
          }
        }
      }
      break;
    case lTeamPlace:
      if (t && !invalidClass && !noTimingTeam() && pc && pc->isValidLeg(legIndex)) {
        if (pp.resultModuleIndex == -1) {
          wcscpy_s(wbf, t->getLegPrintPlaceS(legIndex, false, pp.text.empty()).c_str());
        }
        else
          wsptr = &t->getTempResult(pp.resultModuleIndex).getPrintPlaceS(pp.text.empty());
      }
      break;

    case lTeamLegTimeStatus:
      if (invalidClass)
        wsptr = &lang.tl("Struken");
      else if (t) {
        /*int ix = r ? r->getLegNumber() : counter.level3;
        if (pc)
          ix = pc->getResultDefining(ix);*/
        int ix = legIndex;
        if (pc) {
          if (!pc->isValidLeg(legIndex))
            break;
          ix = pc->getResultDefining(ix);
        }
        RunnerStatus st = t->getLegStatus(ix, true, false);
        if (st == StatusOK)
          wcscpy_s(wbf, t->getLegRunningTimeS(ix, true, false, mode).c_str() );
        else if (st == StatusOutOfCompetition && t->getLegRunningTime(ix, true, false) > 0)
          swprintf_s(wbf, L"(%s)", t->getLegRunningTimeS(ix, true, false, mode).c_str());
        else
          wcscpy_s(wbf, t->getLegStatusS(ix, true, false).c_str() );
      }
      break;
    case lTeamLegTimeAfter:
      if (t) {
        /*int ix = r ? r->getLegNumber() : counter.level3; xxx
        if (pc)
          ix = pc->getResultDefining(ix);
        */
        int ix = legIndex;
        if (pc) {
          if (!pc->isValidLeg(legIndex))
            break;
          ix = pc->getResultDefining(ix);
        }
        if (t->getLegStatus(ix, true, false)==StatusOK && !invalidClass) {
          if (t->getNumShortening(ix) == 0) {
            int ta=t->getTimeAfter(ix, true);
            if (ta>0)
              swprintf_s(wbf, L"+%s", formatTimeMS(ta, false, mode).c_str());
          }
          else {
            wsptr = &makeDash(L"-");
          }
        }
      }
      break;
    case lTeamClub:
      if (t) {
        wcscpy_s(wbf, t->getDisplayClub().c_str());
      }
      break;
    case lTeamRunner:
      if (t && unsigned(legIndex)<t->Runners.size() && t->Runners[legIndex])
        wsptr=&t->Runners[legIndex]->getName();
      break;
    case lTeamRunnerCard:
      if (t && unsigned(legIndex)<t->Runners.size() && t->Runners[legIndex]
      && t->Runners[legIndex]->getCardNo()>0)
        swprintf_s(wbf, L"%d", t->Runners[legIndex]->getCardNo());
      break;
    case lTeamBib:
      if (t) {
        wsptr = &t->getBib();
      }
      break;
    case lTeamTotalTime:
      if (t && !invalidClass) wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, true, mode).c_str() );
      break;
    case lTeamTotalTimeStatus:
      if (invalidClass)
          wsptr = &lang.tl("Struken");
      else if (t) {
        if (pp.resultModuleIndex == -1) {
          if (t->getLegStatus(legIndex, true, true)==StatusOK)
            wcscpy_s(wbf, t->getLegRunningTimeS(legIndex, true, true, mode).c_str() );
          else
            wcscpy_s(wbf, t->getLegStatusS(legIndex, true, true).c_str() );
        }
        else {
          RunnerStatus st = t->getTempResult(pp.resultModuleIndex).getStatus();
          RunnerStatus inp = t->getInputStatus();
          if (st == StatusOK && inp == StatusOK) {
            wsptr = &t->getTempResult(pp.resultModuleIndex).getRunningTimeS(0, mode);
            if (t->getNumShortening() > 0) {
              swprintf_s(wbf, L"*%s", wsptr->c_str());
              wsptr = 0;
            }
          }
          else
            wsptr = &t->getTempResult(pp.resultModuleIndex).getStatusS(StatusOK);
        }
      }
      break;
    case lTeamPlaceDiff:
      if (t && !invalidClass) {
        int p = t->getTotalPlace();
        if (t->getTotalStatus() == StatusOK && p > 0 && t->inputPlace>0) {
          int pd = p - t->inputPlace;
          if (pd > 0)
            swprintf_s(wbf, L"+%d", pd);
          else if (pd < 0)
            swprintf_s(wbf, L"-%d", -pd);
        }
      }
      break;
    case lTeamTotalPlace:
      if (t && !invalidClass && !noTimingTeam()) wcscpy_s(wbf, t->getPrintTotalPlaceS(pp.text.empty()).c_str() );
      break;
    case lTeamTotalTimeAfter:
      if (t && pc && !invalidClass) {
        int tleg = t->getNumRunners() - 1;
        if (t->getTotalStatus()==StatusOK &&  pc && !noTimingTeam()) {
          int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
          if (after > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(after, false, mode).c_str());
        }
      }
      break;
    case lTeamTotalTimeDiff:
      if (t && pc && !invalidClass) {
        int tleg = t->getNumRunners() - 1;
        if (t->getTotalStatus()==StatusOK &&  pc && !noTimingTeam()) {
          int after = t->getTotalRunningTime() - pc->getTotalLegLeaderTime(oClass::AllowRecompute::Yes, tleg, true, true);
          int afterOld = t->inputTime - pc->getBestInputTime(oClass::AllowRecompute::Yes, tleg);
          int ad = after - afterOld;
          if (ad > 0)
            swprintf_s(wbf, L"+%s", formatTimeMS(ad, false, mode).c_str()); 
          if (ad < 0)
            wsptr = &formatTimeMS(ad, false, mode);
        }
      }
      break;
    case lTeamStartNo:
      if (t)
        swprintf_s(wbf, L"%d", t->getStartNo());
      break;
    case lRunnerStartNo:
      if (r)
        swprintf_s(wbf, L"%d", r->getStartNo());
      break;
    case lRunnerDataA:
      if (r)
        wsptr = &itow(r->getDCI().getInt("DataA"));
      break;
    case lRunnerDataB:
      if (r)
        wsptr = &itow(r->getDCI().getInt("DataB"));
      break;
    case lRunnerTextA:
      if (r)
        wsptr = &r->getDCI().getString("TextA");
      break;

    case lTeamDataA:
      if (t)
        wsptr = &itow(t->getDCI().getInt("DataA"));
      break;
    case lTeamDataB:
      if (t)
        wsptr = &itow(t->getDCI().getInt("DataB"));
      break;
    case lTeamTextA:
      if (t)
        wsptr = &t->getDCI().getString("TextA");
      break;
    case lNationality:
      if (r && !(wsptr = &r->getDCI().getString("Nationality"))->empty())
        break;
      else if (t && !(wsptr = &t->getDCI().getString("Nationality"))->empty())
        break;
      else if (c && !(wsptr = &c->getDCI().getString("Nationality"))->empty())
        break;

      break;

    case lCountry:
      if (r && !(wsptr = &r->getDCI().getString("Country"))->empty())
        break;
      else if (t && !(wsptr = &t->getDCI().getString("Country"))->empty())
        break;
      else if (c && !(wsptr = &c->getDCI().getString("Country"))->empty())
        break;

      break;
    case lControlName:
    case lPunchName:
    case lPunchNamedTime:
    case lPunchTeamTotalNamedTime:
    case lPunchNamedSplit:
    case lPunchTime:
    case lPunchTeamTime:
    case lPunchSplitTime:
    case lPunchTotalTime:
    case lPunchTeamTotalTime:
    case lPunchControlNumber:
    case lPunchControlCode:
    case lPunchLostTime:
    case lPunchControlPlace:
    case lPunchControlPlaceAcc:
    case lPunchControlPlaceTeamAcc:
    case lPunchAbsTime:
    case lPunchTotalTimeAfter:
    case lPunchTeamTotalTimeAfter:
      if (r && r->getCourse(false) && !invalidClass) {
        const pCourse crs=r->getCourse(true);
        const oControl *ctrl = nullptr;
        int nCtrl = crs->getNumControls();
        if (counter.level3 != nCtrl) { // Always allow finish
          ctrl=crs->getControl(counter.level3);
          if (!ctrl || ctrl->isRogaining(crs->hasRogaining()))
            break;
        }
        switch (pp.type) {
          case lPunchNamedSplit:
            if (ctrl && ctrl->hasName() && r->getPunchTime(counter.level3, false, true, false) > 0) {
              swprintf_s(wbf, L"%s", r->getNamedSplitS(counter.level3, SubSecond::Off).c_str());
            }
          break;

          case lPunchNamedTime:
          case lPunchTeamTotalNamedTime:
            if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true, false) > 0)) {
              swprintf_s(wbf, L"%s: %s (%s)", ctrl->getName().c_str(),
                         r->getPunchTimeS(counter.level3, false, true, pp.type == lPunchTeamTotalNamedTime, SubSecond::Off).c_str(),
                         r->getNamedSplitS(counter.level3, SubSecond::Off).c_str());
            }
            break;
          case lControlName:
          case lPunchName:
            if (ctrl && ctrl->hasName() && (!par.lineBreakControlList || r->getPunchTime(counter.level3, false, true, false) > 0)) {
              swprintf_s(wbf, L"%s", ctrl->getName().c_str());
            }
            else if (counter.level3 == nCtrl) {
              wsptr = &lang.tl(L"Mål");
            }
            break;

          case lPunchTime:
          case lPunchTeamTime: {
            swprintf_s(wbf, L"%s (%s)",
                       r->getSplitTimeS(counter.level3, false, SubSecond::Off).c_str(),
                       r->getPunchTimeS(counter.level3, false, true, pp.type == lPunchTeamTime, SubSecond::Off).c_str());
            break;
          }
          case lPunchSplitTime: {
            wcscpy_s(wbf, r->getSplitTimeS(counter.level3, false, SubSecond::Off).c_str());
            break;
          }
          case lPunchTotalTime:
          case lPunchTeamTotalTime: {
            int pt = r->getPunchTime(counter.level3, false, true, pp.type == lPunchTeamTotalTime);
            if (pt > 0) 
              wsptr = &formatTime(pt, SubSecond::Off);
            break;
          }
          case lPunchTotalTimeAfter:
          case lPunchTeamTotalTimeAfter: {
            if (r->getPunchTime(counter.level3, false, true, false) > 0) {
              int rt = r->getLegTimeAfterAcc(counter.level3, pp.type == lPunchTeamTotalTimeAfter);
              if (rt > 0)
                wcscpy_s(wbf, (L"+" + formatTime(rt, SubSecond::Off)).c_str());
            }
            break;
          }
          case lPunchControlNumber: {
            wcscpy_s(wbf, crs->getControlOrdinal(counter.level3).c_str());
            break;
          }
          case lPunchControlCode: {
            const oControl *ctrl = crs->getControl(counter.level3);
            if (ctrl) {
              if (ctrl->getStatus() == oControl::ControlStatus::StatusMultiple) {
                wstring str = ctrl->getStatusS();
                swprintf_s(wbf, L"%s.", str.substr(0, 1).c_str());
              }
              else
                swprintf_s(wbf, L"%d", ctrl->getFirstNumber());
            }
            break;
          }
          case lPunchControlPlace: {
            int p = r->getLegPlace(counter.level3);
            if (p > 0)
              swprintf_s(wbf, L"%d", p);
            break;
          }
          case lPunchControlPlaceAcc:
          case lPunchControlPlaceTeamAcc: {
            int p = r->getLegPlaceAcc(counter.level3, pp.type == lPunchControlPlaceTeamAcc);
            if (p > 0)
              swprintf_s(wbf, L"%d", p);
            break;
          }
          case lPunchLostTime: {
            wcscpy_s(wbf, r->getMissedTimeS(counter.level3).c_str());
            break;
          }
          case lPunchAbsTime: {
            int t = r->getPunchTime(counter.level3, false, true, false);
            if (t > 0)
              wsptr = &getAbsTime(r->tStartTime + t);
            break;
          }
        }
      }
      break;
    case lSubSubCounter:
      if (pp.text.empty())
        swprintf_s(wbf, L"%d.", counter.level3+1);
      else
        swprintf_s(wbf, L"%d", counter.level3+1);
      break;
    case lSubCounter:
      if (pp.text.empty())
        swprintf_s(wbf, L"%d.", counter.level2+1);
      else
        swprintf_s(wbf, L"%d", counter.level2+1);
      break;
    case lTotalCounter:
      if (pp.text.empty())
        swprintf_s(wbf, L"%d.", counter.level1+1);
      else
        swprintf_s(wbf, L"%d", counter.level1+1);
      break;

    case lTotalRunLength: {
      int sum = 0;
      for (auto& r : Runners) {
        if (!r.isRemoved() && r.getCourse(false) && r.isStatusOK(false, false))
          sum += r.getCourse(false)->getLength();
      }
      wsptr = &itow(sum/1000);
      break;
    }
    case lTotalRunTime: {
      int sum = 0;
      for (auto& r : Runners) {
        if (!r.isRemoved())
          sum += r.getRunningTime(false);
      }
      wsptr = &formatTimeHMS(sum);
      break;
    }
    case lNumEntries: {
      int sum = 0;
      for (auto& r : Runners) {
        if (!r.isRemoved() && r.tParentRunner == nullptr)
          sum++;
      }
      wsptr = &itow(sum);
      break;
    }
    case lNumStarts: {
      int sum = 0;
      for (auto& r : Runners) {
        if (!r.isRemoved() && r.tParentRunner == nullptr && (oAbstractRunner::isResultStatus(r.getStatus()) || r.isStatusOK(false, false)))
          sum++;
      }
      wsptr = &itow(sum);
      break;
    }
    case lClubName:
      wsptr = c != 0 ? &c->getDisplayName() : 0;
      break;
    case lResultModuleTime:
    case lResultModuleTimeTeam:

      if (pp.resultModuleIndex != -1) {
        if (t && pp.type == lResultModuleTimeTeam)
          wsptr = &t->getTempResult(pp.resultModuleIndex).getOutputTime(legIndex);
        else if (r)
          wsptr = &r->getTempResult(pp.resultModuleIndex).getOutputTime(legIndex);
      }
      break;

    case lResultModuleNumber:
    case lResultModuleNumberTeam:
    
      if (pp.resultModuleIndex != -1) {
        int nr = 0;
        if (t && pp.type == lResultModuleNumberTeam)
          nr = t->getTempResult(pp.resultModuleIndex).getOutputNumber(legIndex);
        else if (r)
          nr = r->getTempResult(pp.resultModuleIndex).getOutputNumber(legIndex);

        if (pp.text.empty() || pp.text[0]!='@')
          wsptr = &itow(nr);
        else {
          wstring &res = StringCache::getInstance().wget();
          MetaList::fromResultModuleNumber(pp.text.substr(1), nr, res);
          return res;
        }
      }
      break;

    case lRogainingPunch:
      if (r && r->Card && r->getCourse(false)) {
        const pCourse crs = r->getCourse(false);
        const pPunch punch = r->Card->getPunchByIndex(counter.level3);
        if (punch && punch->tRogainingIndex>=0) {
          const pControl ctrl = crs->getControl(punch->tRogainingIndex);
          if (ctrl) {
            swprintf_s(wbf, L"%d, %dp, %s (%s)",
                      punch->type, ctrl->getRogainingPoints(),
                      r->Card->getRogainingSplit(counter.level3, r->tStartTime).c_str(),
                      punch->getRunningTime(r->tStartTime).c_str());
          }
        }
      }
      break;

    case lLineBreak:
      break;
  }

  if (pp.type!=lString && (wsptr==0 || wsptr->empty()) && wbf[0]==0)
    return _EmptyWString;
  else if (wsptr) {
    if (pp.text.empty())
      return *wsptr;
    else {
      swprintf_s(wbf, pp.text.c_str(), wsptr->c_str());
      wstring &res = StringCache::getInstance().wget();
      res = wbf;
      return res;
    }
  }
  else {
    if (pp.text.empty()) {
      wstring &res = StringCache::getInstance().wget();
      res = wbf;
      return res;
    }
    else {
      wchar_t bf2[512];
      swprintf_s(bf2, pp.text.c_str(), wbf);
      wstring &res = StringCache::getInstance().wget();
      res = bf2;
      return res;
    }
  }
}

bool oEvent::formatPrintPost(const list<oPrintPost> &ppli, PrintPostInfo &ppi,
                             const pTeam t, const pRunner r, const pClub c,
                             const pClass pc, const pCourse crs, const pControl ctrl,
                             const oPunch *punch, int legIndex) {
  int y = ppi.gdi.getCY();
  int x = ppi.gdi.getCX();
  bool updated = false;
  int lineHeight = 0;
  int pdx = 0, pdy = 0;
  auto ppit = ppli.begin();
  while (ppit != ppli.end()) {
    const oPrintPost &pp = *ppit;

    if (pp.type == lLineBreak) {
      x -= ppi.gdi.scaleLength(pp.dx) - pdx;
      pdx = ppi.gdi.scaleLength(pp.dx);
      y += lineHeight;
      ++ppit;
      continue;
    }
    else if (pp.type == lImage) {
      pdy = ppi.gdi.scaleLength(pp.dy);
      pdx = ppi.gdi.scaleLength(pp.dx);
      int format = 0;
      if (pp.imageNoUpdatePos)
        format |= imageNoUpdatePos;
      ppi.gdi.addImage("", y + pdy, x + pdx, format, pp.text,
              ppi.gdi.scaleLength(pp.fixedWidth), ppi.gdi.scaleLength(pp.fixedHeight));
      ++ppit;
      continue;
    }

    int limit = ppit->xlimit;

    bool keepNext = false;
    //Skip merged entities
    while (ppit != ppli.end() && ppit->doMergeNext)
      ++ppit;

    // Main increment below
    if (++ppit != ppli.end() && ppit->dy == pp.dy)
      limit = ppit->dx - pp.dx;
    else
      keepNext = true;

    if (pp.useStrictWidth)
      limit = max(pp.fixedWidth - 5, 0); // Allow some space
    else
      limit = max(pp.fixedWidth, limit);

    assert(limit >= 0);
    pRunner rr = r;
    if (!rr && t) {
      if (pp.legIndex >= 0) {
        if (pc) {
          int lg = pp.legIndex;
          if (pp.legIndex < max<int>(1, pc->getNumStages()))
            lg = pc->getLinearIndex(pp.legIndex, pp.linearLegIndex);

          rr = t->getRunner(lg);
        }
      }
      else if (legIndex >= 0)
        rr = t->getRunner(legIndex);
      else {
        int lg = ppi.par.getLegNumber(pc);
        rr = t->getRunner(lg);
      }
    }
    const wstring &text = punch ? formatPunchString(pp, ppi.par, t, rr, punch, ppi.counter) :
      ((legIndex == -1) ? formatListString(pp, ppi.par, t, rr, c, pc, ppi.counter) :
       formatSpecialString(pp, ppi.par, t, legIndex, crs, ctrl, ppi.counter));
    updated |= !text.empty();

    TextInfo *ti = 0;
    if (!text.empty()) {
      int tightBBFlag = ppi.par.tightBoundingBox ? 0 : skipBoundingBox;
      pdy = ppi.gdi.scaleLength(pp.dy);
      pdx = ppi.gdi.scaleLength(pp.dx);
      if ((pp.type == lRunnerName || pp.type == lRunnerCompleteName ||
          pp.type == lRunnerFamilyName || pp.type == lRunnerGivenName ||
          pp.type == lTeamRunner || (pp.type == lPatrolNameNames && !t)) && rr) {
        ti = &ppi.gdi.addStringUT(y + pdy, x + pdx, pp.format | tightBBFlag, text,
                                  ppi.gdi.scaleLength(limit), ppi.par.cb, pp.fontFace.c_str());
        ti->setExtra(rr->getId());
        ti->id = "R";
      }
      else if ((pp.type == lTeamName || pp.type == lPatrolNameNames) && t) {
        ti = &ppi.gdi.addStringUT(y + pdy, x + pdx, pp.format | tightBBFlag, text,
                                  ppi.gdi.scaleLength(limit), ppi.par.cb, pp.fontFace.c_str());
        ti->setExtra(t->getId());
        ti->id = "T";
      }
      else {
        ti = &ppi.gdi.addStringUT(y + pdy, x + pdx,
                                  pp.format | tightBBFlag, text, ppi.gdi.scaleLength(limit), 0, pp.fontFace.c_str());
      }
      if (ti && ppi.keepToghether)
        ti->lineBreakPrioity = -1;

      if (ti) {
        lineHeight = ti->getHeight();

      }

      if (pp.color != colorDefault)
        ti->setColor(pp.color);
    }
    ppi.keepToghether |= keepNext;

  }

  return updated;
}

void oEvent::calculatePrintPostKey(const list<oPrintPost> &ppli, gdioutput &gdi, const oListParam &par,
                                   const pTeam t, const pRunner r, const pClub c,
                                   const pClass pc, oCounter &counter, wstring &key)
{
  key.clear();
  list<oPrintPost>::const_iterator ppit;
  for (ppit=ppli.begin();ppit!=ppli.end(); ++ppit) {
    const oPrintPost &pp=*ppit;
    pRunner rr = r;
    if (!rr && t) {
      int linLeg = pp.legIndex;
      if (pc)
        linLeg = pc->getLinearIndex(pp.legIndex, pp.linearLegIndex);
      rr=t->getRunner(linLeg);
    }
    const wstring &text = formatListString(pp, par, t, rr, c, pc, counter);
    key += text;
    //Skip merged entities
    while (ppit != ppli.end() && ppit->doMergeNext)
      ++ppit;
  }
}

void oEvent::listGeneratePunches(const oListInfo &listInfo, gdioutput &gdi, 
                                 pTeam t, pRunner r, pClub club, pClass cls) {
  const list<oPrintPost> &ppli = listInfo.subListPost;
  const oListParam &par = listInfo.lp;
  auto type = listInfo.listSubType;
  if (!r || ppli.empty())
    return;
  bool filterNamed = listInfo.subFilter(ESubFilterNamedControl);
  const bool filterFinish = listInfo.subFilter(ESubFilterNotFinish);

  pCourse crs = r->getCourse(true);

  if (cls && cls->getNoTiming())
    return;
  if (r && (r->getStatusComputed(true) == StatusNoTiming || r->noTiming()))
    return;

  int h = gdi.getLineHeight();
  int w = 0;
  bool newLine = false;
  int haccum = 0;
  for (auto &pl : ppli) {
    if (pl.type == lPunchNamedTime)
      filterNamed = true;
    if (pl.type == lLineBreak) {
      haccum += h;
      h = 0;
      newLine = true;
      continue;
    }
    h = max(h, gdi.getLineHeight(pl.getFont(), pl.fontFace.c_str()) + gdi.scaleLength(pl.dy));
    w = max(w, gdi.scaleLength(pl.fixedWidth + pl.dx));
  }
  h += haccum;
  int xlimit = gdi.getCX() + gdi.scaleLength(600);
  par.lineBreakControlList = newLine; // Controls if controls names are printed even if the runner has not punched there yet.

  if (w > 0) {
    gdi.pushX();
    gdi.fillNone();
  }

  bool neednewline = false;
  bool updated = false;

  int limit = crs ? crs->nControls + 1 : 1;

  if (r->Card && r->Card->getNumPunches() > limit)
    limit = r->Card->getNumPunches();

  vector<char> skip(limit, false);
  if (filterNamed && crs) {
    for (int k = 0; k < crs->nControls; k++) {
      if (crs->getControl(k) && !crs->getControl(k)->hasName())
        skip[k] = true;
    }
    for (int k = crs->nControls + 1; k < limit; k++) {
      skip[k] = true;
    }
    if (filterFinish)
      skip[crs->nControls] = true;
  }
  bool filterRadioTimes = listInfo.sortOrder == SortOrder::ClassLiveResult || listInfo.filter(EFilterList::EFilterAnyResult);
  if (filterRadioTimes && crs) {
    for (int k = 0; k < crs->nControls; k++) {
      if (crs->getControl(k) && !crs->getControl(k)->isValidRadio())
        skip[k] = true;
    }
    for (int k = crs->nControls + 1; k < limit; k++) {
      skip[k] = true;
    }
    if (filterFinish)
      skip[crs->nControls] = true;
  }
  PrintPostInfo ppi(gdi, par);
  if (type == oListInfo::EBaseType::EBaseTypeCoursePunches) {
    for (int k = 0; k < limit; k++) {
      if (w > 0 && updated) {
        updated = false;
        if (gdi.getCX() + w > xlimit || newLine) {
          neednewline = false;
          gdi.popX();
          gdi.setCY(gdi.getCY() + h);
        }
        else
          gdi.setCX(gdi.getCX() + w);
      }

      if (!skip[k]) {
        updated |= formatPrintPost(ppli, ppi, t, r, club, cls,
                                   nullptr, nullptr, nullptr, -1);
        neednewline |= updated;
      }
      ppi.counter.level3++;
    }
  }
  else if(type == oListInfo::EBaseType::EBaseTypeAllPunches) {
    int startType = -1;
    int finishType = -1;
    const pCourse pcrs = r->getCourse(false);

    if (pcrs) {
      startType = pcrs->getStartPunchType();
      finishType = pcrs->getFinishPunchType();
    }
    int prevPunchTime = r->getStartTime();
    vector<pPunch> punches;
    if (r->Card) {
      for (auto &punch : r->Card->punches)
        punches.push_back(&punch);
    }
    else {
      vector<pFreePunch> fPunches;
      oe->getPunchesForRunner(r->getId(), true, fPunches);
      for (auto punch : fPunches)
        punches.push_back(punch);
    }

    for (auto &pPunch : punches) {
      const oPunch &punch = *pPunch;
      punch.previousPunchTime = prevPunchTime;

      if (punch.isCheck() || punch.isStart(startType))
        continue;
      if (filterFinish && punch.isFinish(finishType))
        continue;
      prevPunchTime = punch.getTimeInt();
      if (w > 0 && updated) {
        updated = false;
        if (gdi.getCX() + w > xlimit || newLine) {
          neednewline = false;
          gdi.popX();
          gdi.setCY(gdi.getCY() + h);
        }
        else
          gdi.setCX(gdi.getCX() + w);
      }

      updated |= formatPrintPost(ppli, ppi, t, r, club, cls,
                                  nullptr, nullptr, &punch, -1);
      neednewline |= updated;
      ppi.counter.level3++;
    }
  }
  if (w > 0) {
    gdi.popX();
    gdi.fillDown();
    if (neednewline)
      gdi.setCY(gdi.getCY() + h);
  }
}

void oEvent::generateList(gdioutput &gdi, bool reEvaluate, const oListInfo &li, bool updateScrollBars) {
  if (reEvaluate)
    reEvaluateAll(set<int>(), false);

  oe->calcUseStartSeconds();
  oe->calculateNumRemainingMaps(false);
  oe->updateComputerTime();
  oe->setGeneralResultContext(&li.lp);
  
  wstring listname;
  if (!li.head.empty()) {
    oCounter counter;
    const wstring &name = formatListString(li.head.front(), li.lp, 0, 0, 0, 0, counter);
    listname = name;
    li.lp.updateDefaultName(name);
  }
  bool addHead = li.lp.showHeader && !li.lp.useLargeSize;
  size_t nClassesSelected = li.lp.selection.size();
  if (nClassesSelected!=0 && nClassesSelected < min(Classes.size(), Classes.size()/2+5) ) {
    // Non-trivial class selection:
    Classes.sort();
    wstring cls;
    for (oClassList::iterator it = Classes.begin(); it != Classes.end(); ++it) {
      if (li.lp.selection.count(it->getId())) {
        cls += makeDash(L" - ");
        cls += it->getName();
      }
    }
    listname += cls;
  }

  if (li.lp.getLegNumberCoded() != -1) {
    listname += lang.tl(L" Sträcka X#" + li.lp.getLegName());
  }

  generateListInternal(gdi, li, addHead);
  
  for (list<oListInfo>::const_iterator it = li.next.begin(); it != li.next.end(); ++it) {
    bool interHead = addHead && it->getParam().showInterTitle;
    if (li.lp.pageBreak || it->lp.pageBreak) {
      gdi.dropLine(1.0);
      gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
    }
    else if (interHead) {
      gdi.dropLine(1.5);
      gdi.addStringUT(gdi.getCY() - 1, 0, pageNewChapter, "");
    }
    else {
      gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
    }

    generateListInternal(gdi, *it, interHead);
  }
  // Reset context
  oe->setGeneralResultContext(nullptr);

  gdi.setListDescription(listname);
  if (updateScrollBars)
    gdi.updateScrollbars();
}

bool oListInfo::filterRunner(const oRunner &r) const {
  if (r.isRemoved()) 
    return true;

  if (r.tStatus == StatusNotCompetiting && !filter(EFilterIncludeNotParticipating))
    return true;

  if (!lp.selection.empty() && lp.selection.count(r.getClassId(true)) == 0)
    return true;

  if (!lp.matchLegNumber(r.getClassRef(false), r.legToRun()))
    return true;

  if (lp.ageFilter != oListParam::AgeFilter::All) {
    int age = r.getBirthAge();
    if (age > 0) {
      oDataConstInterface odc = r.getEvent()->getDCI();
      int lowAgeLimit = odc.getInt("YouthAge");
      //int highAgeLimit = odc.getInt("SeniorAge");


      if (lp.ageFilter == oListParam::AgeFilter::ExludeYouth &&
          age <= lowAgeLimit)
        return true;
        
      if (lp.ageFilter == oListParam::AgeFilter::OnlyYouth &&
          age > lowAgeLimit)
        return true;
    }
    else {
      // Consider "normal"
      if (lp.ageFilter != oListParam::AgeFilter::ExludeYouth)
        return true;
    }
  }

  if (filter(EFilterExcludeDNS)) {
    if (r.tStatus == StatusDNS)
      return true;
    if (r.Class && r.Class->isQualificationFinalBaseClass()) {
      if (r.getLegNumber() > 0 && r.getClassRef(true) == r.Class)
        return true; //Not qualified -> out
    }
  }
  if (filter(EFilterExcludeCANCEL) && r.tStatus == StatusCANCEL)
    return true;

  if (filter(EFilterVacant)) {
    if (r.isVacant())
      return true;
  }
  if (filter(EFilterOnlyVacant)) {
    if (!r.isVacant())
      return true;
  }

  if (filter(EFilterAnyResult)) {
    if (!r.hasOnCourseResult())
      return true;
  }

  if (filter(EFilterAPIEntry)) {
    if (!r.hasFlag(oRunner::FlagAddedViaAPI))
      return true;
  }

  if (filter(EFilterWrongFee)) {
    if (r.getEntryFee() == r.getDefaultFee())
      return true;
  }

  if (filter(EFilterModifiedCard)) {
    if (!r.getCard() || r.getCard()->isOriginalCard() != oCard::PunchOrigin::Manual)
      return true;
  }

  if (filter(EFilterRentCard) && !r.isRentalCard())
    return true;

  if (filter(EFilterHasCard) && r.getCardNo() == 0)
    return true;

  if (filter(EFilterHasNoCard) && r.getCardNo() > 0)
    return true;

  return false;
}

bool oListInfo::filterRunnerResult(GeneralResult *gResult, const oRunner &r) const {
  if (gResult && r.getTempResult(0).getStatus() == StatusNotCompetiting)
    return true;

  if (filter(EFilterHasResult)) {
    if (gResult == 0) {
      if (lp.useControlIdResultTo <= 0 && !r.hasResult())
        return true;
      else if ((lp.useControlIdResultTo > 0 || lp.useControlIdResultFrom > 0) && r.tempStatus != StatusOK)
        return true;
      else if (calcTotalResults && r.getTotalStatus() == StatusUnknown)
        return true;
    }
    else {
      auto &res = r.getTempResult(0);
      RunnerStatus st = res.getStatus();
      if (st == StatusUnknown || isPossibleResultStatus(st) && r.getRunningTime(false) <= 0)
        return true;
    }
  }
  else if (filter(EFilterHasPrelResult)) {
    if (gResult == 0) {
      if (lp.useControlIdResultTo <= 0 && (r.tStatus == StatusUnknown || isPossibleResultStatus(r.getStatusComputed(true))) && r.getRunningTime(false) <= 0)
        return true;
      else if ((lp.useControlIdResultTo > 0 || lp.useControlIdResultFrom > 0) && r.tempStatus != StatusOK)
        return true;
      else if (calcTotalResults && r.getTotalStatus() == StatusUnknown && r.getTotalRunningTime() <= 0)
        return true;
    }
    else {
      auto &res = r.getTempResult(0);
      int rt = res.getRunningTime();
      RunnerStatus st = res.getStatus();
      if ((st == StatusUnknown || isPossibleResultStatus(st)) && rt <= 0)
        return true;
    }
  }
  return false;
}

GeneralResult *oListInfo::applyResultModule(oEvent &oe, vector<pRunner> &rlist) const {
  GeneralResult *gResult = nullptr;
  if (!resultModule.empty()) {
    wstring src;
    oListInfo::ResultType resType = getResultType();
    gResult = oe.getGeneralResult(resultModule, src).get();
    gResult->calculateIndividualResults(rlist, false, resType, sortOrder == Custom, getParam().getInputNumber());

    if (sortOrder == SortByFinishTime || sortOrder == SortByFinishTimeReverse || sortOrder == SortByStartTime)
      gResult->sort(rlist, sortOrder);
  }
  return gResult;
}

void oEvent::formatHeader(gdioutput& gdi, const oListInfo& li, const pRunner rInput) {
  vector< pair<EPostType, wstring> > v;
  int* xLimitForwardUpdate = nullptr;
  for (auto& lp : li.head) {
    bool strUpdate = lp.type == lCmpName || lp.type == lString;
    if (lp.xlimit == 0 && !strUpdate) {
      v.clear();
      v.emplace_back(lp.type, lp.text);
      gdiFonts font = lp.getFont();
      lp.xlimit = li.getMaxCharWidth(this, gdi, li.getParam().selection, v, font, lp.fontFace.c_str());
    }
    else if (strUpdate) {
      v.clear();
      const_cast<wstring&>(lp.text) = li.lp.getCustomTitle(lp.text);
      v.emplace_back(lp.type, lp.text);
      gdiFonts font = lp.getFont();
      lp.xlimit = li.getMaxCharWidth(this, gdi, li.getParam().selection, v, font, lp.fontFace.c_str());
    }
    if (xLimitForwardUpdate)
      (*xLimitForwardUpdate) += lp.xlimit;

    if (lp.doMergeNext && xLimitForwardUpdate == nullptr)
      xLimitForwardUpdate = &lp.xlimit;
    else
      xLimitForwardUpdate = nullptr;
  }

  pTeam team = nullptr;
  pClass cls = nullptr;
  pClub club = nullptr;
  pCourse crs = nullptr;
  int legIndex = -1;
  if (rInput) {
    team = rInput->getTeam();
    club = rInput->getClubRef();
    crs = rInput->getCourse(true);
    cls = rInput->getClassRef(true);
  }

  PrintPostInfo printPostInfo(gdi, li.lp);
  formatPrintPost(li.head, printPostInfo, team, rInput, 
                  club, cls, crs,
                  nullptr, nullptr, -1);
}

void oEvent::generateListInternal(gdioutput &gdi, const oListInfo &li, bool formatHead) {
  li.setupLinks();
  pClass sampleClass = 0;
  bool calculatedSplitResults = false;
  if (!li.lp.selection.empty())
    sampleClass = getClass(*li.lp.selection.begin());
  if (!sampleClass && !Classes.empty())
    sampleClass = &*Classes.begin();

  if (li.listType == li.EBaseTypeRunner) {
    if (li.calculateLiveResults || li.sortOrder == SortOrder::ClassLiveResult)
      calculateResults(li.lp.selection, ResultType::PreliminarySplitResults);

    if (li.calcCourseClassResults)
      calculateResults(li.lp.selection, ResultType::ClassCourseResult);

    if (li.calcCourseResults)
      calculateResults(li.lp.selection, ResultType::CourseResult);

    if (li.calcTotalResults) {
      calculateTeamResults(li.lp.selection, ResultType::TotalResult);
      calculateResults(li.lp.selection, ResultType::TotalResult);
    }

    if (li.calcResults) {
      if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) {
        calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo);
        calculatedSplitResults = true;
      }
      else {
        calculateTeamResults(li.lp.selection, ResultType::ClassResult);
        calculateResults(li.lp.selection, ResultType::ClassResult);
      }
    }
  }
  else if (li.listType == li.EBaseTypeTeam || li.listType == li.EBaseTypeClubTeam) {
    if (li.calcResults)
      calculateTeamResults(li.lp.selection, ResultType::ClassResult);
    if (li.calcTotalResults)
      calculateTeamResults(li.lp.selection, ResultType::TotalResult);
    if (li.calcCourseResults)
      calculateTeamResults(li.lp.selection, ResultType::CourseResult);
    if (li.calculateLiveResults || li.sortOrder == SortOrder::ClassLiveResult)
      calculateResults(li.lp.selection, ResultType::PreliminarySplitResults);

    if (li.calcCourseClassResults)
      calculateResults(li.lp.selection, ResultType::ClassCourseResult);
  }
  else if (li.listType == li.EBaseTypeClubRunner) {
    if (li.calcResults) {
      calculateTeamResults(li.lp.selection, ResultType::TotalResult);
      calculateTeamResults(li.lp.selection, ResultType::ClassResult);
    }
    if (li.calcCourseClassResults)
      calculateResults(li.lp.selection, ResultType::ClassCourseResult);
    if (li.calcCourseResults)
      calculateResults(li.lp.selection, ResultType::CourseResult);

    if (li.calcResults) {
      if (li.lp.useControlIdResultTo > 0 || li.lp.useControlIdResultFrom > 0) {
        calculateSplitResults(li.lp.useControlIdResultFrom, li.lp.useControlIdResultTo);
        calculatedSplitResults = true;
      }
      else {
        calculateResults(li.lp.selection, ResultType::ClassResult);
      }
    }
  }

  PrintPostInfo printPostInfo(gdi, li.lp);
  //oCounter counter;
  //Render header
  vector< pair<EPostType, wstring> > v;
  for (auto &listPostList : { &li.subHead, &li.listPost, &li.subListPost }) {
    for (auto &lp : *listPostList) {
      if (lp.xlimit == 0) {
        v.clear();
        v.emplace_back(lp.type, lp.text);
        gdiFonts font = lp.getFont();
        lp.xlimit = li.getMaxCharWidth(this, gdi, li.getParam().selection, v, font, lp.fontFace.c_str());
      }
    }
  }

  if (formatHead && li.getParam().showHeader) 
    formatHeader(gdi, li, nullptr);

  if (li.fixedType) {
    generateFixedList(gdi, li);
    return;
  }
     
  // Apply for all teams (calculate start times etc.)
  
  vector<pTeam> tlist;
  tlist.reserve(Teams.size());
  const bool include_nc = li.filter(EFilterIncludeNotParticipating);
  for (oTeamList::iterator it = Teams.begin(); it != Teams.end(); ++it) {
    if (it->isRemoved() || (!include_nc && it->tStatus == StatusNotCompetiting))
      continue;

    if (!li.lp.selection.empty() && li.lp.selection.count(it->getClassId(true)) == 0)
      continue;
    it->apply(oBase::ChangeType::Quiet, nullptr);
    tlist.push_back(&*it);
  }

  vector<pRunner> rlist;
  GeneralResult *gResult = nullptr;

  if (li.listType == li.EBaseTypeRunner || li.listType == li.EBaseTypeClubRunner) {
    vector<pRunner> rlistInput;
    getRunners(li.lp.selection, rlistInput, false);
    rlist.reserve(rlistInput.size());
    for (auto r : rlistInput) {
      if (!li.filterRunner(*r))
        rlist.push_back(r);
    }

    if (li.sortOrder != Custom && !calculatedSplitResults)
      sortRunners(li.sortOrder, rlist);

    gResult = li.applyResultModule(*this, rlist);
  }


  wstring oldKey;

  auto formatTeam = [this, &gdi, &li, &gResult, &printPostInfo, &oldKey](pTeam it,
    bool includeSubHead, int &parLegRangeMin, int &parLegRangeMax, pClass &parLegRangeClass) {
    int linearLegSpec = li.lp.getLegNumber(it->getClassRef(false));

    if (gResult && it->getTempResult(0).getStatus() == StatusNotCompetiting && !li.filter(EFilterIncludeNotParticipating))
      return false;

    if (li.filter(EFilterExcludeDNS)) {
      if (it->tStatus == StatusDNS)
        return false;
    }

    if (li.filter(EFilterVacant)) {
      if (it->isVacant())
        return false;
    }

    if (li.filter(EFilterOnlyVacant)) {
      if (!it->isVacant())
        return false;
    }

    if (li.filter(EFilterModifiedCard)) {
      bool modified = false;
      for (pRunner r : it->Runners) {
        if (r && r->getCard() && r->getCard()->isOriginalCard() == oCard::PunchOrigin::Manual)
          modified = true;
      }
      if (modified)
        return false;
    }

    if (li.filter(EFilterHasResult)) {
      if (gResult) {
        RunnerStatus st = it->getTempResult(0).getStatus();
        if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getTempResult(0).getRunningTime() <= 0))
          return false;
      }
      else {
        RunnerStatus st = it->getLegStatus(linearLegSpec, true, false);
        if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getLegRunningTime(linearLegSpec, true, false) <= 0))
          return false;
        else if (li.calcTotalResults) {
          st = it->getLegStatus(linearLegSpec, true, true);
          if (st == StatusUnknown || (isPossibleResultStatus(st) && it->getLegRunningTime(linearLegSpec, true, true) <= 0))
            return false;
        }
      }
    }
    else if (li.filter(EFilterHasPrelResult)) {
      if (gResult) {
        RunnerStatus st = it->getTempResult(0).getStatus();
        if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getTempResult(0).getRunningTime() <= 0)
          return false;
      }
      else {
        RunnerStatus st = it->getLegStatus(linearLegSpec, true, false);
        if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getLegRunningTime(linearLegSpec, true, false) <= 0)
          return false;
        else if (li.calcTotalResults) {
          RunnerStatus st = it->getLegStatus(linearLegSpec, true, true);

          if ((st == StatusUnknown || isPossibleResultStatus(st)) && it->getLegRunningTime(linearLegSpec, true, true) <= 0)
            return false;
        }
      }
    }
    else if (li.filter(EFilterAnyResult)) {
      pRunner r = it->getRunner(linearLegSpec);
      if (!r || !r->hasOnCourseResult())
        return false;
    }

    const bool needParRange = li.subFilter(ESubFilterSameParallel)
                              || li.subFilter(ESubFilterSameParallelNotFirst);

    if (needParRange && it->Class != parLegRangeClass && it->Class) {
      parLegRangeClass = it->Class;
      parLegRangeClass->getParallelRange(linearLegSpec < 0 ? 0 : linearLegSpec, parLegRangeMin, parLegRangeMax);
      if (li.subFilter(ESubFilterSameParallelNotFirst))
        parLegRangeMin++;
    }

    if (includeSubHead) {
      wstring newKey;
      printPostInfo.par.relayLegIndex = linearLegSpec;
      calculatePrintPostKey(li.subHead, gdi, li.lp, &*it, 0, it->Club, it->Class, printPostInfo.counter, newKey);
      if (newKey != oldKey) {
        if (!oldKey.empty())
          gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");

        wstring legInfo;
        if (linearLegSpec >= 0 && it->getClassRef(false)) {
          // Specified leg
          legInfo = lang.tl(L", Str. X#" + li.lp.getLegName());
        }

        gdi.addStringUT(pagePageInfo, it->getClass(true) + legInfo); // Teamlist

        oldKey.swap(newKey);
        printPostInfo.counter.level2 = 0;
        printPostInfo.counter.level3 = 0;
        printPostInfo.reset();
        printPostInfo.par.relayLegIndex = linearLegSpec;
        formatPrintPost(li.subHead, printPostInfo, &*it, 0, it->Club, it->Class,
          nullptr, nullptr, nullptr, -1);
      }
    }
    ++printPostInfo.counter;
    if (li.lp.filterInclude(printPostInfo.counter.level2, it)) {
      printPostInfo.counter.level3 = 0;
      printPostInfo.reset();
      printPostInfo.par.relayLegIndex = linearLegSpec;
      formatPrintPost(li.listPost, printPostInfo, it, 0, it->Club, it->Class,
        nullptr, nullptr, nullptr, -1);

      if (li.subListPost.empty())
        return true;

      if (li.listSubType == li.EBaseTypeRunner) {
        int nr = int(it->Runners.size());
        vector<pRunner> tr;
        tr.reserve(nr);
        vector<int> usedIx(nr, -1);

        for (int k = 0; k < nr; k++) {
          if (!it->Runners[k]) {
            if (li.filter(EFilterHasResult) || li.subFilter(ESubFilterHasResult) ||
              li.filter(EFilterHasPrelResult) || li.subFilter(ESubFilterHasPrelResult) ||
              li.filter(EFilterExcludeDNS) || li.filter(EFilterExcludeCANCEL) || li.subFilter(ESubFilterExcludeDNS) ||
              li.subFilter(ESubFilterVacant)) {
              usedIx[k] = -2; // Skip totally
            }
            continue;
          }
          else
            usedIx[k] = -2; // Skip unless allowed after filters
          bool noResult = false;
          bool noPrelResult = false;
          bool noStart = false;
          bool cancelled = false;
          if (gResult == 0) {
            noResult = it->Runners[k]->tStatus == StatusUnknown;
            noPrelResult = it->Runners[k]->tStatus == StatusUnknown && it->Runners[k]->getRunningTime(false) <= 0;
            noStart = it->Runners[k]->tStatus == StatusDNS || it->Runners[k]->tStatus == StatusCANCEL;
            if (it->Runners[k]->Class && it->Runners[k]->Class->isQualificationFinalBaseClass()) {
              if (k > 0 && it->Runners[k]->getClassRef(true) == it->Runners[k]->Class)
                noStart = true;
            }

            cancelled = it->Runners[k]->tStatus == StatusCANCEL;
            //XXX TODO Multiday
          }
          else {
            noResult = it->Runners[k]->tmpResult.status == StatusUnknown;
            noPrelResult = it->Runners[k]->tmpResult.status == StatusUnknown && it->Runners[k]->tmpResult.runningTime <= 0;
            noStart = it->Runners[k]->tmpResult.status == StatusDNS || it->Runners[k]->tmpResult.status == StatusCANCEL;
            cancelled = it->Runners[k]->tmpResult.status == StatusCANCEL;
          }

          if (noResult && (li.filter(EFilterHasResult) || li.subFilter(ESubFilterHasResult)))
            continue;

          if (noPrelResult && (li.filter(EFilterHasPrelResult) || li.subFilter(ESubFilterHasPrelResult)))
            continue;

          if (noStart && (li.filter(EFilterExcludeDNS) || li.subFilter(ESubFilterExcludeDNS)))
            continue;

          if (cancelled && li.filter(EFilterExcludeCANCEL))
            continue;

          if (it->Runners[k]->isVacant() && li.subFilter(ESubFilterVacant))
            continue;

          if ((it->Runners[k]->tLeg < parLegRangeMin || it->Runners[k]->tLeg > parLegRangeMax)
            && needParRange)
            continue;

          usedIx[k] = tr.size();
          tr.push_back(it->Runners[k]);
        }

        if (gResult) {
          gResult->sortTeamMembers(tr);

          for (size_t k = 0; k < tr.size(); k++) {
            bool suitableBreak = k < 2 || (k + 2) >= tr.size();
            printPostInfo.keepToghether = suitableBreak;
            printPostInfo.par.relayLegIndex = tr[k] ? tr[k]->tLeg : -1;
            formatPrintPost(li.subListPost, printPostInfo, &*it, tr[k],
              it->Club, tr[k]->getClassRef(true),
              nullptr, nullptr, nullptr, -1);
            printPostInfo.counter.level3++;
          }
        }
        else {
          for (size_t k = 0; k < usedIx.size(); k++) {
            if (usedIx[k] == -2)
              continue; // Skip
            bool suitableBreak = k < 2 || (k + 2) >= usedIx.size();
            printPostInfo.keepToghether = suitableBreak;
            printPostInfo.par.relayLegIndex = k;
            if (usedIx[k] == -1) {
              pCourse crs = it->Class ? it->Class->getCourse(k, it->StartNo) : 0;
              formatPrintPost(li.subListPost, printPostInfo, &*it, 0,
                it->Club, it->Class, crs, nullptr, nullptr, k);
            }
            else {
              formatPrintPost(li.subListPost, printPostInfo, &*it, tr[usedIx[k]],
                it->Club, tr[usedIx[k]]->getClassRef(true),
                nullptr, nullptr, nullptr, -1);
            }
            printPostInfo.counter.level3++;
          }
        }
      }
      else if (li.listSubType == li.EBaseTypeCoursePunches ||
               li.listSubType == li.EBaseTypeAllPunches) {
        pRunner r = it->getRunner(linearLegSpec);
        if (!r) 
          return true;

        listGeneratePunches(li, gdi, &*it, r, it->Club, it->Class);
      }
    }
    return true;
  };

  if (li.listType == li.EBaseTypeRunner) {
    for (size_t k = 0; k < rlist.size(); k++) {
      pRunner it = rlist[k];
      if (li.filterRunnerResult(gResult, *it))
        continue;
 
      wstring newKey;
      printPostInfo.par.relayLegIndex = -1;
      calculatePrintPostKey(li.subHead, gdi, li.lp, it->tInTeam, &*it, it->Club, it->getClassRef(true), printPostInfo.counter, newKey);

      if (newKey != oldKey) {
        if (!oldKey.empty())
          gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
        
        gdi.addStringUT(pagePageInfo, it->getClass(true));

        oldKey.swap(newKey);
        printPostInfo.counter.level2 = 0;
        printPostInfo.counter.level3 = 0;
        printPostInfo.reset();
        printPostInfo.par.relayLegIndex = -1;
        formatPrintPost(li.subHead, printPostInfo, it->tInTeam, &*it, it->Club, it->getClassRef(true),
                        nullptr, nullptr, nullptr, -1);
      }
      if (li.lp.filterInclude(printPostInfo.counter.level2 + 1, rlist[k])) {
        printPostInfo.reset();
        printPostInfo.par.relayLegIndex = it->tLeg;
        formatPrintPost(li.listPost, printPostInfo, it->tInTeam, &*it, it->Club, it->getClassRef(true),
                        nullptr, nullptr, nullptr, -1);

        if (li.listSubType == li.EBaseTypeCoursePunches ||
            li.listSubType == li.EBaseTypeAllPunches) {
          listGeneratePunches(li, gdi, it->tInTeam, &*it, it->Club, it->getClassRef(true));
        }
      }
      ++printPostInfo.counter;
    }
  }
  else if (li.listType == li.EBaseTypeTeam) {
    if (li.sortOrder != SortOrder::Custom) {
      pair<int, bool> legInfo = li.lp.getLegInfo(sampleClass);
      sortTeams(li.sortOrder, legInfo.first, legInfo.second, tlist);
    }
    
    if (!li.resultModule.empty()) {
      wstring src;
      gResult = getGeneralResult(li.resultModule, src).get();
      oListInfo::ResultType resType = li.getResultType();
      gResult->calculateTeamResults(tlist, false, resType, li.sortOrder == Custom, li.getParam().getInputNumber());
    }
    // Range of runners to include
    int parLegRangeMin = 0, parLegRangeMax = 1000;
    pClass parLegRangeClass = nullptr;
    
    for (pTeam t : tlist) {
      formatTeam(t, true, parLegRangeMin, parLegRangeMax, parLegRangeClass);
    }
  }
  else if (li.listType == li.EBaseTypeClubRunner) {
    Clubs.sort();
    
    map<int, vector<pRunner>> clubToRunner;
    for (pRunner r : rlist) {
      int club = r->getClubId();
      if (club == 0)
        continue;
      clubToRunner[club].push_back(r);
    }

    bool first = true;
    for (auto it = Clubs.begin(); it != Clubs.end(); ++it) {
      if (it->isRemoved())
        continue;

      if (li.filter(EFilterVacant)) {
        if (it->getId() == getVacantClub(false))
          continue;
      }

      if (li.filter(EFilterOnlyVacant)) {
        if (it->getId() != getVacantClub(false))
          continue;
      }

      bool startClub = false;

      for (auto rit : clubToRunner[it->getId()]) {
        if (li.filterRunnerResult(gResult, *rit))
          continue;

        if (!startClub) {
          if (!first)
            gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
          else
            first = false;
          
          gdi.addStringUT(pagePageInfo, it->getName());
          printPostInfo.counter.level2 = 0;
          printPostInfo.counter.level3 = 0;
          printPostInfo.reset();
          printPostInfo.par.relayLegIndex = -1;
          formatPrintPost(li.subHead, printPostInfo, nullptr, nullptr, &*it,
                          nullptr, nullptr, nullptr, nullptr, -1);
          startClub = true;
        }
        ++printPostInfo.counter;
        if (li.lp.filterInclude(printPostInfo.counter.level2, rit)) {
          printPostInfo.counter.level3 = 0;
          printPostInfo.reset();
          printPostInfo.par.relayLegIndex = rit->tLeg;
          formatPrintPost(li.listPost, printPostInfo, nullptr, rit, &*it, rit->getClassRef(true),
                          nullptr, nullptr, nullptr, -1);

          if (li.listSubType == li.EBaseTypeCoursePunches ||
              li.listSubType == li.EBaseTypeAllPunches) {
            listGeneratePunches(li, gdi, rit->tInTeam, &*rit, rit->Club, rit->getClassRef(true));
          }
        }
      }//Runners
    }//Clubs
  }
  else if (li.listType == li.EBaseTypeClubTeam) {
    Clubs.sort();
    map<int, vector<pTeam>> clubToTeam;

    for (pTeam t : tlist) {
      int club = t->getClubId();
      if (club == 0)
        continue;
      clubToTeam[club].push_back(t);
    }

    bool first = true;
    int parLegRangeMin = 0, parLegRangeMax = 1000;
    pClass parLegRangeClass = nullptr;

    for (auto it = Clubs.begin(); it != Clubs.end(); ++it) {
      if (it->isRemoved())
        continue;

      if (li.filter(EFilterVacant)) {
        if (it->getId() == getVacantClub(false))
          continue;
      }

      if (li.filter(EFilterOnlyVacant)) {
        if (it->getId() != getVacantClub(false))
          continue;
      }

      bool startClub = false;
      for (auto rit : clubToTeam[it->getId()]) {
        if (!startClub) {
          if (!first)
            gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
          else
            first = false;

          gdi.addStringUT(pagePageInfo, it->getName());
          printPostInfo.counter.level2 = 0;
          printPostInfo.counter.level3 = 0;
          printPostInfo.reset();
          printPostInfo.par.relayLegIndex = -1;
          formatPrintPost(li.subHead, printPostInfo, nullptr, nullptr, &*it,
            nullptr, nullptr, nullptr, nullptr, -1);
          startClub = true;
        }
        formatTeam(rit, false, parLegRangeMin, parLegRangeMax, parLegRangeClass);
      }//Teams
    }//Clubs
  }
  else if (li.listType == oListInfo::EBaseTypeCourse) {
    Courses.sort();
    oCourseList::iterator it;
    for (it = Courses.begin(); it != Courses.end(); ++it) {
      if (it->isRemoved())
        continue;

      if (!li.lp.selection.empty()) {
        vector<pClass> usageClass;
        it->getClasses(usageClass);

        bool used = false;
        while (!used && !usageClass.empty()) {
          used = li.lp.selection.count(usageClass.back()->getId()) > 0;
          usageClass.pop_back();
        }
        if (!used)
          continue;
      }

      wstring newKey;
      calculatePrintPostKey(li.subHead, gdi, li.lp, 0, 0, 0, 0, printPostInfo.counter, newKey);

      if (newKey != oldKey) {
        if (!oldKey.empty())
          gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
        
        oldKey.swap(newKey);
        printPostInfo.counter.level2 = 0;
        printPostInfo.counter.level3 = 0;
        printPostInfo.reset();
        formatPrintPost(li.subHead, printPostInfo, nullptr,
                        nullptr, nullptr, nullptr, &*it, nullptr, nullptr, 0);
      }
      if (li.lp.filterInclude(printPostInfo.counter.level2 + 1, nullptr)) {
        printPostInfo.reset();
        formatPrintPost(li.listPost, printPostInfo, nullptr, nullptr, nullptr,
                        nullptr, &*it, nullptr, nullptr, 0);

        if (li.listSubType == li.EBaseTypeControl) {
          //TODO: listGeneratePunches(li.subListPost, gdi, li.lp, it->tInTeam, &*it, it->Club, it->Class);
        }
      }
      ++printPostInfo.counter;
    }
  }
  else if (li.listType == oListInfo::EBaseTypeControl) {
    Controls.sort();
    oControlList::iterator it;
    for (it = Controls.begin(); it != Controls.end(); ++it) {
      if (it->isRemoved())
        continue;

      wstring newKey;
      calculatePrintPostKey(li.subHead, gdi, li.lp, 0, 0, 0, 0, printPostInfo.counter, newKey);

      if (newKey != oldKey) {
        if (!oldKey.empty())
          gdi.addStringUT(gdi.getCY() - 1, 0, pageNewPage, "");
        
        oldKey.swap(newKey);
        printPostInfo.counter.level2 = 0;
        printPostInfo.counter.level3 = 0;
        printPostInfo.reset();
        formatPrintPost(li.subHead, printPostInfo, nullptr, nullptr, nullptr,
                        nullptr, nullptr, &*it, nullptr, 0);
      }
      if (li.lp.filterInclude(printPostInfo.counter.level2 + 1, nullptr)) {
        printPostInfo.reset();
        formatPrintPost(li.listPost, printPostInfo, nullptr, nullptr, nullptr,
                        nullptr, nullptr, &*it, nullptr, 0);
      }
      ++printPostInfo.counter;
    }
  }
}

void oEvent::fillListTypes(gdioutput &gdi, const string &name, int filter)
{
  map<EStdListType, oListInfo> listMap;
  getListTypes(listMap, filter);

  //gdi.clearList(name);
  map<EStdListType, oListInfo>::iterator it;

  vector< pair<wstring, size_t> > v;
  for (it=listMap.begin(); it!=listMap.end(); ++it) {
    v.push_back(make_pair(it->second.Name, it->first));
    //gdi.addItem(name, it->second.Name, it->first);
  }
  sort(v.begin(), v.end());
  gdi.setItems(name, v);
}

void oEvent::getListType(EStdListType type, oListInfo &li)
{
  map<EStdListType, oListInfo> listMap;
  getListTypes(listMap, false);
  li = listMap[type];
}


void oEvent::getListTypes(map<EStdListType, oListInfo> &listMap, int filterResults)
{
  listMap.clear();

  if (!filterResults) {
    oListInfo li;
    li.Name=lang.tl(L"Startlista, individuell");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = true;
    listMap[EStdStartList]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Resultat, individuell");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = true;
    li.supportFrom = true;
    li.supportTo = true;
    listMap[EStdResultList]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Resultat, generell");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = true;
    li.supportLarge = true;
    li.supportFrom = true;
    li.supportTo = true;
    li.calcTotalResults = true;
    listMap[EGeneralResultList]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Rogaining, individuell");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = true;
    listMap[ERogainingInd]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Avgjorda klasser (prisutdelningslista)");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = true;
    listMap[EIndPriceList]=li;
  }
  if (!filterResults) {
    oListInfo li;
    li.Name=lang.tl(L"Startlista, patrull");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;

    listMap[EStdPatrolStartList]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Resultat, patrull");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;

    listMap[EStdPatrolResultList]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Patrullresultat (STOR)");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;
    li.largeSize = true;
    listMap[EStdPatrolResultListLARGE]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Resultat (STOR)");
    li.supportClasses = true;
    li.supportLegs = true;
    li.supportFrom = true;
    li.supportTo = true;
    li.largeSize = true;
    li.listType=li.EBaseTypeRunner;
    listMap[EStdResultListLARGE]=li;
  }

  /*{
    oListInfo li;
    li.Name=lang.tl("Stafettresultat, sträcka (STOR)");
    li.supportClasses = true;
    li.supportLegs = true;
    li.largeSize = true;
    li.listType=li.EBaseTypeTeam;
    listMap[EStdTeamResultListLegLARGE]=li;
  }*/

  if (!filterResults) {
    oListInfo li;
    li.Name=lang.tl(L"Hyrbricksrapport");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = false;
    li.supportLegs = false;
    listMap[EStdRentedCard]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Stafettresultat, delsträckor");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;
    listMap[EStdTeamResultListAll]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Stafettresultat, lag");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;
    listMap[EStdTeamResultList]=li;
  }
  /*
  {
    oListInfo li;
    li.Name=lang.tl("Stafettresultat, sträcka");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = true;
    listMap[EStdTeamResultListLeg]=li;
  }*/

  if (!filterResults) {
    {
      oListInfo li;
      li.Name=lang.tl(L"Startlista, stafett (lag)");
      li.listType=li.EBaseTypeTeam;
      li.supportClasses = true;
      li.supportLegs = false;
      listMap[EStdTeamStartList]=li;
    }
    {
      oListInfo li;
      li.Name=lang.tl(L"Startlista, stafett (sträcka)");
      li.listType=li.EBaseTypeTeam;
      li.supportClasses = true;
      li.supportLegs = true;
      listMap[EStdTeamStartListLeg]=li;
    }
    {
      oListInfo li;
      li.Name=lang.tl(L"Bantilldelning, stafett");
      li.listType=li.EBaseTypeTeam;
      li.supportClasses = true;
      li.supportLegs = false;
      listMap[ETeamCourseList]=li;
    }
    {
      oListInfo li;
      li.Name=lang.tl(L"Bantilldelning, individuell");
      li.listType=li.EBaseTypeRunner;
      li.supportClasses = true;
      li.supportLegs = false;
      listMap[EIndCourseList]=li;
    }
    {
      oListInfo li;
      li.Name=lang.tl(L"Individuell startlista, visst lopp");
      li.listType=li.EBaseTypeTeam;
      li.supportClasses = true;
      li.supportLegs = true;
      listMap[EStdIndMultiStartListLeg]=li;
    }
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Individuell resultatlista, visst lopp");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = true;
    listMap[EStdIndMultiResultListLeg]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Individuell resultatlista, visst lopp (STOR)");
    li.listType=li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = true;
    li.largeSize = true;
    listMap[EStdIndMultiResultListLegLARGE]=li;
  }

  {
    oListInfo li;
    li.Name = lang.tl(L"Individuell resultatlista, alla lopp");
    li.listType = li.EBaseTypeTeam;
    li.supportClasses = true;
    li.supportLegs = false;
    listMap[EStdIndMultiResultListAll]=li;
  }

  if (!filterResults) {
    oListInfo li;
    li.Name = lang.tl(L"Klubbstartlista");
    li.listType = li.EBaseTypeClubRunner;
    li.supportClasses = true;
    li.supportLegs = false;
    listMap[EStdClubStartList]=li;
  }

  {
    oListInfo li;
    li.Name = lang.tl(L"Klubbresultatlista");
    li.listType = li.EBaseTypeClubRunner;
    li.supportClasses = true;
    li.supportLegs = false;
    listMap[EStdClubResultList]=li;
  }

  if (!filterResults) {
    {
      oListInfo li;
      li.Name=lang.tl(L"Tävlingsrapport");
      li.supportClasses = false;
      li.supportLegs = false;
      li.listType=li.EBaseTypeNone;
      listMap[EFixedReport]=li;
    }

    {
      oListInfo li;
      li.Name=lang.tl(L"Kontroll inför tävlingen");
      li.supportClasses = false;
      li.supportLegs = false;
      li.listType=li.EBaseTypeNone;
      listMap[EFixedPreReport]=li;
    }

    {
      oListInfo li;
      li.Name=lang.tl(L"Kvar-i-skogen");
      li.supportClasses = false;
      li.supportLegs = false;
      li.listType=li.EBaseTypeNone;
      listMap[EFixedInForest]=li;
    }

    {
      oListInfo li;
      li.Name=lang.tl(L"Fakturor");
      li.supportClasses = false;
      li.supportLegs = false;
      li.listType=li.EBaseTypeNone;
      listMap[EFixedInvoices]=li;
    }

    {
      oListInfo li;
      li.Name=lang.tl(L"Ekonomisk sammanställning");
      li.supportClasses = false;
      li.supportLegs = false;
      li.listType=li.EBaseTypeNone;
      listMap[EFixedEconomy]=li;
    }
  }
  /*
  {
    oListInfo li;
    li.Name=lang.tl("Först-i-mål, klassvis");
    li.supportClasses = false;
    li.supportLegs = false;
    li.listType=li.EBaseTypeNone;
    listMap[EFixedResultFinishPerClass]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl("Först-i-mål, gemensam");
    li.supportClasses = false;
    li.supportLegs = false;
    li.listType=li.EBaseTypeNone;
    listMap[EFixedResultFinish]=li;
  }*/

  if (!filterResults) {
    oListInfo li;
    li.Name=lang.tl(L"Minutstartlista");
    li.supportClasses = false;
    li.supportLegs = false;
    li.listType=li.EBaseTypeNone;
    listMap[EFixedMinuteStartlist]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Händelser - tidslinje");
    li.supportClasses = true;
    li.supportLegs = false;
    li.listType=li.EBaseTypeNone;
    listMap[EFixedTimeLine]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Liveresultat, deltagare");
    li.listType=li.EBaseTypeRunner;
    li.supportClasses = true;
    li.supportLegs = false;
    li.supportFrom = true;
    li.supportTo = true;
    li.supportSplitAnalysis = false;
    li.supportInterResults = false;
    li.supportPageBreak = false;
    li.supportClassLimit = false;
    li.supportCustomTitle = false;
  
    listMap[EFixedLiveResult]=li;
  }

  {
    oListInfo li;
    li.Name=lang.tl(L"Stafettresultat, sträcka (STOR)");
    li.supportClasses = true;
    li.supportLegs = false;
    li.largeSize = true;
    li.listType=li.EBaseTypeTeam;
    listMap[EStdTeamAllLegLARGE]=li;
  }

  getListContainer().setupListInfo(EFirstLoadedList, listMap, filterResults != 0);
}


void oEvent::generateListInfo(const gdioutput& target, EStdListType lt, int classId, oListInfo &li) {
  oListParam par;
  if (classId!=0)
    par.selection.insert(classId);

  par.listCode=lt;
  generateListInfo(target, par, li);
}

int openRunnerTeamCB(gdioutput *gdi, GuiEventType type, BaseInfo *data);

void oEvent::generateFixedList(gdioutput &gdi, const oListInfo &li)
{
  wstring dmy;
  switch (li.lp.listCode) {
    case EFixedPreReport:
      generatePreReport(gdi);
    break;

    case EFixedReport:
      generateCompetitionReport(gdi);
    break;

    case EFixedInForest:
      generateInForestList(gdi, openRunnerTeamCB, 0);
    break;

    case EFixedEconomy:
      printInvoices(gdi, IPTAllPrint, dmy, true);
    break;

    case EFixedInvoices:
      printInvoices(gdi, IPTAllPrint, dmy, false);
    break;

    case EFixedMinuteStartlist:
      generateMinuteStartlist(gdi);
    break;

    case EFixedLiveResult:


    break;

    case EFixedTimeLine:
      gdi.clearPage(false);
      gdi.addString("", boldLarge, makeDash(L"Tidslinje - X#") + getName());

      gdi.dropLine();
      set<__int64> stored;
      vector<oTimeLine> events;

      map<int, wstring> cName;
      for (oClassList::const_iterator it = Classes.begin(); it != Classes.end(); ++it) {
        if (!it->isRemoved())
          cName[it->getId()] = it->getName();
      }

      oe->getTimeLineEvents(li.lp.selection, events, stored, timeConstHour*24*7);
      gdi.fillDown();
      int yp = gdi.getCY();
      int xp = gdi.getCX();

      int w1 = gdi.scaleLength(60);
      int w = gdi.scaleLength(110);
      int w2 = w1+w;

      for (size_t k = 0; k<events.size(); k++) {
        const oTimeLine &ev = events[k];

        pRunner r = dynamic_cast<pRunner>(ev.getSource(*this));
        if (!r)
          continue;

        if (ev.getType() == oTimeLine::TLTFinish && r->getStatus() != StatusOK)
          continue;

        wstring name = L"";
        if (r)
          name = r->getCompleteIdentification() + L" ";

        gdi.addStringUT(yp, xp, 0, oe->getAbsTime(ev.getTime()));
        gdi.addStringUT(yp, xp + w1, 0, cName[ev.getClassId()], w-10);
        gdi.addStringUT(yp, xp + w2, 0, name + lang.tl(ev.getMessage()));

        yp += gdi.getLineHeight();


        /*string detail = ev.getDetail();

        if (detail.size() > 0) {
          gdi.addStringUT(yp, xp + w, 0, detail);
          yp += gdi.getLineHeight();
        }*/

      }
      gdi.refresh();

    break;
  }
}

void oListInfo::setCallback(GUICALLBACK cb) {
  lp.cb=cb;
  for (list<oListInfo>::iterator it = next.begin(); it != next.end(); ++it) {
    it->setCallback(cb);
  }
}

void oEvent::generateListInfo(const gdioutput& target, oListParam &par, oListInfo &li) {
  vector<oListParam> parV(1, par);
  generateListInfo(target, parV, li);
}

void oEvent::generateListInfo(const gdioutput& target, vector<oListParam> &par, oListInfo &li) {
  li.getParam().sourceParam = -1;// Reset source
  loadGeneralResults(false, false);
  for (size_t k = 0; k < par.size(); k++) {
    par[k].cb = 0;
  }

  map<EStdListType, oListInfo> listMap;
  getListTypes(listMap, false);

  if (par.size() == 1) {
    generateListInfoAux(target, par[0], li, listMap[par[0].listCode].Name);
    set<int> used;
    // Add linked lists
    oListParam *cPar = &par[0];
    while (cPar->nextList>0) {
      if (used.count(cPar->nextList))
        break; // Circular definition
      used.insert(cPar->nextList);
      used.insert(cPar->previousList);
      oListParam &nextPar = oe->getListContainer().getParam(cPar->nextList-1);
      li.next.push_back(oListInfo());
      nextPar.cb = 0;
      generateListInfoAux(target, nextPar, li.next.back(), L"");
      cPar = &nextPar;
    }
  }
  else {
    for (size_t k = 0; k < par.size(); k++) {
      if (k > 0) {
        li.next.push_back(oListInfo());
      }
      generateListInfoAux(target, par[k], k == 0 ? li : li.next.back(), 
                          li.Name = listMap[par[0].listCode].Name);
    }
  }
}

extern Image image;

void oEvent::generateListInfoAux(const gdioutput& target, oListParam &par, oListInfo &li, const wstring &name) {
  const int lh=14;
  const int vspace=lh/2;
  int bib;
  pair<int, bool> ln;
  const double scale = 1.8;

  wstring ttl;
  Position pos;

  pClass sampleClass = 0;
  if (!par.selection.empty())
    sampleClass = getClass(*par.selection.begin());
  if (!sampleClass && !Classes.empty())
    sampleClass = &*Classes.begin();

  EStdListType lt=par.listCode;
  li=oListInfo();
  li.lp = par;
  li.Name = name;
  li.lp.defaultName = li.Name;
  if (par.defaultName.empty())
    par.defaultName = li.Name;
  if (par.name.empty())
    par.name = li.Name;
  if (li.lp.name.empty())
    li.lp.name = li.Name;

  switch (lt) {
    case EStdStartList: {
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Startlista - %s", true)), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      
      int bib = 0;
      int rank = 0;
      if (hasBib(true, false)) {
        li.addListPost(oPrintPost(lRunnerBib, L"", normalText, 0, 0));
        bib=40;
      }
      li.addListPost(oPrintPost(lRunnerStart, L"", normalText, 0+bib, 0));
      li.addListPost(oPrintPost(lPatrolNameNames, L"", normalText, 70+bib, 0));
      li.addListPost(oPrintPost(lPatrolClubNameNames, L"", normalText, 300+bib, 0));
      if (hasRank()) {
        li.addListPost(oPrintPost(lRunnerRank, L"", normalText, 470+bib, 0));
        rank = 50;
      }
      li.addListPost(oPrintPost(lRunnerCard, L"", normalText, 470+bib+rank, 0));

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 12));
      li.addSubHead(oPrintPost(lClassLength, lang.tl(L"%s meter"), boldText, 300+bib, 12));
      li.addSubHead(oPrintPost(lClassStartName, L"", boldText, 470+bib+rank, 12));
      li.addSubHead(oPrintPost(lString, L"", boldText, 470+bib, 16));

      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassStartTime;
      li.setFilter(EFilterExcludeDNS);
      break;
    }

    case EStdClubStartList: {
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbstartlista - %s", true)), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));

      if (hasBib(true, true)) {
        pos.add("bib", 40);
        li.addListPost(oPrintPost(lRunnerBib, L"", normalText, 0, 0));
      }

      pos.add("start", li.getMaxCharWidth(this, par.selection, lRunnerStart, L"", normalText));
      li.addListPost(oPrintPost(lRunnerStart, L"", normalText, pos.get("start"), 0));

      if (hasRank()) {
        pos.add("rank", 50);
        li.addListPost(oPrintPost(lRunnerRank, L"", normalText, pos.get("rank"), 0));
      }
      pos.add("name", li.getMaxCharWidth(this, par.selection, lRunnerName, L"", normalText));
      li.addListPost(oPrintPost(lPatrolNameNames, L"", normalText, pos.get("name"), 0));
      pos.add("class", li.getMaxCharWidth(this, par.selection, lClassName, L"", normalText));
      li.addListPost(oPrintPost(lClassName, L"", normalText, pos.get("class"), 0));
      pos.add("length", li.getMaxCharWidth(this, par.selection, lClassLength, L"%s m", normalText));
      li.addListPost(oPrintPost(lClassLength, lang.tl(L"%s m"), normalText, pos.get("length"), 0));
      pos.add("sname", li.getMaxCharWidth(this, par.selection, lClassStartName, L"", normalText));
      li.addListPost(oPrintPost(lClassStartName, L"", normalText, pos.get("sname"), 0));

      pos.add("card", 70);
      li.addListPost(oPrintPost(lRunnerCard, L"", normalText, pos.get("card"), 0));

      li.addSubHead(oPrintPost(lClubName, L"", boldText, 0, 12));
      li.addSubHead(oPrintPost(lString, L"", boldText, 100, 16));

      li.listType=li.EBaseTypeClubRunner;
      li.sortOrder=ClassTeamLeg;
      li.setFilter(EFilterExcludeDNS);
      li.setFilter(EFilterVacant);
      break;
    }

    case EStdClubResultList: {
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Klubbresultatlista - %s", true)), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));

      pos.add("class", li.getMaxCharWidth(this, par.selection, lClassName, L"", normalText));
      li.addListPost(oPrintPost(lClassName, L"", normalText, pos.get("class"), 0));
       
      pos.add("place", 40);
      li.addListPost(oPrintPost(lRunnerPlace, L"", normalText, pos.get("place"), 0));

      pos.add("name", li.getMaxCharWidth(this, par.selection, lPatrolNameNames, L"", normalText));
      li.addListPost(oPrintPost(lRunnerName, L"", normalText, pos.get("name"), 0));

      pos.add("time", li.getMaxCharWidth(this, par.selection, lRunnerTimeStatus, L"", normalText));
      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, pos.get("time"), 0));

      pos.add("after", li.getMaxCharWidth(this, par.selection, lRunnerTimeAfter, L"", normalText));
      li.addListPost(oPrintPost(lRunnerTimeAfter, L"", normalText, pos.get("after"), 0));

      li.addSubHead(oPrintPost(lClubName, L"", boldText, 0, 12));
      li.addSubHead(oPrintPost(lString, L"", boldText, 100, 16));

      li.listType=li.EBaseTypeClubRunner;
      li.sortOrder=ClassResult;
      li.calcResults = true;
      li.setFilter(EFilterVacant);
      break;
    }

    case EStdRentedCard:
    {
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Hyrbricksrapport - %s", true)), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));

      li.addListPost(oPrintPost(lTotalCounter, L"%s", normalText, 0, 0));
      li.addListPost(oPrintPost(lRunnerCard, L"", normalText, 30, 0));
      li.addListPost(oPrintPost(lRunnerName, L"", normalText, 130, 0));
      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));

      li.setFilter(EFilterHasCard);
      li.setFilter(EFilterRentCard);
      li.setFilter(EFilterExcludeDNS);
      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassStartTime;
      break;
    }

    case EStdResultList: {
      wstring stitle;
      getResultTitle(*this, li.lp, stitle);
      stitle = par.getCustomTitle(stitle);

      li.addHead(oPrintPost(lCmpName, makeDash(stitle), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      pos.add("place", 25);
      pos.add("name", li.getMaxCharWidth(this, par.selection, lPatrolNameNames, L"", normalText, 0, false, 25));
      pos.add("club", li.getMaxCharWidth(this, par.selection, lPatrolClubNameNames, L"", normalText, 0, false, 25));
      pos.add("status", li.getMaxCharWidth(this, par.selection, lRunnerTimeStatus, L"", normalText, 0, false, 25));
      pos.add("after", li.getMaxCharWidth(this, par.selection, lRunnerTimeAfter, L"", normalText, 0, false, 25));
      pos.add("missed", 50);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));

      li.addListPost(oPrintPost(lRunnerPlace, L"", normalText, pos.get("place"), 0));
      li.addListPost(oPrintPost(lPatrolNameNames, L"", normalText, pos.get("name"), 0));
      li.addListPost(oPrintPost(lPatrolClubNameNames, L"", normalText, pos.get("club"), 0));

      if (li.lp.useControlIdResultTo<=0 && li.lp.useControlIdResultFrom<=0) {
        li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, pos.get("club"), 10));

        li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, pos.get("status"), 0));
        li.addListPost(oPrintPost(lRunnerTimeAfter, L"", normalText, pos.get("after"), 0));
        if (li.lp.showInterTimes) {
          li.addSubListPost(oPrintPost(lPunchNamedTime, L"", italicSmall, pos.get("name"), 0, make_pair(1, true)));
          li.subListPost.back().fixedWidth = 160;
          li.listSubType = li.EBaseTypeCoursePunches;
        }
        else if (li.lp.showSplitTimes) {
          li.addSubListPost(oPrintPost(lPunchTime, L"", italicSmall, pos.get("name"), 0, make_pair(1, true)));
          li.subListPost.back().fixedWidth = 95;
          li.listSubType = li.EBaseTypeCoursePunches;
        }
      }
      else {
        li.needPunches = oListInfo::PunchMode::SpecificPunch;
        li.addListPost(oPrintPost(lRunnerTempTimeStatus, L"", normalText, pos.get("status"), 0));
        li.addListPost(oPrintPost(lRunnerTempTimeAfter, L"", normalText, pos.get("after"), 0));
      }
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, pos.get("status"), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldText, pos.get("after"), 10));

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", normalText, pos.get("missed"), 0));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldText, pos.get("missed"), 10));
      }

      li.calcResults = true;
      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.supportFrom = true;
      li.supportTo = true;
      li.setFilter(EFilterHasPrelResult);
      li.setFilter(EFilterExcludeCANCEL);
      break;
    }
    case EGeneralResultList: {
      wstring stitle;
      getResultTitle(*this, li.lp, stitle);
      stitle = par.getCustomTitle(stitle);

      gdiFonts normal, header, small;
      double s;

      if (par.useLargeSize) {
        s = scale;
        normal = fontLarge;
        header = boldLarge;
        small = normalText;
      }
      else {
        s = 1.0;
        normal = normalText;
        header = boldText;
        small = italicSmall;
      }

      li.addHead(oPrintPost(lCmpName, makeDash(stitle), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      pos.add("place", li.getMaxCharWidth(this, par.selection, lRunnerGeneralPlace, L"", normalText));
      pos.add("name", li.getMaxCharWidth(this, par.selection, lRunnerCompleteName, L"", normalText));
      pos.add("status", li.getMaxCharWidth(this, par.selection, lRunnerGeneralTimeStatus, L"", normalText));
      pos.add("after", li.getMaxCharWidth(this, par.selection, lRunnerGeneralTimeAfter, L"", normalText));
      pos.add("missed", 50);

      li.addSubHead(oPrintPost(lClassName, L"", header, 0, 10));

      li.addListPost(oPrintPost(lRunnerGeneralPlace, L"", normal, pos.get("place", s), 0));
      li.addListPost(oPrintPost(lRunnerCompleteName, L"", normal, pos.get("name", s), 0));

      if (li.lp.useControlIdResultTo<=0 && li.lp.useControlIdResultFrom<=0) {
        li.addListPost(oPrintPost(lRunnerGeneralTimeStatus, L"", normal, pos.get("status", s), 0));
        li.addListPost(oPrintPost(lRunnerGeneralTimeAfter, L"", normal, pos.get("after", s), 0));
        if (li.lp.showInterTimes) {
          li.addSubListPost(oPrintPost(lPunchNamedTime, L"", small, pos.get("name", s), 0, make_pair(1, true)));
          li.subListPost.back().fixedWidth = 160;
          li.listSubType = li.EBaseTypeCoursePunches;
        }
        else if (li.lp.showSplitTimes) {
          li.addSubListPost(oPrintPost(lPunchTime, L"", small, pos.get("name", s), 0, make_pair(1, true)));
          li.subListPost.back().fixedWidth = 95;
          li.listSubType = li.EBaseTypeCoursePunches;
        }
      }
      else {
        li.needPunches = oListInfo::PunchMode::SpecificPunch;
        li.addListPost(oPrintPost(lRunnerTempTimeStatus, L"", normal, pos.get("status", s), 0));
        li.addListPost(oPrintPost(lRunnerTempTimeAfter, L"", normal, pos.get("after", s), 0));
      }
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), header, pos.get("status", s), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), header, pos.get("after", s), 10));

      li.calcResults = true;
      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasPrelResult);
      li.setFilter(EFilterExcludeCANCEL);
      li.supportFrom = true;
      li.supportTo = true;
      li.calcTotalResults = true;
      break;
    }
    case EIndPriceList:

      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Avgjorda placeringar - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCurrentTime, lang.tl(L"Genererad: ") + L"%s", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      pos.add("place", 25);
      pos.add("name", li.getMaxCharWidth(this, par.selection, lPatrolNameNames, L"", normalText));
      pos.add("club", li.getMaxCharWidth(this, par.selection, lPatrolClubNameNames, L"", normalText));
      pos.add("status", 80);
      pos.add("info", 80);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, pos.get("club"), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, pos.get("status"), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Avgörs kl"), boldText, pos.get("info"), 10));

      li.addListPost(oPrintPost(lRunnerPlace, L"", normalText, pos.get("place"), 0));
      li.addListPost(oPrintPost(lPatrolNameNames, L"", normalText, pos.get("name"), 0));
      li.addListPost(oPrintPost(lPatrolClubNameNames, L"", normalText, pos.get("club"), 0));
      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, pos.get("status"), 0));
      li.addListPost(oPrintPost(lRunnerTimePlaceFixed, L"", normalText, pos.get("info"), 0));

      li.calcResults = true;
      li.listType = li.EBaseTypeRunner;
      li.sortOrder = ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      break;

    case EStdTeamResultList:
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Resultatsammanställning - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", normalText, 280, 14));

      //Use last leg for every team (index=-1)
      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, 0, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, 25, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, 280, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, 340, 5, make_pair(-1, true)));

      li.lp.setLegNumberCoded(-1);
      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.listSubType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);
      break;

    case EStdTeamResultListAll:
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Resultat - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, 280, 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, 400, 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldText, 460, 14));

      //Use last leg for every team (index=-1)
      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, 0, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, 25, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, 400, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, 460, 5, make_pair(-1, true)));

      li.addSubListPost(oPrintPost(lRunnerLegNumberAlpha, L"%s.", normalText, 25, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerName, L"", normalText, 45, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, 280, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lTeamLegTimeStatus, L"", normalText, 400, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lTeamLegTimeAfter, L"", normalText, 460, 0, make_pair(-1, true)));

      if (li.lp.splitAnalysis)  {
        li.addSubListPost(oPrintPost(lRunnerLostTime, L"", normalText, 510, 0));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldText, 510, 14));
      }

      li.lp.setLegNumberCoded(-1);
      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.listSubType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);
      break;

    case unused_EStdTeamResultListLeg: {
      wchar_t title[256];
      if (li.lp.getLegNumberCoded() != 1000)
        swprintf_s(title, (lang.tl(L"Resultat efter sträcka X#" + li.lp.getLegName())+L" - %%s").c_str());
      else
        swprintf_s(title, (lang.tl(L"Resultat efter sträckan")+L" - %%s").c_str());

      pos.add("place", 25);
      pos.add("team", li.getMaxCharWidth(this, par.selection, lTeamName, L"", normalText));
      pos.add("name", li.getMaxCharWidth(this, par.selection, lRunnerName, L"", normalText));
      pos.add("status", 50);
      pos.add("teamstatus", 50);
      pos.add("after", 50);
      pos.add("missed", 50);

      li.addHead(oPrintPost(lCmpName, makeDash(title), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, pos.get("place"), 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, pos.get("name"), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, pos.get("status"), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Totalt"), boldText, pos.get("teamstatus"), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldText, pos.get("after"), 14));

      ln = li.lp.getLegInfo(sampleClass);
      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, pos.get("place"), 2, ln));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, pos.get("team"), 2, ln));
      li.addListPost(oPrintPost(lRunnerName, L"", normalText, pos.get("name"), 2, ln));
      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, pos.get("status"), 2, ln));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, pos.get("teamstatus"), 2, ln));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, pos.get("after"), 2, ln));

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", normalText, pos.get("missed"), 2, ln));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldText, pos.get("missed"), 14));
      }

      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      break;
    }
    case unused_EStdTeamResultListLegLARGE: {
      wchar_t title[256];
      if (li.lp.getLegNumberCoded() != 1000)
        swprintf_s(title, (L"%%s - "+lang.tl(L"sträcka X#" + li.lp.getLegName())).c_str());
      else
        swprintf_s(title, (L"%%s - "+lang.tl(L"slutsträckan")).c_str());

      pos.add("place", 25);
      pos.add("team", min(120, li.getMaxCharWidth(this, par.selection, lTeamName, L"", normalText)));
      pos.add("name", min(120, li.getMaxCharWidth(this, par.selection, lRunnerName, L"", normalText)));
      pos.add("status", 50);
      pos.add("teamstatus", 50);
      pos.add("after", 50);
      pos.add("missed", 50);

      li.addSubHead(oPrintPost(lClassName, makeDash(title), boldLarge, pos.get("place", scale), 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldLarge, pos.get("name", scale), 14));

      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldLarge, pos.get("status", scale), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Totalt"), boldLarge, pos.get("teamstatus", scale), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldLarge, pos.get("after", scale), 14));

      ln = li.lp.getLegInfo(sampleClass);
      li.addListPost(oPrintPost(lTeamPlace, L"", fontLarge, pos.get("place", scale), 5, ln));
      li.addListPost(oPrintPost(lTeamName, L"", fontLarge, pos.get("team", scale), 5, ln));
      li.addListPost(oPrintPost(lRunnerName, L"", fontLarge, pos.get("name", scale), 5, ln));
      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", fontLarge, pos.get("status", scale), 5, ln));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", fontLarge, pos.get("teamstatus", scale), 5, ln));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", fontLarge, pos.get("after", scale), 5, ln));

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", fontLarge, pos.get("missed", scale), 5, ln));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldLarge, pos.get("missed", scale), 14));
      }

      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      break;
    }
    case EStdTeamStartList:
      {
        MetaList mList;
        mList.setListName(L"Startlista, stafett");
        mList.addToHead(lCmpName).setText(L"Startlista - X");;
        mList.newHead();
        mList.addToHead(lCmpDate);

        mList.addToSubHead(lClassName);
        mList.addToSubHead(MetaListPost(lClassStartTime, lNone));
        mList.addToSubHead(MetaListPost(lClassLength, lNone)).setText(L"X meter");
        mList.addToSubHead(MetaListPost(lClassStartName, lNone));

        mList.addToList(lTeamBib);
        mList.addToList(lTeamStartCond);
        mList.addToList(lTeamName);

        mList.addToSubList(lRunnerLegNumberAlpha).align(lTeamName, false).setText(L"X.");
        mList.addToSubList(lRunnerName);
        mList.addToSubList(lRunnerCard).align(lClassStartName);

        mList.interpret(this, target, par, li);
      }
      li.listType=li.EBaseTypeTeam;
      li.listSubType=li.EBaseTypeRunner;
      //li.setFilter(EFilterExcludeDNS);
      li.setSubFilter(ESubFilterVacant);
      li.sortOrder = ClassStartTime;
      break;

    case ETeamCourseList:
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Bantilldelningslista - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      li.listType=li.EBaseTypeTeam;

      bib=0;
      li.addListPost(oPrintPost(lTeamBib, L"", normalText, 0+bib, 4));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, 50+bib, 4));
      li.addListPost(oPrintPost(lTeamClub, L"", normalText, 300+bib, 4));

      li.listSubType=li.EBaseTypeRunner;
      li.addSubListPost(oPrintPost(lRunnerLegNumberAlpha, L"%s.", normalText, 25+bib, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerName, L"", normalText, 50+bib, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerCard, L"", normalText, 300+bib, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerCourse, L"", normalText, 400+bib, 0, make_pair(-1, true)));

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Bricka"), boldText, 300+bib, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Bana"),   boldText, 400+bib, 10));

      li.sortOrder = ClassStartTime;
      break;

    case EIndCourseList: {
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Bantilldelningslista - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      li.listType=li.EBaseTypeRunner;

      bib=0;
      li.addListPost(oPrintPost(lRunnerBib, L"", normalText, 0+bib, 0));
      oPrintPost &rn = li.addListPost(oPrintPost(lRunnerName, L"", normalText, 50+bib, 0));
      rn.doMergeNext = true;
      li.addListPost(oPrintPost(lRunnerClub, L" (%s)", normalText, 50+bib, 0));
      li.addListPost(oPrintPost(lRunnerCard, L"", normalText, 300+bib, 0));
      li.addListPost(oPrintPost(lRunnerCourse, L"", normalText, 400+bib, 0));

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Bricka"), boldText, 300+bib, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Bana"),   boldText, 400+bib, 10));

      li.sortOrder = ClassStartTime;
      break;
    }
    case EStdTeamStartListLeg: {
      wchar_t title[256];
      if (li.lp.getLegNumberCoded() == 1000)
        throw std::exception("Ogiltigt val av sträcka");

      swprintf_s(title, lang.tl(L"Startlista %%s - sträcka X#" + li.lp.getLegName()).c_str());

      li.addHead(oPrintPost(lCmpName, makeDash(title), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));

      ln=li.lp.getLegInfo(sampleClass);
      li.listType=li.EBaseTypeTeam;
      bib=0;
      if (hasBib(false, true)) {
        li.addListPost(oPrintPost(lTeamBib, L"", normalText, 0, 0));
        bib=40;
      }
      li.addListPost(oPrintPost(lTeamStart, L"", normalText, 0+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, 70+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamRunner, L"", normalText, 300+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamRunnerCard, L"", normalText, 520+bib, 0, ln));

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lClassStartName, L"", normalText, 300+bib, 10));

      li.sortOrder=ClassStartTime;
      //li.setFilter(EFilterExcludeDNS);
      break;
    }
    case EStdIndMultiStartListLeg:
      if (li.lp.getLegNumberCoded() == 1000)
        throw std::exception("Ogiltigt val av sträcka");

      //sprintf_s(title, lang.tl("Startlista lopp %d - %%s").c_str(), li.lp.legNumber+1);
      ln=li.lp.getLegInfo(sampleClass);

      ttl = makeDash(lang.tl(L"Startlista lopp X - Y#" + li.lp.getLegName() + L"#%s"));
      li.addHead(oPrintPost(lCmpName, ttl, boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));

      li.listType=li.EBaseTypeTeam;
      bib=0;
      if (hasBib(false, true)) {
        li.addListPost(oPrintPost(lTeamBib, L"", normalText, 0, 0));
        bib=40;
      }
      li.addListPost(oPrintPost(lTeamStart, L"", normalText, 0+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamRunner, L"", normalText, 70+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamClub, L"", normalText, 300+bib, 0, ln));
      li.addListPost(oPrintPost(lTeamRunnerCard, L"", normalText, 500+bib, 0, ln));

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lClassLength, lang.tl(L"%s meter"), boldText, 300+bib, 10, ln));
      li.addSubHead(oPrintPost(lClassStartName, L"", boldText, 500+bib, 10, ln));

      li.sortOrder=ClassStartTime;
      li.setFilter(EFilterExcludeDNS);
      break;

    case EStdIndMultiResultListLeg:
      ln=li.lp.getLegInfo(sampleClass);

      if (li.lp.getLegNumberCoded() != 1000)
        ttl = lang.tl(L"Resultat lopp X - Y#" + li.lp.getLegName() + L"#%s");
      else
        ttl = lang.tl(L"Resultat - X#%s");

      li.addHead(oPrintPost(lCmpName, makeDash(ttl), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, 260, 14));

      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, 460, 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Totalt"), boldText, 510, 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldText, 560, 14));

      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, 0, 0, ln));
      li.addListPost(oPrintPost(lRunnerName, L"", normalText, 40, 0, ln));
      li.addListPost(oPrintPost(lRunnerClub, L"", normalText, 260, 0, ln));

      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, 460, 0, ln));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, 510, 0, ln));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, 560, 0, ln));

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", normalText, 620, 0, ln));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldText, 620, 14));
      }

      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      break;

    case EStdIndMultiResultListLegLARGE:
      if (li.lp.getLegNumberCoded() == 1000)
        throw std::exception("Ogiltigt val av sträcka");

      ln=li.lp.getLegInfo(sampleClass);

      pos.add("place", 25);
      pos.add("name", li.getMaxCharWidth(this, par.selection, lRunnerName, L"", normalText));
      pos.add("club", li.getMaxCharWidth(this, par.selection, lRunnerClub, L"", normalText));

      pos.add("status", 50);
      pos.add("teamstatus", 50);
      pos.add("after", 50);

      ttl = L"%s - " + lang.tl(L"Lopp ") + li.lp.getLegName();
      li.addSubHead(oPrintPost(lClassName, makeDash(ttl), boldLarge, pos.get("place", scale), 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldLarge, pos.get("club", scale), 14));

      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldLarge, pos.get("status", scale), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Totalt"), boldLarge, pos.get("teamstatus", scale), 14));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldLarge, pos.get("after", scale), 14));

      li.addListPost(oPrintPost(lTeamPlace, L"", fontLarge, pos.get("place", scale), 0, ln));
      li.addListPost(oPrintPost(lRunnerName, L"", fontLarge, pos.get("name", scale), 0, ln));
      li.addListPost(oPrintPost(lRunnerClub, L"", fontLarge, pos.get("club", scale), 0, ln));

      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", fontLarge, pos.get("status", scale), 0, ln));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", fontLarge, pos.get("teamstatus", scale), 0, ln));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", fontLarge, pos.get("after", scale), 0, ln));

      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      break;

    case EStdIndMultiResultListAll:
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Resultat - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, L"", boldText, 280, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, lang.tl(L"Tid"), boldText, 480, 14));
      li.addSubHead(oPrintPost(lClassResultFraction, lang.tl(L"Efter"), boldText, 540, 14));

      //Use last leg for every team (index=-1)
      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, 0, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lRunnerName, L"", normalText, 25, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lRunnerClub, L"", normalText, 280, 5, make_pair(-1, true)));

      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, 480, 5, make_pair(-1, true)));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, 540, 5, make_pair(-1, true)));

      li.addSubListPost(oPrintPost(lSubSubCounter, lang.tl(L"Lopp %s"), normalText, 25, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, 90, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lTeamLegTimeStatus, L"", normalText, 150, 0, make_pair(-1, true)));
      li.addSubListPost(oPrintPost(lTeamLegTimeAfter, L"", normalText, 210, 0, make_pair(-1, true)));

      li.lp.setLegNumberCoded(-1);
      li.calcResults=true;
      li.listType=li.EBaseTypeTeam;
      li.listSubType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      break;
    case EStdPatrolStartList:
    {
      MetaList mList;
      mList.setListName(L"Startlista, patrull");

      mList.addToHead(lCmpName).setText(L"Startlista - X").align(false);
      mList.newHead();
      mList.addToHead(lCmpDate).align(false);

      mList.addToSubHead(lClassName);
      mList.addToSubHead(lClassStartTime);
      mList.addToSubHead(lClassLength).setText(L"X meter");
      mList.addToSubHead(lClassStartName);

      mList.addToList(lTeamBib);
      mList.addToList(lTeamStartCond).setLeg(0);
      mList.addToList(lTeamName);

      mList.newListRow();

      mList.addToList(MetaListPost(lTeamRunner, lTeamName, 0));
      mList.addToList(MetaListPost(lTeamRunnerCard, lAlignNext, 0));
      mList.addToList(MetaListPost(lTeamRunner, lAlignNext, 1));
      mList.addToList(MetaListPost(lTeamRunnerCard, lAlignNext, 1));

      mList.setListType(li.EBaseTypeTeam);
      mList.setSortOrder(ClassStartTime);
      mList.addFilter(EFilterExcludeDNS);
      mList.interpret(this, target, par, li);
      break;
    }
    case EStdPatrolResultList:
      li.addHead(oPrintPost(lCmpName, makeDash(lang.tl(L"Resultatlista - %s")), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addListPost(oPrintPost(lTeamPlace, L"", normalText, 0, vspace, make_pair(1, true)));
      li.addListPost(oPrintPost(lTeamName, L"", normalText, 70, vspace));
      //li.addListPost(oPrintPost(lTeamClub, "", normalText, 250, vspace));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", normalText, 400, vspace, make_pair(1, true)));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", normalText, 460, vspace, make_pair(1, true)));

      li.addListPost(oPrintPost(lTeamRunner, L"", normalText, 70, lh+vspace, make_pair(0, true)));
      li.addListPost(oPrintPost(lTeamRunner, L"", normalText, 250, lh+vspace, make_pair(1, true)));
      li.setFilter(EFilterHasResult);

      li.addSubHead(oPrintPost(lClassName, L"", boldText, 0, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, 400, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldText, 460, 10));

      if (li.lp.showInterTimes) {
        li.addSubListPost(oPrintPost(lPunchNamedTime, L"", normalText, 10, 0, make_pair(1, true)));
        li.subListPost.back().fixedWidth=160;
        li.listSubType=li.EBaseTypeCoursePunches;
      }

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", normalText, 520, vspace, make_pair(1, true)));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldText, 520, 10));
      }

      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.lp.setLegNumberCoded(-1);
      li.calcResults=true;
      li.setFilter(EFilterExcludeCANCEL);

      break;

    case EStdPatrolResultListLARGE:
      pos.add("place", 25);
      pos.add("team", int(0.7*max(li.getMaxCharWidth(this, par.selection, lTeamName, L"", normalText),
                        li.getMaxCharWidth(this, par.selection, lPatrolNameNames, L"", normalText))));

      pos.add("status", 50);
      pos.add("after", 50);
      pos.add("missed", 50);

      li.addListPost(oPrintPost(lTeamPlace, L"", fontLarge, pos.get("place", scale), vspace, make_pair(1, true)));
      li.addListPost(oPrintPost(lTeamName, L"", fontLarge, pos.get("team", scale), vspace));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", fontLarge, pos.get("status", scale), vspace, make_pair(1, true)));
      li.addListPost(oPrintPost(lTeamTimeAfter, L"", fontLarge, pos.get("after", scale), vspace, make_pair(1, true)));

      li.addListPost(oPrintPost(lPatrolNameNames, L"", fontLarge, pos.get("team", scale), 25+vspace, make_pair(0, true)));
      //li.addListPost(oPrintPost(lTeamRunner, "", fontLarge, pos.get("status", scale), 25+vspace, 1));

      li.addSubHead(oPrintPost(lClassName, L"", boldLarge, 0, 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldLarge, pos.get("status", scale), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Efter"), boldLarge, pos.get("after", scale), 10));

      if (li.lp.showInterTimes) {
        li.addSubListPost(oPrintPost(lPunchNamedTime, L"", normalText, 10, 0, make_pair(1, true)));
        li.subListPost.back().fixedWidth=200;
        li.listSubType=li.EBaseTypeCoursePunches;
      }

      if (li.lp.splitAnalysis)  {
        li.addListPost(oPrintPost(lRunnerLostTime, L"", fontLarge, pos.get("missed", scale), vspace, make_pair(0, true)));
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldLarge, pos.get("missed", scale), 10));
      }

      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      li.listType=li.EBaseTypeTeam;
      li.sortOrder=ClassResult;
      li.lp.setLegNumberCoded(-1);
      li.calcResults=true;
      break;

    case unused_EStdRaidResultListLARGE:
      li.addListPost(oPrintPost(lTeamPlace, L"", fontLarge, 0, vspace));
      li.addListPost(oPrintPost(lTeamName, L"", fontLarge, 40, vspace));
      li.addListPost(oPrintPost(lTeamTimeStatus, L"", fontLarge, 490, vspace));

      li.addListPost(oPrintPost(lTeamRunner, L"", fontLarge, 40, 25+vspace, make_pair(0, true)));
      li.addListPost(oPrintPost(lTeamRunner, L"", fontLarge, 300, 25+vspace, make_pair(1, true)));

      li.addSubListPost(oPrintPost(lPunchNamedTime, L"", fontMedium, 0, 2, make_pair(1, true)));
      li.subListPost.back().fixedWidth=200;
      li.setFilter(EFilterHasResult);

      li.addSubHead(oPrintPost(lClassName, L"", boldLarge, 0, 10));

      li.listType=li.EBaseTypeTeam;
      li.listSubType=li.EBaseTypeCoursePunches;
      li.sortOrder=ClassResult;
      li.calcResults=true;
      break;

   case EStdResultListLARGE:
      pos.add("place", 25);
      pos.add("name", li.getMaxCharWidth(this, par.selection, lPatrolNameNames, L"", normalText, 0, true));
      pos.add("club", li.getMaxCharWidth(this, par.selection, lPatrolClubNameNames, L"", normalText, 0, true));
      pos.add("status", 50);
      pos.add("missed", 50);

      li.addListPost(oPrintPost(lRunnerPlace, L"", fontLarge, pos.get("place", scale), vspace));
      li.addListPost(oPrintPost(lPatrolNameNames, L"", fontLarge, pos.get("name", scale), vspace));
      li.addListPost(oPrintPost(lPatrolClubNameNames, L"", fontLarge, pos.get("club", scale), vspace));

      li.addSubHead(oPrintPost(lClassName, L"", boldLarge, pos.get("place", scale), 10));

      if (li.lp.useControlIdResultTo<=0 && li.lp.useControlIdResultFrom<=0) {
        li.addSubHead(oPrintPost(lClassResultFraction, L"", boldLarge, pos.get("club", scale), 10));
        li.addListPost(oPrintPost(lRunnerTimeStatus, L"", fontLarge, pos.get("status", scale), vspace));
      }
      else {
        li.needPunches = oListInfo::PunchMode::SpecificPunch;
        li.addListPost(oPrintPost(lRunnerTempTimeStatus, L"", normalText, pos.get("status", scale), vspace));
      }
      if (li.lp.splitAnalysis) {
        li.addSubHead(oPrintPost(lString, lang.tl(L"Bomtid"), boldLarge, pos.get("missed", scale), 10));
        li.addListPost(oPrintPost(lRunnerLostTime, L"", fontLarge, pos.get("missed", scale), vspace));
      }

      if (li.lp.showInterTimes) {
        li.addSubListPost(oPrintPost(lPunchNamedTime, L"", normalText, 0, 0, make_pair(1, true)));
        li.subListPost.back().fixedWidth = 160;
        li.listSubType = li.EBaseTypeCoursePunches;
      }
      else if (li.lp.showSplitTimes) {
        li.addSubListPost(oPrintPost(lPunchTime, L"", normalText, 0, 0, make_pair(1, true)));
        li.subListPost.back().fixedWidth = 95;
        li.listSubType = li.EBaseTypeCoursePunches;
      }

      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      li.lp.setLegNumberCoded(0);
      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.calcResults=true;

      li.supportFrom = true;
      li.supportTo = true;
      break;

   case EStdUM_Master:
      li.addListPost(oPrintPost(lRunnerPlace, L"", fontMedium, 0, 0));
      li.addListPost(oPrintPost(lRunnerName, L"", fontMedium, 40, 0));
      li.addListPost(oPrintPost(lRunnerClub, L"", fontMedium, 250, 0));
      li.addListPost(oPrintPost(lClassName, L"", fontMedium, 490, 0));
      li.addListPost(oPrintPost(lRunnerUMMasterPoint, L"", fontMedium, 580, 0));

      li.setFilter(EFilterHasResult);

      li.lp.setLegNumberCoded(0);
      li.listType=li.EBaseTypeRunner;
      li.sortOrder=ClassResult;
      li.calcResults=true;
      break;

   case ERogainingInd:
      pos.add("place", 25);
      pos.add("name", li.getMaxCharWidth(this, par.selection, lRunnerCompleteName, L"", normalText, 0, true));
      pos.add("points", 50);
      pos.add("status", 50);
      li.addHead(oPrintPost(lCmpName, makeDash(par.getCustomTitle(lang.tl(L"Rogainingresultat - %s"))), boldLarge, 0,0));
      li.addHead(oPrintPost(lCmpDate, L"", normalText, 0, 25));
      generateNBestHead(par, li, 25+lh);

      li.addListPost(oPrintPost(lRunnerPlace, L"", normalText, pos.get("place"), vspace));
      li.addListPost(oPrintPost(lRunnerCompleteName, L"", normalText, pos.get("name"), vspace));
      li.addListPost(oPrintPost(lRunnerRogainingPoint, L"%sp", normalText, pos.get("points"), vspace));
      li.addListPost(oPrintPost(lRunnerTimeStatus, L"", normalText, pos.get("status"), vspace));

      li.setFilter(EFilterHasResult);
      li.setFilter(EFilterExcludeCANCEL);

      if (li.lp.splitAnalysis || li.lp.showInterTimes) {
        li.addSubListPost(oPrintPost(lRogainingPunch, L"", normalText, 10, 0, make_pair(1, true)));
        li.subListPost.back().fixedWidth=130;
        li.listSubType=li.EBaseTypeCoursePunches;
      }

      li.addSubHead(oPrintPost(lClassName, L"", boldText, pos.get("place"), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Poäng"), boldText, pos.get("points"), 10));
      li.addSubHead(oPrintPost(lString, lang.tl(L"Tid"), boldText, pos.get("status"), 10));

      li.listType=li.EBaseTypeRunner;
      li.sortOrder = ClassPoints;
      li.lp.setLegNumberCoded(-1);
      li.calcResults = true;
      li.rogainingResults = true;
    break;

    case EStdTeamAllLegLARGE: {
      vector< pair<wstring, size_t> > out;
      fillLegNumbers(par.selection, false, false, out);
      par.listCode = oe->getListContainer().getType("legresult");
      par.useLargeSize = true;
      for (size_t k = 0; k < out.size(); k++) {
        if (out[k].second >= 1000)
          continue;
        par.setLegNumberCoded(out[k].second);
        if (k == 0)
          generateListInfo(target, par, li);
        else {
          li.next.push_back(oListInfo());
          generateListInfo(target, par, li.next.back());
        }
      }
    }
    break;

    case EFixedPreReport:
    case EFixedReport:
    case EFixedInForest:
    case EFixedEconomy:
    case EFixedInvoices:
    case EFixedMinuteStartlist:
    case EFixedTimeLine:
    case EFixedLiveResult:
      li.fixedType = true;
    break;

    default:
      if (!getListContainer().interpret(this, target, par, li))
        throw meosException("Could not load list 'X'#L" + itos(par.listCode));
  }
}

string oPrintPost::encodeFont(const string &face, int factor) {
  string out(face);
  if (factor > 0 && factor != 100) {
    out += ";" + itos(factor/100) + "." + itos(factor%100);
  }
  return out;
}

wstring oPrintPost::encodeFont(const wstring &face, int factor) {
  wstring out(face);
  if (factor > 0 && factor != 100) {
    out += L";" + itow(factor/100) + L"." + itow(factor%100);
  }
  return out;
}

void oListInfo::setupLinks(const list<oPrintPost> &lst) const {
  for (list<oPrintPost>::const_iterator it = lst.begin(); it != lst.end(); ++it) {
    list<oPrintPost>::const_iterator itNext = it;
    ++itNext;
    if (itNext != lst.end() && it->doMergeNext)
      it->mergeWithTmp = &*itNext;
    else
      it->mergeWithTmp = 0;
  }
}

void oListInfo::setupLinks() const {
  setupLinks(head);
  setupLinks(subHead);
  setupLinks(listPost);
  setupLinks(subListPost);
}

void oListInfo::shrinkSize() {

  auto scale = [](int& format) -> double {

    int highFormat = format & ~0xFF;
    format &= 0xFF;

    double s = 1.0;
    double fs = GDIImplFontSet::baseSize(format, 1.0);
    if (format == boldLarge)
      format = boldText;
    else if (format == boldHuge)
      format = boldLarge;
    else if (format == boldText)
      format = boldSmall;
    else if (format == normalText || format == fontMedium)
      format = fontSmall;
    else if (format = fontMediumPlus || format == fontLarge)
      format = fontMedium;
    else if (format == italicText)
      format = italicSmall;
    else if (format == italicMediumPlus)
      format = italicText;
    double fsNew = GDIImplFontSet::baseSize(format, 1.0);
    format |= highFormat;
    return fsNew/fs;
  };

  auto scaleForward = [](oPrintPost& pp, double scale, 
                         list<oPrintPost> &pList, map<int, int> &yOffset) {
    if (pp.xlimit > 0)
      pp.xlimit = int(pp.xlimit*scale);
    
    int minDX = -1;
    for (auto& h : pList) {
      if (h.dy == pp.dy && h.dx > pp.dx) {
        int df = h.dx - pp.dx;
        //h.dx = pp.dx + int(df * scale);
        int dx = h.dx - (pp.dx + int(df * scale));
        assert(dx >= 0);
        if (minDX == -1 || dx < minDX)
          minDX = dx;
      }
    }

    for (auto& h : pList) {
      if (h.dy == pp.dy && h.dx > pp.dx) {
        h.dx -= minDX;
      }
    }

    int dy = 0;
    for (auto& h : pList) {
      if (h.dy > pp.dy) {
        dy = h.dy - int(pp.dy + (h.dy - pp.dy) * scale);
        auto res = yOffset.emplace(pp.dy, dy);
        if (!res.second) {
          if (dy > res.first->second) {
            // Already moved. Only do the extra offset and store the highest offset.
            int newDY = dy - res.first->second;
            res.first->second = dy;
            dy = newDY;
          }
        }
        break;
      }
    }
    if (dy > 0) {
      for (auto& h : pList) {
        if (h.dy > pp.dy) {
          h.dy -= dy;
        }
      }
    }
  };

  list<oPrintPost>* allList[4] = { &head, &subHead, &listPost, &subListPost };

  // Store original position in a map
  map<int, vector<pair<int, int>>> xOffsetToListPost;
  for (int j = 0; j < 4; j++) {
    list<oPrintPost>& lp = *allList[j];
    int ix = 0;
    for (auto& h : lp) {
      int off = h.dx;
      xOffsetToListPost[off].emplace_back(j, ix);
      ix++;
    }
  }

  for (int j = 0; j < 4; j++) {
    list<oPrintPost>& lp = *allList[j];
    map<int, int> yOffsets;
    for (auto& h : lp) {
      double s = scale(h.format);
      if (s < 1.0)
        scaleForward(h, s, lp, yOffsets);
    }
  }

  map<pair<int, int>, oPrintPost*> listPostToOffsetAfter;

  for (int j = 0; j < 4; j++) {
    list<oPrintPost>& lp = *allList[j];
    int ix = 0;
    for (auto& h : lp) {
      listPostToOffsetAfter[make_pair(j, ix)] = &h;
      ix++;
    }
  }

  for (auto& it : xOffsetToListPost) {
    int minNewPos = -1;
    for (pair<int, int>& lpIx : it.second) {
      oPrintPost &pp = *listPostToOffsetAfter[lpIx];
      if (minNewPos == -1 || minNewPos > pp.dx)
        minNewPos = pp.dx;
    }

    for (pair<int, int>& lpIx : it.second) {
      oPrintPost& pp = *listPostToOffsetAfter[lpIx];
      int off = pp.dx - minNewPos;

      if (off > 0) {
        pp.dx = minNewPos;
        // TODO: Offset forward
      }
    }
  }
    

  /*for (auto& h : Head) {
    scale(h.format);
  }
  
  for (auto& h : subHead) {
    scale(h.format);
  }

  for (auto& h : listPost) {
    double s = scale(h.format);
    if (s < 1.0)
      scaleForward(h, s, listPost);
  }

  for (auto& h : subListPost) {
    scale(h.format);
  }*/
}


oListInfo::ResultType oListInfo::getResultType() const {
  return resType;
}

bool oListParam::matchLegNumber(const pClass cls, int leg)  const {
  if (cls == 0 || legNumber == -1 || leg < 0)
    return true;
  int number, order;
  cls->splitLegNumberParallel(leg, number, order);
  if (number == legNumber)
    return true;
  int sub = legNumber % 10000;
  int maj = legNumber / 10000;
  return maj == number + 1 && sub == order;
}

int oListParam::getLegNumber(const pClass cls) const {
  if (legNumber < 0)
    return legNumber;

  int sub = legNumber % 10000;
  int maj = legNumber / 10000;
  
  if (cls) {
    if (legNumber < 10000)
      return cls->getLegNumberLinear(legNumber, 0);
    else
      return cls->getLegNumberLinear(maj-1, sub);
  }
  return sub;
}

pair<int, bool> oListParam::getLegInfo(const pClass cls) const {
  if (legNumber == -1)
    return make_pair(-1, true);
  else if (legNumber < 10000)
    return make_pair(legNumber, false);

  int sub = legNumber % 10000;
  int maj = legNumber / 10000;
  
  int lin = cls ? cls->getLegNumberLinear(maj-1, sub) : sub+maj;
  
  return make_pair(lin, true);
}

wstring oListParam::getLegName() const {
  if (legNumber == -1)
    return L"";

  if (legNumber < 1000)
    return itow(legNumber + 1);

  int sub = legNumber % 10000;
  int maj = legNumber / 10000;
  
  wchar_t bf[64];
  char symb = 'a' + sub;
  swprintf_s(bf, L"%d%c", maj, symb);
  return bf;
}


void SplitPrintListInfo::serialize(xmlparser& xml) const {
  xml.writeBool("Split", includeSplitTimes);
  xml.writeBool("Speed", withSpeed);
  xml.writeBool("Result", withResult);
  xml.writeBool("Analysis", withAnalysis);
  xml.writeBool("Heading", withStandardHeading);
  xml.write("NumClsResult", numClassResults);
}

void SplitPrintListInfo::deserialize(const xmlobject& xml) {
  includeSplitTimes = xml.getObjectBool("Split");
  withSpeed = xml.getObjectBool("Speed");
  withResult = xml.getObjectBool("Result");
  withAnalysis = xml.getObjectBool("Analysis");
  withStandardHeading = xml.getObjectBool("Heading");
  if (xml.got("NumClsResult"))
    numClassResults = xml.getObjectInt("NumClsResult");
}

int64_t SplitPrintListInfo::checkSum() const {
  int64_t ch = 0;
  ch = 2 * ch + includeSplitTimes;
  ch = 2 * ch + withSpeed;
  ch = 2 * ch + withResult;
  ch = 2 * ch + withAnalysis;

  return ch;
}
