unit dlheader;

{$mode objfpc}

interface

uses
  Classes, SysUtils, FileUtil, LResources, Forms, Controls, Graphics, Dialogs,
  StdCtrls, ExtCtrls, Spin, uPascalTZ, appsettings,
  LCLIntf,
  IniFiles
  //, Grids
  //, StrUtils
  ;
type

  { TDLHeaderForm }

  TDLHeaderForm = class(TForm)
    Button1: TButton;
    CloseButton: TButton;
    CoverOffsetEntry: TLabeledEdit;
    DataSupplierEntry: TLabeledEdit;
    DefinitionsLink: TLabel;
    InvalidLocationName: TLabel;
    Memo1: TMemo;
    TZRefLink: TLabel;
    EditPositionButton: TButton;
    FieldOfViewEntry: TLabeledEdit;
    FiltersPerChannelEntry: TComboBox;
    PDFDocButton: TButton;
    SelectedGroupBox: TGroupBox;
    InstrumentIDEntry: TLabeledEdit;
    InvalidInstrumentID: TLabel;
    Label10: TLabel;
    Label11: TLabel;
    Label6: TLabel;
    Label7: TLabel;
    Label8: TLabel;
    Label9: TLabel;
    LocationNameEntry: TLabeledEdit;
    MeasurementDirectionPerChannelEntry: TLabeledEdit;
    MovingStationaryDirectionCombo: TComboBox;
    MovingStationaryPositionCombo: TComboBox;
    NumberOfChannelsEntry: TSpinEdit;
    PositionEntry: TLabeledEdit;
    ScrollBox1: TScrollBox;
    SerialNumber: TLabeledEdit;
    TimeSynchEntry: TLabeledEdit;
    TZLocationBox: TComboBox;
    TZRegionBox: TComboBox;
    UserComment1: TLabeledEdit;
    UserComment2: TEdit;
    UserComment3: TEdit;
    UserComment4: TEdit;
    UserComment5: TEdit;
    procedure Button1Click(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure PDFDocButtonClick(Sender: TObject);
    procedure CloseButtonClick(Sender: TObject);
    procedure EditPositionButtonClick(Sender: TObject);
    procedure CoverOffsetEntryChange(Sender: TObject);
    procedure DefinitionsLinkClick(Sender: TObject);
    procedure DefinitionsLinkMouseEnter(Sender: TObject);
    procedure DefinitionsLinkMouseLeave(Sender: TObject);
    procedure FiltersPerChannelEntryChange(Sender: TObject);
    procedure FieldOfViewEntryChange(Sender: TObject);
    procedure MeasurementDirectionPerChannelEntryChange(Sender: TObject);
    procedure MovingStationaryDirectionComboChange(Sender: TObject);
    procedure MovingStationaryPositionComboChange(Sender: TObject);
    procedure DataSupplierEntryChange(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure InstrumentIDEntryChange(Sender: TObject);
    procedure NumberOfChannelsEntryChange(Sender: TObject);
    procedure TimeSynchEntryChange(Sender: TObject);
    procedure LocationNameEntryChange(Sender: TObject);
    procedure TZLocationBoxChange(Sender: TObject);
    procedure TZRefLinkClick(Sender: TObject);
    procedure TZRegionBoxChange(Sender: TObject);
    procedure UserComment1Change(Sender: TObject);
    procedure UserComment2Change(Sender: TObject);
    procedure UserComment3Change(Sender: TObject);
    procedure UserComment4Change(Sender: TObject);
    procedure UserComment5Change(Sender: TObject);
  private
    { private declarations }
  public
    { public declarations }
    procedure ReadINI();
    function CheckInstrumentID() : Boolean;
    function CheckLocationName() : Boolean;
  end;

const
  SpecificationsLink='https://darksky.org/app/uploads/bsk-pdf-manager/47_SKYGLOW_DEFINITIONS.PDF';

var
  SerialINIsection: String;
  DLHeaderForm: TDLHeaderForm;
  AZones: TStringList;
  ptz :TPascalTZ;
  TZInitialLoad:Boolean = True;
  LoadingValues:Boolean = False;

implementation

uses Unit1
  , worldmap
  , logcont
  , header_utils
  ;

{ TDLHeaderForm }

{ Save the TZ selection. }
procedure TDLHeaderForm.TZLocationBoxChange(Sender: TObject);
begin
 if not LoadingValues then begin
    vConfigurations.WriteString(SerialINIsection,'Local time zone',TZLocationBox.Text);
    SelectedTZLocation:=vConfigurations.ReadString(SerialINIsection,'Local time zone');
 end;
end;

procedure TDLHeaderForm.TZRefLinkClick(Sender: TObject);
begin
  {Link to very complete Wikipedia list of time zones with offsets explained}
   OpenURL('https://en.wikipedia.org/wiki/List_of_tz_database_time_zones');
end;

procedure TDLHeaderForm.TZRegionBoxChange(Sender: TObject);
begin

 if not LoadingValues then begin
     { Clear out location because region has changed. }
     //TZLocationBox.Text:=''; { not needed, and in Mac, this always clears the field on INI for some strange GUI threading issue}

     { Save the TZ region selection. }
     SelectedTZRegion:=TZRegionBox.Text;
     Application.ProcessMessages;
     vConfigurations.WriteString(SerialINIsection,'Local region',SelectedTZRegion);

     { Read the region database table. }
     ptz.Destroy;
     ptz := TPascalTZ.Create();
     ptz.ParseDatabaseFromFile(appsettings.TZDirectory+SelectedTZRegion);
     Azones.Clear;
     ptz.GetTimeZoneNames(AZones,true); //only geo name = true
     TZLocationBox.Items.Clear;
     TZLocationBox.Items.AddStrings(AZones);
 end;
end;

procedure TDLHeaderForm.FormCreate(Sender: TObject);
begin
     { Initialize required variables. }
     AZones:=TStringList.Create;
     ptz := TPascalTZ.Create();
     CheckInstrumentID;
     DefinitionsLink.hint:=SpecificationsLink;
end;

procedure TDLHeaderForm.FormDestroy(Sender: TObject);
begin
 CloseButtonClick(nil);
 AZones.Destroy;
 ptz.Destroy;
end;

procedure TDLHeaderForm.Button1Click(Sender: TObject);
begin
  {check specific timezone, from upascaltz example wiki}
 if ptz.TimeZoneExists('Australia/AS') then
    StatusMessage('tz exists.') else
      StatusMessage('tz does not exist.');
end;


procedure TDLHeaderForm.DataSupplierEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Data Supplier',DataSupplierEntry.Text);
end;

procedure TDLHeaderForm.MovingStationaryPositionComboChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Moving Stationary Position',MovingStationaryPositionCombo.Text);
end;

procedure TDLHeaderForm.MovingStationaryDirectionComboChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Moving Stationary Direction',MovingStationaryDirectionCombo.Text);
end;

procedure TDLHeaderForm.FiltersPerChannelEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Filters Per Channel',FiltersPerChannelEntry.Text);
end;

procedure TDLHeaderForm.CoverOffsetEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'CoverOffset',CoverOffsetEntry.Text);
end;

procedure TDLHeaderForm.DefinitionsLinkClick(Sender: TObject);
begin
   OpenURL(SpecificationsLink);
 end;

procedure TDLHeaderForm.DefinitionsLinkMouseEnter(Sender: TObject);
begin
 DefinitionsLink.Cursor := crHandPoint;
 DefinitionsLink.Font.Color := clBlue;
 DefinitionsLink.Font.Style := [fsUnderline];
 //if Pos('http://www.', DefinitionsLink.Caption) = 0 then
 //  DefinitionsLink.Caption := 'http://www.' + DefinitionsLink.Caption;
end;

procedure TDLHeaderForm.DefinitionsLinkMouseLeave(Sender: TObject);
begin
 DefinitionsLink.Font.Style := [];
 //if Pos('http://www.', DefinitionsLink.Caption) > 0 then
 //  DefinitionsLink.Caption := Copy(DefinitionsLink.Caption, Pos('http://www.', DefinitionsLink.Caption) + Length('http://www.'), Length(DefinitionsLink.Caption));
end;

procedure TDLHeaderForm.CloseButtonClick(Sender: TObject);
var
  MyFile: TIniFile;
begin
  MyFile := TIniFile.Create(ConfigFilePath);
  try
  MyFile.UpdateFile;
  Close;
  finally
    MyFile.Free;
  end;
end;

procedure TDLHeaderForm.PDFDocButtonClick(Sender: TObject);
begin
  OpenDocument(appsettings.DataDirectory+'47_SKYGLOW_DEFINITIONS.PDF');
end;

procedure TDLHeaderForm.EditPositionButtonClick(Sender: TObject);
begin
   worldmap.FormWorldmap.WorldmapShow('DLHeader', PositionEntry.Text );
end;

procedure TDLHeaderForm.UserComment1Change(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'UserComment1',UserComment1.Text);
end;

procedure TDLHeaderForm.UserComment2Change(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'UserComment2',UserComment2.Text);
end;

procedure TDLHeaderForm.UserComment3Change(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'UserComment3',UserComment3.Text);
end;

procedure TDLHeaderForm.UserComment4Change(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'UserComment4',UserComment4.Text);
end;

procedure TDLHeaderForm.UserComment5Change(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'UserComment5',UserComment5.Text);
end;

procedure TDLHeaderForm.FieldOfViewEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Field Of View',FieldOfViewEntry.Text);
end;
procedure TDLHeaderForm.ReadINI();
var
  pieces: TStringList;

begin

   {Prevent other routines from affecting the Time zone location name}
   TZInitialLoad:=True;

   {Prepare for parsing}
   pieces := TStringList.Create;
   pieces.Delimiter := ',';
   pieces.StrictDelimiter := False; {will be parsing spaces }

   {Determine the section based on the selected serial number}
   SerialINIsection:='Serial:'+Unit1.SelectedUnitSerialNumber;

   { Pull Timezone information from INI file if it exists.}
   SelectedTZRegion:= vConfigurations.ReadString(SerialINIsection,'Local region');
   TZRegionBox.Text:=SelectedTZRegion;
   if (FileExists(appsettings.TZDirectory+SelectedTZRegion) and (length(SelectedTZRegion)>0))then begin
       ptz.Destroy;
       ptz := TPascalTZ.Create();
       ptz.ParseDatabaseFromFile(appsettings.TZDirectory+SelectedTZRegion);
       ptz.GetTimeZoneNames(AZones,true);  //only geo name = true, does not show short names
       TZLocationBox.Items.Clear;
       TZLocationBox.Items.AddStrings(AZones);
     end;

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

   InstrumentIDEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Instrument ID');
   DataSupplierEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Data Supplier');
   LocationNameEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Location Name');

   PositionEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Position');
   {Parse location}
   pieces.DelimitedText := PositionEntry.Text;
   if pieces.Count>1 then begin
     MyLatitude:=StrToFloatDef(pieces.Strings[0],0);
     MyLongitude:=StrToFloatDef(pieces.Strings[1],0);
   end else begin
     MyLatitude:=0;
     MyLongitude:=0;
   end;
   //Parse elevation
   if pieces.Count>2 then begin
     MyElevation:=StrToFloatDef(pieces.Strings[2],0);
   end else begin
     MyElevation:=0;
   end;

   TimeSynchEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Time Synchronization');
   MovingStationaryPositionCombo.Text:=vConfigurations.ReadString(SerialINIsection,'Moving Stationary Position');
   MovingStationaryDirectionCombo.Text:=vConfigurations.ReadString(SerialINIsection,'Moving Stationary Direction');
   NumberOfChannelsEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Number Of Channels');
   FiltersPerChannelEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Filters Per Channel');
   MeasurementDirectionPerChannelEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Measurement Direction Per Channel');
   FieldOfViewEntry.Text:=vConfigurations.ReadString(SerialINIsection,'Field Of View');
   CoverOffsetEntry.Text:=vConfigurations.ReadString(SerialINIsection,'CoverOffset');
   UserComment1.Text:=vConfigurations.ReadString(SerialINIsection,'UserComment1');
   UserComment2.Text:=vConfigurations.ReadString(SerialINIsection,'UserComment2');
   UserComment3.Text:=vConfigurations.ReadString(SerialINIsection,'UserComment3');
   UserComment4.Text:=vConfigurations.ReadString(SerialINIsection,'UserComment4');
   UserComment5.Text:=vConfigurations.ReadString(SerialINIsection,'UserComment5');


   //GPS tab
   FormLogCont.GPSPortSelect.Text:=vConfigurations.ReadString(SerialINIsection,'GPS Port');
   logcont.GPSBaudrate:=StrToIntDef(vConfigurations.ReadString(SerialINIsection,'GPS Baud','4800'),4800);
   FormLogCont.GPSBaudSelect.Text:=IntToStr(logcont.GPSBaudrate);
   FormLogCont.GPSEnable.Checked:=vConfigurations.ReadBool(SerialINIsection,'GPS Enabled');

   //GoTo tab
   logcont.GotoBaudrate:=StrToIntDef(vConfigurations.ReadString('GoToSettings','GoTo Baud',''),9600);
   FormLogCont.GoToBaudSelect.Text:=IntToStr(logcont.GoToBaudrate);

   //Write hardware identifier (Ethernet-MAC, USB-ID)
   {This detail is not chosen by the user, it is automatically stored after reading other header information.}
   vConfigurations.WriteString(SerialINIsection,'HardwareID',SelectedHardwareID);

   if Assigned(pieces) then
     FreeAndNil(pieces);

   TZInitialLoad:=False; {Allow other routines to affect the Time zone location name}


end;

procedure TDLHeaderForm.MeasurementDirectionPerChannelEntryChange(Sender: TObject);
begin
   if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Measurement Direction Per Channel',MeasurementDirectionPerChannelEntry.Text);
end;

function TDLHeaderForm.CheckInstrumentID() : Boolean;
var
  CheckChar: char;
begin
     CheckInstrumentID:=True; {Assume that text is valid}

     for CheckChar in InstrumentIDEntry.Text do
         if not (CheckChar in ['0'..'9','A'..'Z','a'..'z','_','-']) then
            CheckInstrumentID:=False;

     InvalidInstrumentID.Visible:=not CheckInstrumentID;
end;

function TDLHeaderForm.CheckLocationName() : Boolean;
var
  CheckChar: char;
begin
     CheckLocationName:=True; {Assume that text is valid}

     for CheckChar in LocationNameEntry.Text do
         if not (CheckChar in ['0'..'9','A'..'Z','a'..'z','_','-']) then
            CheckLocationName:=False;

     InvalidLocationName.Visible:=not CheckLocationName;
end;
procedure TDLHeaderForm.InstrumentIDEntryChange(Sender: TObject);
begin
  if not LoadingValues then begin
    Application.ProcessMessages; //Wait for widgets to become visible
    if CheckInstrumentID then
      vConfigurations.WriteString(SerialINIsection,'Instrument ID',InstrumentIDEntry.Text);
  end;
end;

procedure TDLHeaderForm.LocationNameEntryChange(Sender: TObject);
begin
   if not LoadingValues then begin
     Application.ProcessMessages; //Wait for widgets to become visible
     if CheckLocationName then
       vConfigurations.WriteString(SerialINIsection,'Location Name',LocationNameEntry.Text);
   end;
end;

procedure TDLHeaderForm.NumberOfChannelsEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Number Of Channels',NumberOfChannelsEntry.Text);
end;

procedure TDLHeaderForm.TimeSynchEntryChange(Sender: TObject);
begin
 if not LoadingValues then
     vConfigurations.WriteString(SerialINIsection,'Time Synchronization',TimeSynchEntry.Text);
end;


initialization
  {$I dlheader.lrs}

end.

