unit MainUnit;

interface

uses
  System.SysUtils, System.JSON, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, HttpApp,DateUtils,
  FMX.StdCtrls, System.Actions, FMX.ActnList, FMX.Menus, FMX.Layouts, XSuperJson, XSuperObject,
  FMX.Gestures, FMX.Controls.Presentation, FMX.Edit, FMX.ListBox,
  FMX.DateTimeCtrls, System.IOUtils, FMX.WebBrowser, FMX.Calendar, FMX.ListView.Types,
  FMX.ListView, FMX.Objects, FMX.TabControl, EventTabUnit, DayUnit, FMX.Effects,
  FMX.Filter.Effects, IdBaseComponent, IdCoder, IdCoder3to4, IdCoderMIME, IdGlobal,
  IPPeerClient, REST.Client, Data.Bind.Components, Data.Bind.ObjectScope,
  REST.Authenticator.OAuth,REST.Utils,
  REST.Types,
  REST.Response.Adapter,
  REST.Authenticator.Simple,
  REST.Authenticator.Basic;

type
  TSCalendar = class(TForm)
    MainPanel: TPanel;
    MenuPanel: TPanel;
    StyleBook1: TStyleBook;
    SettingsPanel: TPanel;
    CalendarBox: TComboBox;
    OkButton: TButton;
    SettingsButton: TSpeedButton;
    ChooseCalendarLabel: TLabel;
    MainTabControl: TTabControl;
    DateLabel: TLabel;
    WeekTab: TTabItem;
    WeekPeriodLabel: TLabel;
    DaysScrollBox: TFramedVertScrollBox;
    SettingsTab: TTabItem;
    ButtonPanel: TPanel;
    ProfileBox: TComboBox;
    GradeBox: TComboBox;
    UpdateButton: TButton;
    AniIndicator1: TAniIndicator;
    TopPanel: TPanel;
    HomeTab: TTabItem;
    NowPanel: TPanel;
    NowImageControl: TImageControl;
    NowLabel: TLabel;
    NowTimePanel: TPanel;
    EndsAfterLabel: TLabel;
    EATimeLabel: TLabel;
    PrevPanel: TPanel;
    PrevLabel: TLabel;
    EBTimeLabel: TLabel;
    NextPanel: TPanel;
    NextLabel: TLabel;
    SATimeLabel: TLabel;
    HueAdjustEffect1: THueAdjustEffect;
    HueAdjustEffect2: THueAdjustEffect;
    TeacherPanel: TPanel;
    TeacherLabel: TLabel;
    TeacherNameLabel: TLabel;
    RoomPanel: TPanel;
    RoomLabel: TLabel;
    RoomNumberLabel: TLabel;
    ViewBox: TComboBox;
    WeekTimer: TTimer;
    HomeTimer: TTimer;
    GoogleOAuth2: TOAuth2Authenticator;
    RESTClient1: TRESTClient;
    RESTRequest1: TRESTRequest;
    RESTResponse1: TRESTResponse;
    procedure FormCreate(Sender: TObject);
    procedure OkButtonClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SettingsButtonClick(Sender: TObject);
    procedure CalendarBoxChange(Sender: TObject);
    procedure UpdateButtonClick(Sender: TObject);
    procedure ViewBoxChange(Sender: TObject);
    procedure HomeTimerTimer(Sender: TObject);
    procedure WeekTimerTimer(Sender: TObject);
  private
    { Private declarations }
    procedure UpdateCalendarMenu;
    procedure UpdateCalendarEvents(AStartDay, AEndDay: TDate; ACheck: Boolean);
    procedure UpdateWeek;
    function GetWeekDay(AWeekDay: Integer): ISuperArray;
    function GetNowEventIndex(AWeekDay: ISuperArray): Integer;
    function GetFTNextEventIndex(AWeekDay: ISuperArray): Integer;
    function GetFTPrevEventIndex(AWeekDay: ISuperArray): Integer;
    function ExtractGradeFromName(AName: String): String;
    function ClearDescFromTag(ADescription: String): String;
    function GetValueFromDescription(ADescription,AName: String): String;
    function ParseValues(AEventsArray: ISuperArray; AName: String): TStrings;
    procedure LoadSubjectImage(SubjectName: String);
    procedure OpenSettings;
    procedure SaveSettings;
    procedure InitSettings;
    procedure InitFiles;
    procedure CashEvents;
    function DeCashEvents: Boolean;
    procedure UpdateWeekView;
    procedure UpdateHomeView;
    procedure UpdateNowPanel(ANowEventIndex: Integer; AThisDay: ISuperArray);
    procedure UpdateNextPanel(AThisDay: ISuperArray);
    procedure UpdatePrevPanel(AThisDay: ISuperArray);
    procedure ResetRESTComponentsToDefault;
    procedure RefreshToken;
    function GetEvents(AStartDay, AEndDay: TDate): String;
  public
    { Public declarations }
  end;
    function BuildRequest(AStartDay, AEndDay: TDate): String;
    function ReplaceAllChars(ASource: String; AChar, ChChar: Char): String;
    function TimeLeftStr(ADateTime: TDateTime): String;
    procedure InitDayNames;
    procedure InitSubjectNames;
var
  SCalendar: TSCalendar;
  CalendarList, SettingsList: TStringList;
  EventsArray: ISuperArray;
  FormatSettings: TFormatSettings;
  SettingsFile, CashFile, CalendarFile, ProfileFile, GradeFile, CalendarListFile: String;
  DayFrames: array [0..6] of TDayFrame;
  StPeriodDate: TDate;
  DayNames: array [1..7] of String;
  SubjectNames: array [1..21] of String;
  Cashed: Boolean;

implementation

{$R *.fmx}
{$R *.NmXhdpiPh.fmx ANDROID}

procedure InitSubjectNames;
begin
  SubjectNames[1] := 'Язык English Russian';
  SubjectNames[2] := 'Литература Literature';
  SubjectNames[3] := 'Алгебра Algebra';
  SubjectNames[4] := 'Математика Mathematics';
  SubjectNames[5] := 'Геометрия Geometry';
  SubjectNames[6] := 'ИВТ Computer Science';
  SubjectNames[7] := 'Физкультура PE';
  SubjectNames[8] := 'Естествознание Natural Sciences';
  SubjectNames[9] := 'Физика Physics';
  SubjectNames[10] := 'Биология Biology';
  SubjectNames[11] := 'География Geography';
  SubjectNames[12] := 'Химия Chemistry';
  SubjectNames[13] := 'Право Law';
  SubjectNames[14] := 'Обществознание Social Studies';
  SubjectNames[15] := 'ИЗО Art';
  SubjectNames[16] := 'Черчение Drawing';
  SubjectNames[17] := 'Музыка Music';
  SubjectNames[18] := 'Free time!';
  SubjectNames[19] := 'История History';
end;

procedure InitDayNames;
begin
  DayNames[1] := 'Monday';
  DayNames[2] := 'Tuesday';
  DayNames[3] := 'Wednesday';
  DayNames[4] := 'Thursday';
  DayNames[5] := 'Friday';
  DayNames[6] := 'Saturday';
  DayNames[7] := 'Sunday';
end;

function ReplaceAllChars(ASource: String; AChar, ChChar: Char): String;
var
  ReplaceStr: String;
begin
  ReplaceStr := ASource;
  while ReplaceStr.Contains(AChar) do
    ReplaceStr := ReplaceStr.Replace(AChar, ChChar);
  Result := ReplaceStr;
end;

procedure TSCalendar.CalendarBoxChange(Sender: TObject);
begin
  if CalendarBox.Selected.Text.Contains('SC') then
  begin
    AniIndicator1.Visible := True;
    Application.ProcessMessages;
    SCalendar.UpdateActions;
    UpdateCalendarEvents(today,today+6,false);
    Application.ProcessMessages;
    GradeBox.Items.Free;
    GradeBox.Items := ParseValues(EventsArray,'grade');
    if GradeBox.Count > 0 then
      GradeBox.ItemIndex := 0;
    GradeBox.Enabled := True;
    Application.ProcessMessages;
    ProfileBox.Items.Free;
    ProfileBox.Items := ParseValues(EventsArray,'major');
    if ProfileBox.Count > 0 then
      ProfileBox.ItemIndex := 0;
    ProfileBox.Enabled := True;
    Application.ProcessMessages;
    AniIndicator1.Visible := False;
    SCalendar.UpdateActions;
  end
  else
  begin
    ProfileBox.Enabled := False;
    GradeBox.Enabled := False;
  end;
end;

procedure TSCalendar.CashEvents;
begin
  if EventsArray <> nil then
    EventsArray.SaveTo(CashFile);
end;

procedure TSCalendar.UpdateWeek;
var
  d: TDate;
begin
  d := EncodeDate(YearOf(Today), 9, 1);
  UpdateCalendarEvents(d,d+6,True);
end;

procedure TSCalendar.ViewBoxChange(Sender: TObject);
begin
  case ViewBox.ItemIndex of
    0: begin
      //UpdateHomeView;
      WeekTimer.Enabled := False;
      HomeTimer.Enabled := True;
    end;
    1: begin
      HomeTimer.Enabled := False;
      WeekTimer.Enabled := True;
      UpdateWeekView;
    end;
  end;
  MainTabControl.TabIndex := ViewBox.ItemIndex;
end;

procedure TSCalendar.WeekTimerTimer(Sender: TObject);
begin
  UpdateWeekView;
end;

function TSCalendar.ClearDescFromTag(ADescription: String): String;
var
  stpos: Integer;
begin
  if ADescription.Contains('#lesson') then
  begin
    stpos := pos('#lesson',ADescription);
    ADescription := ADescription.Remove(stpos-1,'#lesson'.Length);
  end;
  Result := ADescription;
end;

function TSCalendar.DeCashEvents: Boolean;
var
  ss: TStringStream;
begin
  if FileExists(CashFile) then
  begin
    ss := TStringStream.Create;
    ss.LoadFromFile(CashFile);
    EventsArray := SA(ss.DataString);
    ss.Free;
    Result := True;
  end
  else
    Result := False;
end;

function TSCalendar.ExtractGradeFromName(AName: String): String;
begin
  Result := AName.Substring(AName.IndexOf('/')+1)
end;

procedure TSCalendar.FormCreate(Sender: TObject);
begin
  InitFiles;
  InitDayNames;
  InitSubjectNames;
  CalendarList := TStringList.Create;
  FormatSettings.ShortDateFormat := 'yyyy-MM-dd';
  FormatSettings.DateSeparator := '-';
  FormatSettings.LongTimeFormat := 'HH:mm:ss';
  FormatSettings.TimeSeparator := ':';
  StPeriodDate := today;
  DateLabel.Text := DateToStr(StPeriodDate);
  try
    InitSettings;
  except
    MessageDlg('Internet connection lost.'
      + ' Please check your connection and open app again.',
       System.UITypes.TMsgDlgType.mtError,
    [
      System.UITypes.TMsgDlgBtn.mbYes
    ], 0,
    procedure(const AResult: TModalResult)
    begin
      case AResult of
        mrYes:
          Application.Terminate;
      end;
    end);
  end;
end;

procedure TSCalendar.FormDestroy(Sender: TObject);
begin
  CalendarList.Free;
  SettingsList.Free;
end;

function TSCalendar.GetEvents(AStartDay, AEndDay: TDate): String;
begin
  ResetRESTComponentsToDefault;

  RESTClient1.BaseURL := 'https://www.googleapis.com/calendar/v3/';
  RESTClient1.Authenticator := GoogleOAuth2;

  RESTRequest1.Resource := 'calendars/'+
      HttpEncode(CalendarList.Values[SCalendar.CalendarBox.Selected.Text])
      +'/events?singleEvents=true&orderBy=startTime'{+'?singleEvents=true&orderBy=startTime';
  RESTRequest1.Resource:= RESTRequest1.Resource + '&timeMax=' +
      HttpEncode(FormatDateTime('yyyy-MM-dd',AEndDay)
      +'T23:59:59+00:00')+'&timeMin='+
      HttpEncode(FormatDateTime('yyyy-MM-dd',AStartDay) + 'T00:00:00+00:00')};

  RESTRequest1.Execute;
  Result := RESTRequest1.Response.JSONText;
end;

function TSCalendar.GetFTNextEventIndex(AWeekDay: ISuperArray): Integer;
var
  i, ind: Integer;
  start_time, s_start_time: Double;
  EventObject, StartObject: ISuperObject;
begin
  ind := -1;
  s_start_time := -1;
  for i := 0 to AWeekDay.Length - 1 do
  begin
    EventObject := AWeekDay.O[i];
    StartObject := EventObject.O['start'];
    start_time := TimeOf(StrToDateTime(StartObject.S['dateTime'],FormatSettings));
    if (start_time > Frac(now)) and
      ((start_time < s_start_time) or (s_start_time = -1)) then
    begin
      ind := i;
      s_start_time := start_time;
    end;
  end;
  Result := ind;
end;

function TSCalendar.GetFTPrevEventIndex(AWeekDay: ISuperArray): Integer;
var
  i, ind: Integer;
  end_time, s_end_time: Double;
  EventObject, EndObject: ISuperObject;
  Summary: String;
begin
  ind := -1;
  s_end_time := -1;
  for i := 0 to AWeekDay.Length - 1 do
  begin
    EventObject := AWeekDay.O[i];
    Summary := EventObject.S['summary'];
    EndObject := EventObject.O['end'];
    end_time := TimeOf(StrToDateTime(EndObject.S['dateTime'],FormatSettings));
    if (end_time < Frac(now)) and
      ((end_time > s_end_time) or (s_end_time = -1)) then
    begin
      ind := i;
      s_end_time := end_time;
    end;
  end;
  Result := ind;
end;

function TSCalendar.GetNowEventIndex(AWeekDay: ISuperArray): Integer;
var
  i: Integer;
  start_time, end_time: Double;
  EventObject, StartObject, EndObject: ISuperObject;
begin
  Result := -1;
  for i := 0 to AWeekDay.Length - 1 do
  begin
    EventObject := AWeekDay.O[i];
    StartObject := EventObject.O['start'];
    EndObject := EventObject.O['end'];
    start_time := TimeOf(StrToDateTime(StartObject.S['dateTime'],FormatSettings));
    end_time := TimeOf(StrToDateTime(EndObject.S['dateTime'],FormatSettings));
    if (start_time < Frac(now)) and (end_time > Frac(now)) then
    begin
      Result := i;
      break;
    end;
  end;
end;

function TSCalendar.GetValueFromDescription(ADescription,
  AName: String): String;
var
  X: ISuperObject;
begin
  try
    X := SO(ReplaceAllChars(ADescription,';',','));
    Result := X.S[AName];
  except
    Result := '';
  end;
end;

function TSCalendar.GetWeekDay(AWeekDay: Integer): ISuperArray;
var
  i, day: Integer;
  EventObject, StartObject: ISuperObject;
  WeekDay: ISuperArray;
begin
  WeekDay := SA(EventsArray.AsJSON);
  i := 0;
  while i < WeekDay.Length do
  begin
    try
      EventObject := WeekDay.O[i];
      StartObject := EventObject.O['start'];
      day := Trunc(StrToDateTime(StartObject.S['dateTime'],FormatSettings));
      if DayOfTheWeek(day) <> AWeekDay then
        WeekDay.Delete(i)
      else
        i := i + 1;
    except
    end;
  end;
  Result := WeekDay;
end;

procedure TSCalendar.HomeTimerTimer(Sender: TObject);
begin
  UpdateHomeView;
end;

procedure TSCalendar.InitFiles;
begin
  SettingsFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'sc.ini');
  CashFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'events.json');
  CalendarFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'calendar.strings');
  ProfileFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'profile.strings');
  GradeFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'grade.strings');
  CalendarListFile := System.IOUtils.TPath.Combine(
  System.IOUtils.tpath.getdocumentspath,'calendars.list');
end;

procedure TSCalendar.InitSettings;
begin
  SettingsList := TStringList.Create;
  if FileExists(SettingsFile) then
  begin
    try
      SettingsList.LoadFromFile(SettingsFile);
      CalendarList.LoadFromFile(CalendarListFile);
      CalendarBox.Items.LoadFromFile(CalendarFile);
      CalendarBox.ItemIndex := StrToInt(SettingsList.Values['CalendarIndex']);
      if CalendarBox.Selected.Text.Contains('SC') then
      begin
        ProfileBox.Items.LoadFromFile(ProfileFile);
        ProfileBox.ItemIndex := StrToInt(SettingsList.Values['MajorIndex']);
        GradeBox.Items.LoadFromFile(GradeFile);
        GradeBox.ItemIndex := StrToInt(SettingsList.Values['GradeIndex']);
      end;
      try
        UpdateWeek;
        CashEvents;
      except
        if not DeCashEvents then
        begin
          ShowMessage('DeCash failed');
          raise;
        end;
      end;
      ViewBox.ItemIndex := 0;
      ViewBoxChange(Self);
    except
      OpenSettings;
    end;
  end
  else
  begin
    OpenSettings;
  end;
end;

procedure TSCalendar.LoadSubjectImage(SubjectName: String);
var
  FoundIndex, i: Integer;
  SubjectImageStream: TResourceStream;
  SubjectResName: String;
begin
  FoundIndex := 0;
  for i := 1 to 21 do
  begin
    if (SubjectNames[i].Contains(SubjectName)
    or SubjectName.Contains(SubjectNames[i])) then
      FoundIndex := i;
  end;
  SubjectResName := 'other';
  case FoundIndex of
    1: SubjectResName := 'lang';
    2: SubjectResName := 'literature';
    3: SubjectResName := 'algebra';
    4: SubjectResName := 'mathematics';
    5: SubjectResName := 'geometry';
    6: SubjectResName := 'it';
    7: SubjectResName := 'pe';
    8: SubjectResName := 'w_u';
    9: SubjectResName := 'physics';
    10: SubjectResName := 'biology';
    11: SubjectResName := 'geography';
    12: SubjectResName := 'chemistry';
    13: SubjectResName := 'law';
    14: SubjectResName := 'sociology';
    15: SubjectResName := 'atrs';
    16: SubjectResName := 'lines';
    17: SubjectResName := 'music';
    18: SubjectResName := 'engineering';
    19: SubjectResName := 'history';
  end;
  SubjectImageStream := TResourceStream.Create(HInstance, SubjectResName, RT_RCDATA);
  try
    NowImageControl.Bitmap.LoadFromStream(SubjectImageStream);
  finally
    SubjectImageStream.Free;
  end;
end;

procedure TSCalendar.UpdateWeekView;
var
  i, i1, ind, StartY: Integer;
  DayDate: TDate;
  EventObject, StartObject: ISuperObject;
  ThisDay: ISuperArray;
  desc: String;
begin
  StartY := 0;
  for i := 0 to 6 do
  begin
    DayDate := today + i;
    if DayFrames[i] = nil then
    begin
      DayFrames[i] := TDayFrame.Create(DaysScrollBox);
      DayFrames[i].Name := 'DayFrame'+IntToStr(i);
      DayFrames[i].EventsBox.StyleLookup := 'listboxstyle';
      DayFrames[i].Parent := DaysScrollBox;
      DayFrames[i].Position.Y := StartY;
      DayFrames[i].Position.X := 0;
      DayFrames[i].Width := DaysScrollBox.Width;
    end;
    DayFrames[i].DateText.Text := DayNames[DayOfTheWeek(DayDate)]
         + ' ' + DateToStr(DayDate);
    DayFrames[i].EventsBox.Clear;
    ThisDay := GetWeekDay(DayOfTheWeek(DayDate));
    for i1 := 0 to ThisDay.Length-1 do
    begin
      try
        EventObject := ThisDay.O[i1];
        StartObject := EventObject.O['start'];
        ind := DayFrames[i].EventsBox.Items.Add(EventObject.S['summary']);
        desc := EventObject.S['description'];
        if desc.Contains('#lesson') then
          DayFrames[i].EventsBox.Items[ind] := IntToStr(ind+1) + '. ' +
            DayFrames[i].EventsBox.Items[ind]+'  '+
            GetValueFromDescription(ClearDescFromTag(desc),'room')
      except
      end;
    end;
    DayFrames[i].Height := 103+DayFrames[i].EventsBox.Count*DayFrames[i].EventsBox.ItemHeight;
    StartY := StartY + Trunc(DayFrames[i].Height);
    SCalendar.UpdateActions;
    Application.ProcessMessages;
  end;
  WeekPeriodLabel.Text := DateToStr(today)+' - '+DateToStr(today+6);
  SCalendar.UpdateActions;
end;


procedure TSCalendar.SaveSettings;
var
  ADay: TDate;
begin
  if SettingsList = nil then
    SettingsList := TStringList.Create;
  SettingsList.Clear;
  SettingsList.Add('CalendarIndex='+IntToStr(CalendarBox.ItemIndex));
  SettingsList.Add('MajorIndex='+IntToStr(ProfileBox.ItemIndex));
  SettingsList.Add('GradeIndex='+IntToStr(GradeBox.ItemIndex));
  SettingsList.SaveToFile(SettingsFile);
  CalendarBox.Items.SaveToFile(CalendarFile);
  ProfileBox.Items.SaveToFile(ProfileFile);
  GradeBox.Items.SaveToFile(GradeFile);
  CalendarList.SaveToFile(CalendarListFile);
  try
    UpdateWeek;
    CashEvents;
    ViewBox.Visible := True;
    HomeTimer.Enabled := True;
    MainTabControl.ActiveTab := MainTabControl.Tabs[0];
  except
    if not DeCashEvents then
      ShowMessage('Lost Internet connection. Try again later.');
  end;
end;


function TimeLeftStr(ADateTime: TDateTime): String;
begin
  Result := IntToStr(SecondsBetween(now, ADateTime) div 3600)
    + ':' + IntToStr(SecondsBetween(now, ADateTime) mod 3600 div 60)
    + ':' + IntToStr(SecondsBetween(now, ADateTime) mod 3600 mod 60);
end;

procedure TSCalendar.UpdateCalendarMenu;
var
  Response: TStringStream;
  JSONArray: ISuperArray;
  JsonObject, CalendarInf: ISuperObject;
  i: Integer;
  Summary: String;
begin
  Response := TStringStream.Create;
  try
    RefreshToken;
    ResetRESTComponentsToDefault;
    RESTClient1.BaseURL := 'https://www.googleapis.com/calendar/v3/';
    RESTClient1.Authenticator := GoogleOAuth2;
    RESTRequest1.Resource := 'users/me/calendarList';
    RESTRequest1.Execute;
    //Response.SaveToFile('calendarlist.json');
    JsonObject := SO(RESTRequest1.Response.JSONText);
    JSONArray := JsonObject['items'].AsArray;
    CalendarList.Clear;
    CalendarBox.Clear;
    for I := 0 to JSONArray.Length-1 do
    begin
      CalendarInf := JSONArray.O[i];
      Summary := CalendarInf.S['summary'];
      if Summary.Contains('SC') then
      begin
        CalendarList.Add(CalendarInf.S['summary']+'='+CalendarInf.S['id']);
        CalendarBox.Items.Add(CalendarInf.S['summary']);
      end;
    end;
    finally
      try
        Response.Free;
      except
      end;
    end;
end;

procedure TSCalendar.UpdateHomeView;
var
  ind: Integer;
  ThisDay: ISuperArray;
begin
  ThisDay := GetWeekDay(DayOfTheWeek(Today));
  ind := GetNowEventIndex(ThisDay);
  UpdateNowPanel(ind, ThisDay);
  UpdateNextPanel(ThisDay);
  UpdatePrevPanel(ThisDay);
end;

procedure TSCalendar.UpdateNextPanel(AThisDay: ISuperArray);
var
  ind: Integer;
  EventObject, StartObject: ISuperObject;
  DateTime: TDateTime;
begin
  ind := GetFTNextEventIndex(AThisDay);
  if ind <> -1 then
  begin
    EventObject := AThisDay.O[ind];
    NextLabel.Text := EventObject.S['summary'] + ' starts after:';
    StartObject := EventObject.O['start'];
    DateTime := StrToDateTime(StartObject.S['dateTime'],FormatSettings);
    SATimeLabel.Text := TimeLeftStr(today + frac(DateTime));
    NextPanel.Visible := True;
  end
  else
    NextPanel.Visible := False;
end;

procedure TSCalendar.UpdateNowPanel(ANowEventIndex: Integer; AThisDay: ISuperArray);
var
  EventObject, EndObject: ISuperObject;
  desc: String;
  DateTime: TDateTime;
begin
  if ANowEventIndex = -1 then
  begin
    NowLabel.Text := 'Free time!';
    EATimeLabel.Text := '-';
    TeacherNameLabel.Text := '-';
    RoomNumberLabel.Text := '-';
    LoadSubjectImage('Free time!');
  end
  else
  begin
    EventObject := AThisDay.O[ANowEventIndex];
    NowLabel.Text := EventObject.S['summary'];
    LoadSubjectImage(EventObject.S['summary']);
    EndObject := EventObject.O['end'];
    DateTime := StrToDateTime(EndObject.S['dateTime'], FormatSettings);
    EATimeLabel.Text := TimeLeftStr(today + frac(DateTime));
    desc := EventObject.S['description'];
    if desc.Contains('#lesson') then
    begin
      desc := ClearDescFromTag(desc);
      RoomNumberLabel.Text := GetValueFromDescription(desc,'room');
      TeacherNameLabel.Text := GetValueFromDescription(desc,'teacher');
    end;
  end;
end;

procedure TSCalendar.UpdatePrevPanel(AThisDay: ISuperArray);
var
  ind: Integer;
  EventObject, EndObject: ISuperObject;
  DateTime: TDateTime;
begin
  ind := GetFTPrevEventIndex(AThisDay);
  if ind <> -1 then
  begin
    EventObject := AThisDay.O[ind];
    PrevLabel.Text := EventObject.S['summary'] + ' ended:';
    EndObject := EventObject.O['end'];
    DateTime := StrToDateTime(EndObject.S['dateTime'], FormatSettings);
    EBTimeLabel.Text := TimeLeftStr(today + frac(DateTime));
    PrevPanel.Visible := True;
  end
  else
    PrevPanel.Visible := False;
end;

function BuildRequest(AStartDay, AEndDay: TDate): String;
var
  RequestStr: String;
begin
  RequestStr := 'https://www.googleapis.com/calendar/v3/calendars/'+
      HttpEncode(CalendarList.Values[SCalendar.CalendarBox.Selected.Text])
      +'/events?singleEvents=true&orderBy=startTime';
  RequestStr := RequestStr + '&timeMax=' + HttpEncode(
      FormatDateTime('yyyy-MM-dd',AEndDay)
      +'T23:59:59+00:00')+'&timeMin='+
      HttpEncode(FormatDateTime('yyyy-MM-dd',AStartDay) + 'T00:00:00+00:00');
  Result := RequestStr;
end;

procedure TSCalendar.UpdateButtonClick(Sender: TObject);
begin
  try
    UpdateWeek;
    CashEvents;
  except
  end;
end;

procedure TSCalendar.UpdateCalendarEvents(AStartDay, AEndDay: TDate; ACheck: Boolean);
var
  Response: TStringStream;
  JSONArray: ISuperArray;
  JsonObject, EventInf, DescriptionObject: ISuperObject;
  stpos: Integer;
  i: Integer;
  RequestString, EventInfStr, Grade, Major: String;
begin
  Response := TStringStream.Create;
  try
    RefreshToken;
    JsonObject := SO(GetEvents(AStartDay, AEndDay));
    JSONArray := JsonObject['items'].AsArray;
    i := 0;
    while i < (JSONArray.Length-1) do
    begin
      EventInf := JSONArray.O[i];
      EventInfStr := EventInf.S['description'];
      if EventInfStr.Contains('#lesson') and CalendarBox.Selected.Text.Contains('SC') and ACheck then
      begin
        try
          stpos := pos('#lesson',EventInfStr);
          EventInfStr := EventInfStr.Remove(stpos-1,'#lesson'.Length);
          EventInfStr := ReplaceAllChars(EventInfStr,';',',');
          DescriptionObject := SO(EventInfStr);
          Grade := DescriptionObject.S['grade'];
          Major := DescriptionObject.S['major'];
          if ((not Grade.Contains(GradeBox.Items[GradeBox.ItemIndex])) or
          (not Major.Contains(ProfileBox.Items[ProfileBox.ItemIndex]))) then
            JSONArray.Delete(i)
          else
            i := i + 1;
        except
          JSONArray.Delete(i);
        end;
      end
      else
       i := i + 1;
    end;
    EventsArray := SA(JSONArray.AsJson);
  finally
    Response.Free;
  end;
end;

procedure TSCalendar.OkButtonClick(Sender: TObject);
begin
  if CalendarBox.ItemIndex > -1 then
  begin
    if CalendarBox.Selected.Text.Contains('SC') then
    begin
      if ((GradeBox.ItemIndex > -1) and (ProfileBox.ItemIndex > -1)) then
      begin
        SaveSettings;
        UpdateButton.Visible := True;
      end
      else
        ShowMessage('Major and grade are not chosen!');
    end
    else
    begin
      SaveSettings;
      UpdateButton.Visible := True;
    end;
  end;
end;

procedure TSCalendar.OpenSettings;
begin
  UpdateCalendarMenu;
  ViewBox.Visible := False;
  HomeTimer.Enabled := False;
  WeekTimer.Enabled := False;
  UpdateButton.Visible := False;
  MainTabControl.ActiveTab := MainTabControl.Tabs[2];
end;

procedure TSCalendar.SettingsButtonClick(Sender: TObject);
begin
  OpenSettings;
end;

function TSCalendar.ParseValues(AEventsArray: ISuperArray; AName: String): TStrings;
var
 i: Integer;
 s: String;
 X: ISuperObject;
 st: TStringList;
begin
  st := TStringList.Create;
  for I := 0 to AEventsArray.Length-1 do
  begin
    X := AEventsArray.O[i];
    s := X.S['description'];
    if s.Contains('#lesson') then
    begin
      s := ClearDescFromTag(s);
      s := ReplaceAllChars(s,';',',');
      X := SO(s);
      s := X.S[AName];
      if s.Contains('/') then
        s := s.Substring(0,s.IndexOf('/')-1);
      if (st.IndexOf(s) = -1) and ((s.Length>1) or (s = '-')) then
        st.Add(s);
    end;
  end;
  Result := st;
end;

procedure TSCalendar.RefreshToken;
var
  LToken: String;
begin
  ResetRESTComponentsToDefault;

  RESTClient1.BaseURL := 'https://accounts.google.com/';

  RESTRequest1.Method := TRESTRequestMethod.rmPOST;
  RESTRequest1.Resource := 'o/oauth2/token';
  RESTRequest1.Params.AddItem('refresh_token', GoogleOAuth2.RefreshToken, TRESTRequestParameterKind.pkGETorPOST);
  RESTRequest1.Params.AddItem('client_id', GoogleOAuth2.ClientId, TRESTRequestParameterKind.pkGETorPOST);
  RESTRequest1.Params.AddItem('client_secret', GoogleOAuth2.ClientSecret, TRESTRequestParameterKind.pkGETorPOST);
  RESTRequest1.Params.AddItem('grant_type', 'refresh_token', TRESTRequestParameterKind.pkGETorPOST);

  RESTRequest1.Execute;

  if RESTRequest1.Response.GetSimpleValue('access_token', LToken) then
    // edt_GoogleTasks_AccessToken.Text := LToken;
    GoogleOAuth2.AccessToken := LToken;

end;

procedure TSCalendar.ResetRESTComponentsToDefault;
begin
  RESTRequest1.ResetToDefaults;
  RESTClient1.ResetToDefaults;
  RESTResponse1.ResetToDefaults;
end;

end.
