
#Include "TextLib" as TextLib
#Include "MathLib" as MathLib
#Include "ColorLib" as ColorLib
#Include "AnimLib" as AnimLib

#Struct BestCp {
    Text nickname;
    Integer time;
    Text login;
}

Text FormatSec(Real sec) {
    if (sec > 10.) return TextLib::FormatReal(sec,3,False,False);
    return  0 ^ TextLib::FormatReal(sec,3,False,False);
}

Text TimeToText(Integer intime) {
    declare time = MathLib::Abs(intime);
    declare Integer cent = time % 1000;
    declare Integer sec2 = (time / 1000) % 60;
    declare Real sec = 1. * sec2 + cent * 0.001;
    declare Integer min = (time / 60000) % 60;
    declare Integer hour = time / 3600000;
    declare Text sign = "";
    if (intime < 0) sign = "-";
    if (hour > 0) return sign ^ hour ^ "'" ^ TextLib::FormatInteger(min,2) ^ ":" ^ FormatSec(sec);
    if (min == 0) return sign ^ "00:" ^ FormatSec(sec);
    if (min > 10)  return sign ^ min ^ ":" ^ FormatSec(sec);
    return sign ^ 0 ^ min ^ ":" ^ FormatSec(sec);

}

Void HideCp(Integer _Index) {
    (Page.GetFirstChild("CpFrame_"^ _Index) as CMlFrame).RelativeScale = 0.;
}


Void UpdateCp(Integer _Index, BestCp cpinfo, Boolean _Animate) {
    declare ElementCount for Page = 18;
    declare CMlQuad[] Animations for Page;

    if (_Index >= ElementCount) {
        return;
    }
    declare Frame <=> (Page.GetFirstChild("CpFrame_"^ (_Index)) as CMlFrame);

    declare Text Color = "fff";
    if (cpinfo.time == 99999999) {
        (Frame.Controls[1] as CMlLabel).Value = "" ^ (_Index+1);
        (Frame.Controls[3] as CMlLabel).Value = "";
        (Frame.Controls[5] as CMlLabel).Value = "";
        Frame.RelativeScale = 0.; 
    } else {
        declare color = "$ff0";
        declare Vec3 animColor for Frame.Controls[2];
        declare Real lightness for Frame.Controls[2];
        declare Integer start for Frame.Controls[2];

        animColor = ColorLib::RgbToHsv(ColorLib::HexToRgb("ffffff"));
        if (InputPlayer.User.Login == cpinfo.login) {
            color = "$0f3";
            animColor = ColorLib::RgbToHsv(ColorLib::HexToRgb("00ff33"));
        }

        (Frame.Controls[1] as CMlLabel).Value = "" ^ (_Index+1);
        (Frame.Controls[3] as CMlLabel).Value = cpinfo.nickname;     
        (Frame.Controls[5] as CMlLabel).Value = color ^ TimeToText(cpinfo.time);
       
        if (_Animate) {
            lightness = animColor.Z;
            start = Now;
            declare elem <=> (Frame.Controls[2] as CMlQuad);
            elem.BgColor = animColor;
            if (!Animations.exists(elem)) {
                Animations.add(elem);
            }
        }
    }
    
    if (Frame.RelativeScale == 0.) {
        Frame.RelativeScale = 1.;
    }
}

Void Reset() {
    declare ElementCount for Page = 18;
    declare BestCp[] MapBestCheckpoints for Page;
    declare CMlQuad[] Animations for Page;

    // clear
    Animations.clear();
    MapBestCheckpoints.clear();
    for (i, 0, MapCheckpointPos.count+1) {
        MapBestCheckpoints.add(BestCp{time = 99999999, nickname = ""});
    }
    
    // enable all checkpoints
    for (i, 0, (ElementCount-1)) {
       (Page.GetFirstChild("CpFrame_"^i) as CMlFrame).RelativeScale = 1.;
    }

    // hide checkpoints not needed
    for (i, (MapCheckpointPos.count), (ElementCount-1)) {
      HideCp(i);
    }

    // sync bestCps from scores
    foreach(_Score in Scores) {
        for(i, 0, MapCheckpointPos.count-1) {
            if (_Score.BestLap.Checkpoints.existskey(i)) {
                if (_Score.BestLap.Checkpoints[i] < MapBestCheckpoints[i].time) {
                    MapBestCheckpoints[i].time = _Score.BestLap.Checkpoints[i];
                    MapBestCheckpoints[i].nickname = _Score.User.Name;
                    MapBestCheckpoints[i].login = _Score.User.Login; 
                }               
            }
        }
    }
    
    for(i, 0, MapCheckpointPos.count-1) {
       UpdateCp(i, MapBestCheckpoints[i], False);
    }

}


main() {
    declare ElementCount for Page = 18;
    declare CMlQuad[] Animations for Page;
    declare BestCp[] MapBestCheckpoints for Page;
    declare CUIConfig::EUISequence LastUiStatus = CUIConfig::EUISequence::None;


    // -------------- enable distractionFreeMode -------------
    declare Boolean Prev_DistractionFreeMode = False;
    declare netwrite Boolean Net_DistractionFreeMode for UI;

    if (Net_DistractionFreeMode == True) {
      Page.GetClassChildren("distraction-hide", Page.MainFrame, True);
      foreach (Control in Page.GetClassChildren_Result) {
        Control.Hide();
      }
    }
    // ------------------------

    Reset();

    while (True) {
        yield;

    // check for scene changes, reset on start of playing, hide on podium 
        if (UI.UISequence != LastUiStatus) {
            LastUiStatus = UI.UISequence;
            if (UI.UISequence == CUIConfig::EUISequence::Playing) {
                Reset();
            }
            if (UI.UISequence == CUIConfig::EUISequence::Podium) {
                for (i, 0, (ElementCount-1)) {
                HideCp(i);
                }
            }
        }

        // -------------- enable distractionFreeMode -------------
        if (Prev_DistractionFreeMode != Net_DistractionFreeMode) {
            Prev_DistractionFreeMode = Net_DistractionFreeMode;

            if (Net_DistractionFreeMode == True) {
            Page.GetClassChildren("distraction-hide", Page.MainFrame, True);
            foreach (Control in Page.GetClassChildren_Result) {
                Control.Hide();
            }
            } else {
            Page.GetClassChildren("distraction-hide", Page.MainFrame, True);
            foreach (Control in Page.GetClassChildren_Result) {
                Control.Show();
            }
            }
        }
    // -----------------------

        foreach (RaceEvent in RaceEvents) {
            if (RaceEvent.Type == CTmRaceClientEvent::EType::WayPoint) {
                declare cp = RaceEvent.CheckpointInLap;
                if (RaceEvent.IsEndRace || RaceEvent.IsEndLap) continue;
                if (cp <= MapBestCheckpoints.count) {
                    if (RaceEvent.LapTime < MapBestCheckpoints[cp].time) {
                                MapBestCheckpoints[cp].time = RaceEvent.LapTime;
                                MapBestCheckpoints[cp].nickname = RaceEvent.Player.User.Name;
                                MapBestCheckpoints[cp].login = RaceEvent.Player.User.Login;
                                UpdateCp(RaceEvent.CheckpointInLap, MapBestCheckpoints[cp], True);
                    }
                }
            }
        }  
    
    foreach(Element in Animations) {
      declare Real lightness for Element = 0.;
      declare Integer start for Element = Now;
      declare Vec3 animColor for Element = <0., 0., 0.>;
    
      // check for black
      if (lightness >= 0. ) {
          lightness = AnimLib::EaseLinear(Now - start, 1., -1., 500);
          Element.BgColor = ColorLib::HsvToRgb(<animColor.X, animColor.Y, lightness>);
      } else {
          Animations.remove(Element);
      }

    }

    } // while

} // main
    

