unit convertlogfileunit;

//Tool: .dat to Moon Sun .csv

{$mode objfpc}

interface

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.
  moon, //required for Moon calculations
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, ComCtrls;

type

  { Tconvertdialog }

  Tconvertdialog = class(TForm)
    CloseButton: TButton;
    OutputFilenameDisplay: TLabeledEdit;
    LatitudeDisplay: TLabeledEdit;
    LongitudeDisplay: TLabeledEdit;
    Memo1: TMemo;
    OpenFileDialog: TOpenDialog;
    SelectButton: TButton;
    StatusBar1: TStatusBar;
    procedure CloseButtonClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure SelectButtonClick(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
  end;

var
  convertdialog: Tconvertdialog;

implementation

uses
  Unit1
  , header_utils;

{ Tconvertdialog }

procedure Tconvertdialog.SelectButtonClick(Sender: TObject);
var
    File1,OutFile: TextFile;
    Str: String;
    pieces: TStringList;

    MoonElevation: extended = 0.0;
    MoonAzimuth: extended = 0.0;

    SunElevation: extended = 0.0;
    SunAzimuth: extended = 0.0;

    UTCRecord :TDateTime;
    ComposeString: String;
    OutFileString: String;

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

    LineNumber: Integer =0;
    EndTrimString: String =', Zenith, Azimuth, MoonPhaseDeg, MoonElevDeg, MoonIllum';
    ErrorString: String;
begin
     pieces := TStringList.Create;
     { Clear status bar }
     StatusBar1.Panels.Items[0].Text:='';

     OpenFileDialog.Filter:='data log files|*.dat|All files|*.*';
     OpenFileDialog.InitialDir := appsettings.LogsDirectory;
if OpenFileDialog.Execute then begin
    //Start reading file.
    StatusBar1.Panels.Items[0].Text:='Reading Input file';
    AssignFile(File1, OpenFileDialog.Filename);
    OutFileString:=ChangeFileExt(OpenFileDialog.Filename,'.csv');
    AssignFile(OutFile, OutFileString);
    OutputFilenameDisplay.Text:=OutFileString;
    //StatusBar1.Panels.te;
    if FileExists(OutFileString) 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(File1);

      Rewrite(OutFile); //Open file for writing

      StatusBar1.Panels.Items[0].Text:='Processing Input file, please wait ...'; Application.ProcessMessages;

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

        // Get location data from header.
        if AnsiStartsStr('# Position',Str) then begin
            //Prepare for parsing.
            pieces.Delimiter := ',';
            pieces.StrictDelimiter := True; //Do not parse spaces.
            //Remove comment from beginning of line.
            pieces.DelimitedText := AnsiRightStr(Str,length(Str) - RPos(':',Str));
            if (pieces.Count<2) then begin
                ErrorString:='No location data found in header.';
                StatusBar1.Panels.Items[0].Text:=ErrorString;
                MessageDlg('Error',ErrorString, mtError, [mbOK],0);
                Writeln(OutFile,'Error: No location data found in header.');
                LatitudeDisplay.Text:='nul';
                LongitudeDisplay.Text:='nul';
                break;
              end
            else
              begin
               MyLatitude:=StrToFloat(pieces.Strings[0]);
               if ((MyLatitude<-90.0) or (MyLatitude>90.0)) then begin
                   ErrorString:='Error: Latitude ' + FloatToStr(MyLatitude) +' out of range';
                   StatusBar1.Panels.Items[0].Text:=ErrorString;
                   StatusMessage(ErrorString);
                   break;
                 end;
               MyLongitude:=StrToFloat(pieces.Strings[1]);
               if ((MyLongitude<-180.0) or (MyLongitude>180.0)) then begin
                   ErrorString:='Error: Longitude '+FloatToStr(MyLongitude)+' out of range';
                   StatusBar1.Panels.Items[0].Text:=ErrorString;
                   StatusMessage('Error: Longitude '+FloatToStr(MyLongitude)+' out of range');
                   break;
                 end;
                LatitudeDisplay.Text:=Format('%.3f',[MyLatitude]);
                LongitudeDisplay.Text:=Format('%.3f',[MyLongitude]);
              end;
          end;

        Application.ProcessMessages;

        //Get and modify record header string
        //if AnsiStartsStr('# YYYY-MM-DDTHH:mm:ss.fff;',Str) then
        //  Writeln(OutFile,'UTC '+AnsiRightStr(Str,length(Str) - 2)+';MoonPhaseDeg;MoonElevDeg;MoonIllum%;SunElevDeg');
        Str:=Trim(Str);
        if AnsiStartsStr('# UTC Date & Time,',Str) then begin
            if AnsiEndsStr(EndTrimString,Str) then begin
                Str:=AnsiLeftStr(Str,Length(Str)-Length(EndTrimString));
            end;
          //Remove preceding comment marker
          Writeln(OutFile,AnsiRightStr(Str,length(Str) - 2)+';MoonPhaseDeg;MoonElevDeg;MoonIllum%;SunElevDeg');
        end;

        Application.ProcessMessages;

        //Ignore comment lines which have # as first character.
        if not AnsiStartsStr('#',Str) then
          begin
            //Separate the fields of the record.
            pieces.Delimiter := ';';
            pieces.DelimitedText := Str;

            begin

            //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]);

            //Calculate Moon position
            //Change sign for Moon calculations
            Moon_Position_Horizontal(
               StrToDateTime(DateTimeToStr(UTCRecord)),
               -1.0*MyLongitude,
               MyLatitude,
               MoonElevation,
               MoonAzimuth);

            Sun_Position_Horizontal(
               StrToDateTime(DateTimeToStr(UTCRecord)),
               -1.0*MyLongitude,
               MyLatitude,SunElevation,
               SunAzimuth);


            //Prepare string for output.
            ComposeString:= Str + ';'
                           //Moon Phase angle (0 to 180 degrees).
                           + Format('%.1f;',[moon_phase_angle(StrToDateTime(DateTimeToStr(UTCRecord)))])

                           //Moon elevation (positive = above horizon, negative = below horizon).
                           + Format('%.3f;',[MoonElevation])

                           //Moon illumination pecent.
                           + Format('%.1f;',[current_phase(StrToDateTime(DateTimeToStr(UTCRecord)))*100.0])

                           //Sun elevation (positive = above horizon, negative = below horizon).
                           + Format('%.3f;',[SunElevation])
            ;

            WriteLn(OutFile,ComposeString);
            end;//End of checking number of fields in record.

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

    except
      //on E: EInOutError do begin
      on E: Exception do begin
       MessageDlg('Error', 'File handling error occurred. Details: ' + sLineBreak + 'On Line number: '+ IntToStr(LineNumber) + sLineBreak +E.ClassName+'/'+E.Message, mtError, [mbOK],0);
       StatusBar1.Panels.Items[0].Text:='Error encountered.';
      end;
    end;
    Flush(OutFile);
    CloseFile(OutFile);
    StatusBar1.Panels.Items[0].Text:='Processing complete';
    end;//End of WriteAllowable check.

  end;

end;

procedure Tconvertdialog.CloseButtonClick(Sender: TObject);
begin
  Close;
end;

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

end;


initialization
  {$I convertlogfileunit.lrs}

end.

