unit datlocalcorrect;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, Buttons, ComCtrls
  , upascaltz //Required for timezone corrections
  ;

type

  { Tdatlocalcorrectform }

  Tdatlocalcorrectform = class(TForm)
    CorrectButton: TBitBtn;
    FileSelectButton: TButton;
    Label11: TLabel;
    Label6: TLabel;
    SettingsGroupBox: TGroupBox;
    InGroupBox: TGroupBox;
    InputFile: TLabeledEdit;
    OpenDialog1: TOpenDialog;
    OutGroupBox: TGroupBox;
    OutputFile: TLabeledEdit;
    StatusBar1: TStatusBar;
    TZLocationBox: TComboBox;
    TZRegionBox: TComboBox;
    procedure CorrectButtonClick(Sender: TObject);
    procedure FileSelectButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure TZLocationBoxChange(Sender: TObject);
    procedure TZRegionBoxChange(Sender: TObject);
  private
    procedure ReadINI();
    procedure FillTimezones();
  public

  end;

var
  datlocalcorrectform: Tdatlocalcorrectform;
  InFileName: String;
  Timediff: Int64;
  LocalRecINIsection: String;
  AZones: TStringList;
  ptz :TPascalTZ;
  subfix: ansistring; //Used for time zone conversions
  LocalRecTZRegion, LocalRecTZLocation:String; //Only used for local timeone correction
  TZChanging:Boolean=False;//Indicates that programmatic changes are taking place to the Time Zone

implementation

uses
  appsettings //Required to read application settings (like locations).
  , dateutils //Required to convert logged UTC string to TDateTime
  , strutils //Required for checking lines in conversion file.
  , LazFileUtils //required for ExtractFileNameOnly
  , dlheader //For Timezone conversions.
  ;

{ Tdattimecorrectform }

{ Populate form from INI file }
procedure Tdatlocalcorrectform.ReadINI();
var
  pieces: TStringList;

begin

   pieces := TStringList.Create;
   pieces.Delimiter := ',';
   pieces.StrictDelimiter := False; //Parse spaces also

   LocalRecINIsection:='LocalReconstruct:';

   { Pull Timezone information from INI file if it exists.}
   LocalRecTZRegion:=vConfigurations.ReadString(LocalRecINIsection,'Local region');
   TZRegionBox.Text:= LocalRecTZRegion;
   FillTimezones();

   { Read the previously recorded entries. }
   LocalRecTZLocation:= vConfigurations.ReadString(LocalRecINIsection,'Local time zone');
   TZLocationBox.Text:=LocalRecTZLocation;

   if Assigned(pieces) then FreeAndNil(pieces);

end;

//Select file for correction
procedure Tdatlocalcorrectform.FileSelectButtonClick(Sender: TObject);
begin
  { Clear input filename in preparation for new selected filename}
  InputFile.Text:='';

  { Clear status bar }
  StatusBar1.Panels.Items[0].Text:='';

  OpenDialog1.Filter:='data log files|*.dat|All files|*.*';
  OpenDialog1.InitialDir := appsettings.LogsDirectory;

  { Get Input filename from user }
  if (OpenDialog1.Execute) then  begin
     InFileName:=OpenDialog1.FileName;
     InputFile.Text:=InFileName;

     { Create output file name }
     OutputFile.Text:= ExtractFilePath(InFileName) +
                       LazFileUtils.ExtractFileNameOnly (InFileName) +
                       '_LocalCorr' +
                       ExtractFileExt(InFileName);


  end;
end;

procedure Tdatlocalcorrectform.FormCreate(Sender: TObject);
begin
  { Clear status bar }
  StatusBar1.Panels.Items[0].Text:='';

  { get previous timezone settings }
  { Initialize required variables. }
  AZones:=TStringList.Create;
  ptz := TPascalTZ.Create();

  ReadINI();
end;

procedure Tdatlocalcorrectform.FormDestroy(Sender: TObject);
begin
  if Assigned(AZones) then FreeAndNil(AZones);
  if Assigned(ptz) then FreeAndNil(ptz);
end;

procedure Tdatlocalcorrectform.TZLocationBoxChange(Sender: TObject);
begin
  if not TZChanging then begin
     TZChanging:=True;
     { Save the TZ selection. }
     LocalRecTZLocation:=TZLocationBox.Text;
     Application.ProcessMessages;
     vConfigurations.WriteString(LocalRecINIsection,'Local time zone',LocalRecTZLocation);
     TZChanging:=False;
  end;
end;

procedure Tdatlocalcorrectform.TZRegionBoxChange(Sender: TObject);
begin
  if not TZChanging then begin
    TZChanging:=True;

    { Get and save region }
    LocalRecTZRegion:=TZRegionBox.Text;
    Application.ProcessMessages; //Wait for GUI to put screen text into variable.
    vConfigurations.WriteString(LocalRecINIsection,'Local region',LocalRecTZRegion); //Save TZ Region

    { Fill up timezone location names }
    FillTimezones();

    { Clear out selected time zone location because time zone region was just changed. }
    LocalRecTZLocation:='';
    TZLocationBox.Text:=LocalRecTZLocation;
    vConfigurations.WriteString(LocalRecINIsection,'Local time zone',LocalRecTZLocation);

    TZChanging:=False;
  end;

end;

{ Correct file }
procedure Tdatlocalcorrectform.CorrectButtonClick(Sender: TObject);
var
    InFile,OutFile: TextFile;
    Str: String;
    pieces: TStringList;

    index: Integer;

    UTCRecord :TDateTime;
    LocalRecord :TDateTime;
    ComposeString: String;

    WriteAllowable: Boolean = True; //Allow output file to be written or not.

begin
     pieces := TStringList.Create;

     { Clear status bar }
     StatusBar1.Panels.Items[0].Text:='';


    //Start reading file.
    AssignFile(InFile, InFileName);
    AssignFile(OutFile, OutputFile.Text);
    if FileExists(OutputFile.Text) then begin
        if (MessageDlg('Overwrite existing file?','Do you want to overwrite the existing file?',mtConfirmation,[mbOK,mbCancel],0) = mrOK) then
            WriteAllowable:=True
        else
          WriteAllowable:=False;
      end;
    if WriteAllowable then begin
    {$I+}
    try
      Reset(InFile);

      Rewrite(OutFile); //Open file for writing

      StatusBar1.Panels.Items[0].Text:='Reading Input file';

      repeat
        // Read one line at a time from the file.
        Readln(InFile, Str);

        StatusBar1.Panels.Items[0].Text:='Processing : '+Str;

        //Ignore most comment lines which have # as first character.
        if (AnsiStartsStr('#',Str)) then begin
          { Touch up time zone location line }
          if AnsiStartsStr('# Local timezone:',Str) then
            WriteLn(OutFile,format('# Local timezone: %s',[LocalRecTZLocation]))
          else
           { Write untouched header line }
           WriteLn(OutFile,Str);
        end else begin
            //Separate the fields of the record.
            pieces.Delimiter := ';';
            pieces.DelimitedText := Str;

            //parse the fields, and convert as necessary.
            //Convert UTC string 'YYYY-MM-DDTHH:mm:ss.fff' into TDateTime
            UTCRecord:=ScanDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz',pieces.Strings[0]);
            LocalRecord:=ptz.GMTToLocalTime(UTCRecord,LocalRecTZLocation, subfix);

            { Correct UTC and Local times }
            ComposeString:=
                           FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz;',IncSecond(UTCRecord,-1*Timediff)) +
                           FormatDateTime('yyyy-mm-dd"T"hh:nn:ss.zzz',IncSecond(LocalRecord,-1*Timediff));

            { Compose remainderof string }
            for index:=2 to pieces.count-1 do begin
                           ComposeString:=ComposeString+';'+pieces.Strings[index];
            end;

            WriteLn(OutFile,ComposeString);

          end;
      until(EOF(InFile)); // EOF(End Of File) The the program will keep reading new lines until there is none.
      CloseFile(InFile);
      StatusBar1.Panels.Items[0].Text:='Finished';

    except
      on E: EInOutError do
      begin
       MessageDlg('Error', 'File handling error occurred. Details: '+E.ClassName+'/'+E.Message, mtError, [mbOK],0);
      end;
    end;
    Flush(OutFile);
    CloseFile(OutFile);

    end;//End of WriteAllowable check.

end;

//Fill timezone location dropdown entries
procedure Tdatlocalcorrectform.FillTimezones();
begin
  if (FileExists(appsettings.TZDirectory+LocalRecTZRegion) and (length(LocalRecTZRegion)>0))then begin
      ptz.Destroy;
      ptz := TPascalTZ.Create();
      try
        ptz.ParseDatabaseFromFile(appsettings.TZDirectory+LocalRecTZRegion);
      except
        ShowMessage(Format('Failed getting zones from %s',[LocalRecTZRegion]));
      end;
      ptz.GetTimeZoneNames(AZones,true);  //only geo name = true, does not show short names
      TZLocationBox.Items.Clear;
      TZLocationBox.Items.AddStrings(AZones);
    end;
end;

initialization
  {$I datlocalcorrect.lrs}

end.

