/////////////////////////////////////////////////
// Property-Tag
typedef enum {
  ptUndef, ptBool, ptInt, ptFlt, ptStr, ptValV, ptSel, ptSet} TPpTagVal;

class TPpTag{
public:
  static TStr GetStr(const int& Tag);
  static TPpTagVal GetTag(const TStr& Str);
  static TPpTagVal LoadTxt(TILx& Lx){
    return GetTag(Lx.GetIdStr());}
  static void SaveTxt(TOLx& Lx, const TInt& Tag){SaveTxt(Lx, int(Tag));}
  static void SaveTxt(TOLx& Lx, const int& Tag){
    Lx.PutIdStr(GetStr(Tag));}
};

/////////////////////////////////////////////////
// Property-Value
class TPpVal{
private:
  TInt Tag;
  TBool Bool; TInt Int; TFlt Flt; TStr Str; TVec<TPpVal> ValV;
public:
  TPpVal(): Tag(ptUndef){}
  TPpVal(const TPpVal& PpVal): Tag(PpVal.Tag), Bool(PpVal.Bool),
    Int(PpVal.Int), Flt(PpVal.Flt), Str(PpVal.Str), ValV(PpVal.ValV){}
  TPpVal(const bool& b): Tag(ptBool), Bool(b){}
  TPpVal(const TBool& b): Tag(ptBool), Bool(b){}
  TPpVal(const int& _Int): Tag(ptInt), Int(_Int){}
  TPpVal(const TInt& _Int): Tag(ptInt), Int(_Int){}
  TPpVal(const double& _Flt): Tag(ptFlt), Flt(_Flt){}
  TPpVal(const TFlt& _Flt): Tag(ptFlt), Flt(_Flt){}
  TPpVal(const TStr& _Str): Tag(ptStr), Str(_Str){}
  TPpVal(const char* CStr): Tag(ptStr), Str(TStr(CStr)){}
  TPpVal(const TVec<TPpVal>& _ValV): Tag(ptValV), ValV(_ValV){}
  TPpVal(TSIn& SIn):
    Tag(SIn), Bool(SIn), Int(SIn), Flt(SIn), Str(SIn), ValV(SIn){}
  void Save(TSOut& SOut){
    Tag.Save(SOut); Bool.Save(SOut); Int.Save(SOut);
    Flt.Save(SOut); Str.Save(SOut); ValV.Save(SOut);}

  TPpVal& operator=(const TPpVal& PpVal){
    Tag=PpVal.Tag; Bool=PpVal.Bool; Int=PpVal.Int;
    Flt=PpVal.Flt; Str=PpVal.Str; ValV=PpVal.ValV; return *this;}
  bool operator==(const TPpVal& PpVal) const;
  bool operator<(const TPpVal& PpVal) const {
    if ((Tag==PpVal.Tag)&&(int(Tag)==ptInt)){return Int<PpVal.Int;}
    else if ((Tag==PpVal.Tag)&&(int(Tag)==ptFlt)){return Flt<PpVal.Flt;}
    else {Fail; return false;}}

  int GetTag() const {return (int)Tag;}
  TBool GetBool() const {IAssert(int(Tag)==ptBool); return Bool;}
  TInt GetInt() const {IAssert(int(Tag)==ptInt); return Int;}
  TFlt GetFlt() const {IAssert(int(Tag)==ptFlt); return Flt;}
  TStr GetStr() const {IAssert(int(Tag)==ptStr); return Str;}
  TVec<TPpVal> GetValV() const {IAssert(int(Tag)==ptValV); return ValV;}

  TStr GetValStr(const bool& DoAddTag=false) const;
  static TPpVal LoadTxt(TILx& Lx);
  void SaveTxt(TOLx& Lx) const;
};
typedef TVec<TPpVal> TPpValV;

/////////////////////////////////////////////////
// Property
class TPp;
typedef TPt<TPp> PPp;
typedef TVec<PPp> TPpV;

class TPp{
private:
  TCRef CRef;
private:
  static TStr PpNm;
private:
  TStr IdNm, UcIdNm, DescNm;
  TInt Tag, ValVTag;
  TPpVal Val, DfVal;
  TPpVal MnVal, MxVal;
  TPpValV CcValV;
  TPpV PpV;
  void IAssertSubPp() const {IAssert((int(Tag)==ptSel)||(int(Tag)==ptSet));}
  int GetPpN(const TStr& IdNm) const;
  void GetChA(const int& Lev, const bool& Brief, TChA& ChA) const;
public:
  TPp():
    IdNm(), UcIdNm(), DescNm(),
    Tag(ptUndef), ValVTag(),
    Val(), DfVal(), MnVal(), MxVal(),
    CcValV(), PpV(){}
  TPp(const TPp& Pp):
    IdNm(Pp.IdNm), UcIdNm(Pp.IdNm.GetUc()), DescNm(Pp.DescNm),
    Tag(Pp.Tag), ValVTag(Pp.ValVTag),
    Val(Pp.Val), DfVal(Pp.DfVal), MnVal(Pp.MnVal), MxVal(Pp.MxVal),
    CcValV(Pp.CcValV), PpV(Pp.PpV){}
  TPp(const TStr& _IdNm, const TStr& _DescNm,
   const TPpTagVal& _Tag=ptUndef, const TPpTagVal& _ValVTag=ptUndef):
    IdNm(_IdNm), UcIdNm(_IdNm.GetUc()), DescNm(_DescNm),
    Tag(_Tag), ValVTag(_ValVTag),
    Val(), DfVal(), MnVal(), MxVal(),
    CcValV(), PpV(){IAssert(int(ValVTag)!=ptValV);}
  TPp(TSIn& SIn):
    IdNm(SIn), UcIdNm(SIn), DescNm(SIn),
    Tag(SIn), ValVTag(SIn),
    Val(SIn), DfVal(SIn), MnVal(SIn), MxVal(SIn),
    CcValV(SIn), PpV(SIn){}
  static PPp Load(TSIn& SIn){return new TPp(SIn);}
  void Save(TSOut& SOut){
    IdNm.Save(SOut); UcIdNm.Save(SOut); DescNm.Save(SOut);
    Tag.Save(SOut); ValVTag.Save(SOut);
    Val.Save(SOut); DfVal.Save(SOut); MnVal.Save(SOut); MxVal.Save(SOut);
    CcValV.Save(SOut); PpV.Save(SOut);}

  TPp& operator=(const TPp& Pp){
    if (this!=&Pp){
      IdNm=Pp.IdNm; UcIdNm=Pp.UcIdNm; DescNm=Pp.DescNm;
      Tag=Pp.Tag; ValVTag=Pp.ValVTag;
      Val=Pp.Val; DfVal=Pp.DfVal; MnVal=Pp.MnVal; MxVal=Pp.MxVal;
      CcValV=Pp.CcValV; PpV=Pp.PpV;}
    return *this;}

  // basic operations
  TStr GetIdNm() const {return IdNm;}
  TStr GetUcIdNm() const {return UcIdNm;}
  TStr GetDescNm() const {return DescNm;}
  int GetTag() const {return Tag;}
  int GetValVTag() const {return ValVTag;}
  void PutVal(const TPpVal& _Val);
  TPpVal GetVal() const {if (Val==TPpVal()){return DfVal;} else {return Val;}}

  // property definition
  void PutDfVal(const TPpVal& _DfVal){PutVal(_DfVal); DfVal=Val;}
  TPpVal GetDfVal() const {return DfVal;}
  void PutMnMxVal(const TPpVal& _MnVal, const TPpVal& _MxVal){
    IAssert((int(Tag)==ptInt)||(int(Tag)==ptFlt));
    IAssert((Tag==_MnVal.GetTag())&&(Tag==_MxVal.GetTag()));
    MnVal=_MnVal; MxVal=_MxVal;}
  TPpVal GetMnVal() const {IAssert((int(Tag)==ptInt)||(int(Tag)==ptFlt)); return MnVal;}
  TPpVal GetMxVal() const {IAssert((int(Tag)==ptInt)||(int(Tag)==ptFlt)); return MxVal;}
  void AddCcVal(const TPpVal& _Val){
    IAssert((int(Tag)==ptStr)||(int(Tag)==_Val.GetTag()));
    IAssert(CcValV.SearchForw(_Val)==-1); CcValV.Add(_Val);}
  void PutCcValV(const TPpValV& CcValV){
    for (int CcValN=0; CcValN<CcValV.Len(); CcValN++){AddCcVal(CcValV[CcValN]);}}
  int GetCcVals() const {IAssert(int(Tag)==ptStr); return CcValV.Len();}
  TPpVal GetCcVal(const int& CcValN) const {
    IAssert(int(Tag)==ptStr); return CcValV[CcValN];}

  // property put/get
  bool IsPp(const TStr& IdNm) const {IAssertSubPp(); return GetPpN(IdNm)!=-1;}
  bool IsPp(const TStr& IdNm, PPp& Pp) const {
    IAssertSubPp(); int PpN=GetPpN(IdNm);
    if (PpN==-1){return false;} else {Pp=GetPp(PpN); return true;}}
  int GetPps() const {IAssertSubPp(); return PpV.Len();}
  int AddPp(const PPp& Pp){IAssertSubPp(); return PpV.Add(Pp);}
  void AddPpV(const TPpV& PpV){
    for (int PpN=0; PpN<PpV.Len(); PpN++){AddPp(PpV[PpN]);}}
  PPp GetPp(const int& PpN) const {IAssertSubPp(); return PpV[PpN];}
  PPp GetPp(const TStr& IdPath) const;
  PPp GetSelPp() const {
    IAssert(int(Tag)==ptSel); return GetPp(GetVal().GetStr());}

  // add&create property shortcuts
  int AddPpInt(const TStr& IdNm, const TStr& DescNm=TStr()){
    PPp Pp=PPp(new TPp(IdNm, DescNm, ptInt)); return AddPp(Pp);}
  int AddPpInt(const TStr& IdNm, const TStr& DescNm,
   const int& MnVal, const int& MxVal, const int& DfVal){
    PPp Pp=PPp(new TPp(IdNm, DescNm, ptInt));
    Pp->PutMnMxVal(MnVal, MxVal); Pp->PutDfVal(DfVal); return AddPp(Pp);}
  int AddPpFlt(const TStr& IdNm, const TStr& DescNm=TStr()){
    PPp Pp=PPp(new TPp(IdNm, DescNm, ptFlt)); return AddPp(Pp);}
  int AddPpFlt(const TStr& IdNm, const TStr& DescNm,
   const double& MnVal, const double& MxVal, const double& DfVal){
    PPp Pp=PPp(new TPp(IdNm, DescNm, ptFlt));
    Pp->PutMnMxVal(MnVal, MxVal); Pp->PutDfVal(DfVal); return AddPp(Pp);}
  int AddPpStr(const TStr& IdNm, const TStr& DescNm=TStr(), const TStr& DfVal=TStr()){
    PPp Pp=PPp(new TPp(IdNm, DescNm, ptStr));
    Pp->PutDfVal(DfVal); return AddPp(Pp);}
  static PPp GetSetPp(const TStr& IdNm, const TStr& DescNm=TStr()){
    return PPp(new TPp(IdNm, DescNm, ptSet));}

  // boolean property put/get shortcuts
  void PutValBool(const bool& Val){PutVal(TPpVal(Val));}
  TBool GetValBool() const {return GetVal().GetBool();}
  void PutValBool(const TStr& IdPath, const bool& Val){
    GetPp(IdPath)->PutVal(TPpVal(Val));}
  TBool GetValBool(const TStr& IdPath) const {
    return GetPp(IdPath)->GetVal().GetBool();}

  // integer property put/get shortcuts
  void PutValInt(const int& Val){PutVal(TPpVal(Val));}
  TInt GetValInt() const {return GetVal().GetInt();}
  void PutValInt(const TStr& IdPath, const int& Val){
    GetPp(IdPath)->PutVal(TPpVal(Val));}
  TInt GetValInt(const TStr& IdPath) const {
    return GetPp(IdPath)->GetVal().GetInt();}

  // float property put/get shortcuts
  void PutValFlt(const double& Val){PutVal(TPpVal(Val));}
  TFlt GetValFlt() const {return GetVal().GetFlt();}
  void PutValFlt(const TStr& IdPath, const double& Val){
    GetPp(IdPath)->PutVal(TPpVal(Val));}
  TFlt GetValFlt(const TStr& IdPath) const {
    return GetPp(IdPath)->GetVal().GetFlt();}

  // string property put/get shortcuts
  void PutValStr(const TStr& Val){PutVal(TPpVal(Val));}
  TStr GetValStr() const {return GetVal().GetStr();}
  void PutValStr(const TStr& IdPath, const TStr& Val){
    GetPp(IdPath)->PutVal(TPpVal(Val));}
  TStr GetValStr(const TStr& IdPath) const {
    return GetPp(IdPath)->GetVal().GetStr();}

  // value property vector put/get shortcuts
  void PutValValV(const TPpValV& Val){PutVal(TPpVal(Val));}
  TPpValV GetValValV() const {return GetVal().GetValV();}
  void PutValValV(const TStr& IdPath, const TPpValV& Val){
    GetPp(IdPath)->PutVal(TPpVal(Val));}
  TPpValV GetValValV(const TStr& IdPath) const {
    return GetPp(IdPath)->GetVal().GetValV();}

  // string
  TStr GetStr(const bool& Brief=true) const {
    TChA ChA; GetChA(0, Brief, ChA); return ChA;}

  // file
  static PPp LoadTxt(const PSIn& SIn){
    TILx Lx(SIn, TFSet()|iloCmtAlw|iloSigNum|oloCsSens); return LoadTxt(Lx);}
  void SaveTxt(const PSOut& SOut) const {
    TOLx Lx(SOut, TFSet()|oloCmtAlw|oloFrcEoln|oloSigNum|oloCsSens); SaveTxt(Lx);}
  static PPp LoadTxt(TILx& Lx);
  void SaveTxt(TOLx& Lx, const int& Lev=0) const;

  friend class TPt<TPp>;
};

inline PPp MkClone(const PPp& Pp){return new TPp(*Pp);}

