#pragma once
/************************************************************************
    MeOS - Orienteering Software
    Copyright (C) 2009-2017 Melin Software HB

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

    Melin Software HB - software@melin.nu - www.melin.nu
    Eksoppsvägen 16, SE-75646 UPPSALA, Sweden

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

#include <set>
#include <vector>
#include <map>
#include "oBase.h"
#include "gdifonts.h"
#include "gdioutput.h"
#include "oEvent.h"
class oClass;

typedef oEvent *pEvent;
typedef oClass *pClass;

enum EPostType
{
  lAlignNext,
  lNone,
  lString,
  lResultDescription,
  lTimingFromName,
  lTimingToName,
  lCmpName,
  lCmpDate,
  lCurrentTime,
  lClubName,
  lClassName,
  lClassStartName,
  lClassStartTime,
  lClassStartTimeRange,
  lClassLength,
  lClassResultFraction,
  lCourseLength,
  lCourseName,
  lCourseClimb,
  lCourseShortening,
  lCourseUsage,
  lCourseUsageNoVacant,
  lCourseClasses,
  lRunnerName,
  lRunnerGivenName,
  lRunnerFamilyName,
  lRunnerCompleteName,
  lPatrolNameNames, // Single runner's name or both names in a patrol
  lPatrolClubNameNames, // Single runner's club or combination of patrol clubs
  lRunnerFinish,
  lRunnerTime,
  lRunnerTimeStatus,
  lRunnerTotalTime,
  lRunnerTimePerKM,
  lRunnerTotalTimeStatus,
  lRunnerTotalPlace,
  lRunnerPlaceDiff,
  lRunnerClassCoursePlace,
  lRunnerTotalTimeAfter,
  lRunnerClassCourseTimeAfter,
  lRunnerTimeAfterDiff,
  lRunnerTempTimeStatus,
  lRunnerTempTimeAfter,
  lRunnerGeneralTimeStatus,
  lRunnerGeneralPlace,
  lRunnerGeneralTimeAfter,
  lRunnerTimeAfter,
  lRunnerMissedTime,
  lRunnerPlace,
  lRunnerStart,
  lRunnerStartCond,
  lRunnerStartZero,
  lRunnerClub,
  lRunnerCard,
  lRunnerBib,
  lRunnerStartNo,
  lRunnerRank,
  lRunnerCourse,
  lRunnerRogainingPoint,
  lRunnerRogainingPointTotal,
  lRunnerRogainingPointReduction,
  lRunnerRogainingPointOvertime,
  lRunnerRogainingPointGross,
  lRunnerTimeAdjustment,
  lRunnerPointAdjustment,

  lRunnerUMMasterPoint,
  lRunnerTimePlaceFixed,
  lRunnerLegNumberAlpha,
  lRunnerLegNumber,

  lRunnerBirthYear,
  lRunnerAge,
  lRunnerSex,
  lRunnerNationality,
  lRunnerPhone,
  lRunnerFee,
  lRunnerPaid,
  lRunnerPayMethod,
  lRunnerEntryDate,
  lRunnerEntryTime,

  lTeamName,
  lTeamStart,
  lTeamStartCond,
  lTeamStartZero,
  lTeamTimeStatus,
  lTeamTimeAfter,
  lTeamPlace,
  lTeamLegTimeStatus,
  lTeamLegTimeAfter,
  lTeamRogainingPoint,
  lTeamRogainingPointTotal,
  lTeamRogainingPointReduction,
  lTeamRogainingPointOvertime,
  lTeamTimeAdjustment,
  lTeamPointAdjustment,

  lTeamTime,
  lTeamStatus,
  lTeamClub,
  lTeamRunner,
  lTeamRunnerCard,
  lTeamBib,
  lTeamStartNo,
  lTeamFee,

  lTeamTotalTime,
  lTeamTotalTimeStatus,
  lTeamTotalPlace,
  lTeamTotalTimeAfter,
  lTeamTotalTimeDiff,
  lTeamPlaceDiff,

  lPunchNamedTime,
  lPunchTime,
  lPunchControlNumber,
  lPunchControlCode,
  lPunchLostTime,
  lPunchControlPlace,
  lPunchControlPlaceAcc,

  lResultModuleTime,
  lResultModuleNumber,
  lResultModuleTimeTeam,
  lResultModuleNumberTeam,
  
  lCountry,
  lNationality,

  lControlName,
  lControlCourses,
  lControlClasses,
  lControlVisitors,
  lControlPunches,
  lControlMedianLostTime,
  lControlMaxLostTime,
  lControlMistakeQuotient,
  lControlRunnersLeft,
  lControlCodes,

  lRogainingPunch,
  lTotalCounter,
  lSubCounter,
  lSubSubCounter,
  lLastItem
};

enum EStdListType
{
  EStdNone=-1,
  EStdStartList=1,
  EStdResultList,
  EGeneralResultList,
  ERogainingInd,
  EStdTeamResultListAll,
  unused_EStdTeamResultListLeg,//EStdTeamResultListLeg,
  EStdTeamResultList,
  EStdTeamStartList,
  EStdTeamStartListLeg,
  EStdIndMultiStartListLeg,
  EStdIndMultiResultListLeg,
  EStdIndMultiResultListAll,
  EStdPatrolStartList,
  EStdPatrolResultList,
  EStdRentedCard,
  EStdResultListLARGE,
  unused_EStdTeamResultListLegLARGE,//EStdTeamResultListLegLARGE,
  EStdPatrolResultListLARGE,
  EStdIndMultiResultListLegLARGE,
  unused_EStdRaidResultListLARGE,//EStdRaidResultListLARGE, //Obsolete
  ETeamCourseList,
  EIndCourseList,
  EStdClubStartList,
  EStdClubResultList,

  EIndPriceList,
  EStdUM_Master,

  EFixedPreReport,
  EFixedReport,
  EFixedInForest,
  EFixedInvoices,
  EFixedEconomy,
  unused_EFixedResultFinishPerClass,
  unused_EFixedResultFinish,
  EFixedMinuteStartlist,
  EFixedTimeLine,
  EFixedLiveResult,

  EStdTeamAllLegLARGE,
  
  EFirstLoadedList = 1000
};

enum EFilterList
{
  EFilterHasResult,
  EFilterHasPrelResult,
  EFilterRentCard,
  EFilterHasCard,
  EFilterHasNoCard,
  EFilterExcludeDNS,
  EFilterVacant,
  EFilterOnlyVacant,
  _EFilterMax
};

enum ESubFilterList
{
  ESubFilterHasResult,
  ESubFilterHasPrelResult,
  ESubFilterExcludeDNS,
  ESubFilterVacant,
  ESubFilterSameParallel,
  ESubFilterSameParallelNotFirst,
  _ESubFilterMax
};

enum gdiFonts;

struct oPrintPost {
  oPrintPost();
  oPrintPost(EPostType type_, const wstring &format_,
             int style_, int dx_, int dy_, 
             pair<int, bool> legIndex_=make_pair(0, true));

  static string encodeFont(const string &face, int factor);
  static wstring encodeFont(const wstring &face, int factor);

  EPostType type;
  wstring text;
  wstring fontFace;
  int resultModuleIndex;
  int format;
  GDICOLOR color;
  int dx;
  int dy;
  int legIndex;
  bool linearLegIndex;
  gdiFonts getFont() const {return gdiFonts(format & 0xFF);}
  oPrintPost &setFontFace(const wstring &font, int factor) {
    fontFace = encodeFont(font, factor);
    return *this;
  }
  int fixedWidth;
  bool doMergeNext;
  mutable const oPrintPost *mergeWithTmp; // Merge text with this output
};

class gdioutput;
enum gdiFonts;
typedef int (*GUICALLBACK)(gdioutput *gdi, int type, void *data);
class xmlparser;
class xmlobject;
class MetaListContainer;

struct oListParam {
  oListParam();

  bool operator==(const oListParam& a) const {
    return a.listCode == listCode &&
      a.selection == selection &&
      a.useControlIdResultFrom == useControlIdResultFrom &&
      a.useControlIdResultTo == useControlIdResultTo &&
      a.filterMaxPer == filterMaxPer &&
      a.pageBreak == pageBreak &&
      a.showInterTimes == showInterTimes &&
      a.showSplitTimes == showSplitTimes &&
      a.inputNumber == inputNumber &&
      a.nextList == nextList &&
      a.previousList == previousList &&
      a.bgColor == bgColor &&
      a.bgColor2 == bgColor2 &&
      a.bgImage == bgImage &&
      a.legNumber == legNumber;
  }
  EStdListType listCode;
  GUICALLBACK cb;
  set<int> selection;
  
  bool lockUpdate; // Temporary prevent animation update

  int useControlIdResultTo;
  int useControlIdResultFrom;
  int filterMaxPer;
  bool pageBreak;
  bool showInterTimes;
  bool showSplitTimes;
  bool splitAnalysis;
  bool showInterTitle;
  wstring title;
  wstring name;
  int inputNumber;
  int nextList; // 1-based index of next list (in the container, MetaListParam::listParam) for linked lists
  int previousList; // 1-based index of previous list (in the container, MetaListParam::listParam) for linked lists. Not serialized

  mutable int relayLegIndex; // Current index of leg (or -1 for entire team)
  mutable wstring defaultName; // Initialized when generating list
  // Generate a large-size list (supported as input when supportLarge is true)
  bool useLargeSize;
  bool saved;

  int bgColor;
  int bgColor2;

  int fgColor;
  wstring bgImage;

  int nColumns;
  bool animate;
  int timePerPage;
  int margin;
  int screenMode;// 0 normal window, 1 = page by page, 2 = scroll

  void updateDefaultName(const wstring &pname) const {defaultName = pname;}
  void setCustomTitle(const wstring &t) {title = t;}
  void getCustomTitle(wchar_t *t) const; // 256 size buffer required. Get title if set
  const wstring &getCustomTitle(const wstring &t) const;
  const wstring &getDefaultName() const {return defaultName;}
  void setName(const wstring &n) {name = n;}
  const wstring &getName() const {return name;}

  int getInputNumber() const {return inputNumber;}
  void setInputNumber(int n) {inputNumber = n;}

  void serialize(xmlparser &xml, 
                 const MetaListContainer &container, 
                 const map<int, int> &idToIndex) const;
  void deserialize(const xmlobject &xml, const MetaListContainer &container);

  void setLegNumberCoded(int code) {
    if (code == 1000)
      legNumber = -1;
    else
      legNumber = code;
  }

  bool matchLegNumber(const pClass cls, int leg) const;
  int getLegNumber(const pClass cls) const;
  pair<int, bool> getLegInfo(const pClass cls) const;

  wstring getLegName() const;

  const int getLegNumberCoded() const {
    return legNumber >= 0 ? legNumber : 1000;
  }

private:
   int legNumber;
};

class oListInfo {
public:
  enum EBaseType {EBaseTypeRunner,
                  EBaseTypeTeam,
                  EBaseTypeClub,
                  EBaseTypePunches,
                  EBaseTypeNone,
                  EBaseTypeRunnerGlobal,  // Used only in metalist (meaning global, not classwise)
                  EBaseTypeRunnerLeg,  // Used only in metalist, meaning legwise
                  EBaseTypeTeamGlobal, // Used only in metalist (meaning global, not classwise)
                  EBaseTypeCourse,
                  EBaseTypeControl,
                  EBasedTypeLast_};

  bool isTeamList() const {return listType == EBaseTypeTeam;}
  
  enum ResultType {
    Global,
    Classwise,
    Legwise,
    Coursewise
  };

  static bool addRunners(EBaseType t) {return t == EBaseTypeRunner || t == EBaseTypeClub;}
  static bool addTeams(EBaseType t) {return t == EBaseTypeTeam || t == EBaseTypeClub;}
  static bool addPatrols(EBaseType t) {return t == EBaseTypeTeam || t == EBaseTypeClub;}

  const wstring &getName() const {return Name;}
protected:
  wstring Name;
  EBaseType listType;
  EBaseType listSubType;
  SortOrder sortOrder;
     
  bool calcResults;
  bool calcCourseClassResults;
  bool calcTotalResults;
  bool rogainingResults;

  oListParam lp;

  list<oPrintPost> Head;
  list<oPrintPost> subHead;
  list<oPrintPost> listPost;
  vector<char> listPostFilter;
  vector<char> listPostSubFilter;
  list<oPrintPost> subListPost;
  bool fixedType;
  bool needPunches;
  string resultModule;
  set<string> additionalModules;

  void setupLinks(const list<oPrintPost> &lst) const;
  void setupLinks() const;

  list<oListInfo> next;
public:
  ResultType getResultType() const;

  bool supportClasses;
  bool supportLegs;
  bool supportParameter;
  // True if large (and non-large) is supported
  bool supportLarge;
  // True if a large-size list only
  bool largeSize;

  bool supportSplitAnalysis;
  bool supportInterResults;
  bool supportPageBreak;
  bool supportClassLimit;
  bool supportCustomTitle;

  // True if supports timing from control
  bool supportTo;
  // True if supports timing to control
  bool supportFrom;
  // Result type 
  ResultType resType;


  bool needPunchCheck() const {return needPunches;}
  void setCallback(GUICALLBACK cb);
  int getLegNumberCoded() const {return lp.getLegNumberCoded();}


  EStdListType getListCode() const {return lp.listCode;}
  oPrintPost &addHead(const oPrintPost &pp) {
    Head.push_back(pp);
    return Head.back();
  }
  oPrintPost &addSubHead(const oPrintPost &pp) {
    subHead.push_back(pp);
    return subHead.back();
  }
  oPrintPost &addListPost(const oPrintPost &pp) {
    listPost.push_back(pp);
    return listPost.back();
  }
  oPrintPost &addSubListPost(const oPrintPost &pp) {
    subListPost.push_back(pp);
    return subListPost.back();
  }
  inline bool filter(EFilterList i) const {return listPostFilter[i]!=0;}
  inline bool subFilter(ESubFilterList i) const {return listPostSubFilter[i]!=0;}

  void setFilter(EFilterList i) {listPostFilter[i]=1;}
  void setSubFilter(ESubFilterList i) {listPostSubFilter[i]=1;}

  void setResultModule(const string &rm) {resultModule = rm;}
  void additionalResultModule(const string &rm) {additionalModules.insert(rm);}
  const string &getResultModule() const {return resultModule;}
  oListInfo(void);
  ~oListInfo(void);

  friend class oEvent;
  friend class MetaList;
  friend class MetaListContainer;

  int getMaxCharWidth(const oEvent *oe,
                      const gdioutput &gdi,
                      const set<int> &clsSel,
                      const vector< pair<EPostType, wstring> > &typeFormats,
                      gdiFonts font,
                      const wchar_t *fontFace = 0,
                      bool large = false, 
                      int minSize = 0);


  int getMaxCharWidth(const oEvent *oe, 
                      const set<int> &clsSel,
                      EPostType type, 
                      wstring formats,
                      gdiFonts font,
                      const wchar_t *fontFace = 0,
                      bool large = false, 
                      int minSize = 0) {
    vector< pair<EPostType, wstring> > typeFormats(1, make_pair(type, formats));
    return getMaxCharWidth(oe, oe->gdiBase(), clsSel, typeFormats, font, fontFace, largeSize, minSize);
  }


  const oListParam &getParam() const {return lp;}
  oListParam &getParam() {return lp;}

  // Returns true if the list needs to be regenerated due to competition changes
  bool needRegenerate(const oEvent &oe) const;

};
