unit CalibForm;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  FileCtrl, Grids, StdCtrls, ComCtrls, Buttons, Mask, Spin,
  Registry, ExtCtrls, Printers, Menus,
  ParmSrch, MyUtils, FcstUtil, MyDates, Arrays,
  LBRMTypes, LBRMGlob, LBRMThread, LBRMRead, ImgList;


const LakeName : array[1..7] of string = ('SUPERIOR', 'MICHIGAN', 'HURON',
                                 'GEORGIAN', 'STCLAIR', 'ERIE', 'ONTARIO');


type
   TNotifyEvent = procedure(Sender: TObject) of object;

type
  TForm1 = class(TForm)
    EBHidden: TEdit;
    Memo1: TMemo;
    Panel1: TPanel;
    OpenDialog1: TOpenDialog;
    PageControl1: TPageControl;
    TabSheet1: TTabSheet;
    Label_WorkDir: TLabel;
    Label_MetFile: TLabel;
    EBWorkingDir: TEdit;
    BtnWorkingDir: TButton;
    EBMetFile: TEdit;
    BtnMetFile: TButton;
    TabSheet2: TTabSheet;
    TabSheet3: TTabSheet;
    TabSheet4: TTabSheet;
    Label_FlowFile: TLabel;
    EBFlowFile: TEdit;
    BtnFlowFile: TButton;
    Label_BoundFile: TLabel;
    EBBoundaryFile: TEdit;
    BtnBoundLoad: TButton;
    BtnBoundSave: TButton;
    BtnBoundSaveAs: TButton;
    Label_BCMS: TLabel;
    SGBoundary: TStringGrid;
    BtnAddBound: TButton;
    BtnDelBound: TButton;
    Label_ParmFile: TLabel;
    EBParmFile: TEdit;
    BtnParmLoad: TButton;
    BtnParmSave: TButton;
    BtnParmSaveAs: TButton;
    Label_StdParms: TLabel;
    SGMain: TStringGrid;
    SGOpt: TStringGrid;
    Label_OptimumVal: TLabel;
    Label_DSI: TLabel;
    SGInsolationValues: TStringGrid;
    Label_B1: TLabel;
    Label_B2: TLabel;
    Label_DepthUSZ: TLabel;
    EB_B1: TEdit;
    EB_B2: TEdit;
    EB_USZC: TEdit;
    Label_ConsK: TLabel;
    EB_K: TEdit;
    SaveDialog1: TSaveDialog;
    RGOptimization: TRadioGroup;
    BtnClearLog: TButton;
    BtnPrintLog: TButton;
    PrintDialog1: TPrintDialog;
    BtnSaveLog: TButton;
    GroupBox1: TGroupBox;
    Label_SSD: TLabel;
    Label_ED: TLabel;
    Label_CSD: TLabel;
    BtnAdjustDate: TButton;
    DTPStart2: TDateTimePicker;
    DTPEnd: TDateTimePicker;
    DTPStart1: TDateTimePicker;
    StartDiffLbl1: TLabel;
    StartDiffLbl2: TLabel;
    Label_Initial: TLabel;
    GroupBox2: TGroupBox;
    Lbl_Instruct1: TLabel;
    Lbl_CSD_1: TLabel;
    CalStartDate: TLabel;
    Lbl_SSD_1: TLabel;
    SimStartDate: TLabel;
    Lbl_ED_1: TLabel;
    SimCalEndDate: TLabel;
    Label_CfgFile: TLabel;
    BtnConfigLoad: TButton;
    BtnConfigSave: TButton;
    BtnConfigSaveAs: TButton;
    EBConfigFile: TEdit;
    StartButton: TBitBtn;
    Label_Area: TLabel;
    EB_Area: TEdit;
    MainMenu1: TMainMenu;
    MainFile: TMenuItem;
    FileOpen: TMenuItem;
    FileClear: TMenuItem;
    N1: TMenuItem;
    FileExit: TMenuItem;
    MainHelp: TMenuItem;
    HelpAbout: TMenuItem;
    Label_OutFile: TLabel;
    EBOutputFile: TEdit;
    BtnOutFile: TButton;
    Lbl_Msg2: TLabel;
    Lbl_OutputWarning: TLabel;
    ImageList1: TImageList;
    SGMainColumn1: TDrawGrid;
    RB_RMSE: TRadioButton;
    RB_Corr: TRadioButton;

    procedure FormCreate(Sender: TObject);
    procedure FormResize(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,Y: Integer);

    procedure PageControl1DrawTab(Control: TCustomTabControl;
              TabIndex: Integer; const Rect: TRect; Active: Boolean);
    procedure PageControl1Change(Sender: TObject);
    procedure TabSheet3Show(Sender: TObject);

    procedure BtnLoadClick(Sender: TObject);
    procedure BtnSaveClick(Sender: TObject);
    procedure BtnSaveAsClick(Sender: TObject);

    procedure BtnWorkingDirClick(Sender: TObject);
    procedure BtnMetFlowFileClick(Sender: TObject);
    procedure BtnOutFileClick(Sender: TObject);

    procedure BtnAdjustDateClick(Sender: TObject);

    procedure BtnAddBoundClick(Sender: TObject);
    procedure BtnDelBoundClick(Sender: TObject);

    procedure EBFileBoxEnter(Sender: TObject);
    procedure EBFileBoxExit(Sender: TObject);
    procedure EBParmFileExit(Sender: TObject);
    procedure EBFileBoxKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure EBFileNameOnChange(Sender: TObject);

    procedure BtnClearLogClick(Sender: TObject);
    procedure BtnSaveLogClick(Sender: TObject);
    procedure BtnPrintLogClick(Sender: TObject);

    procedure EB_KChange(Sender: TObject);
    procedure DTPStart1Change(Sender: TObject);
    procedure DTPStart2Change(Sender: TObject);
    procedure DTPEndChange(Sender: TObject);

    procedure StartButtonClick(Sender: TObject);
    procedure EBHiddenChange(Sender: TObject);

    procedure SGBoundarySelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
    procedure SGMainSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
    procedure SGOptSelectCell(Sender: TObject; ACol, ARow: Integer;  var CanSelect: Boolean);

    procedure SGBoundaryExit(Sender: TObject);
    procedure SGBoundaryKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure SGMainKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure SGInsolationValuesKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    procedure SGMainClick(Sender: TObject);
    procedure FileOpenClick(Sender: TObject);
    procedure FileClearClick(Sender: TObject);
    procedure FileExitClick(Sender: TObject);
    procedure HelpAboutClick(Sender: TObject);
    procedure SGMainColumn1DrawCell(Sender: TObject; ACol, ARow: Integer;
      Rect: TRect; State: TGridDrawState);
  private
    RMSE, Correlation  : real;      // "Real" is equivalent to "double"
    OptimalStat        : real;
    InitialRun         : boolean;
    RandomSearch       : boolean;   // are we randomly searching?
    RandomSearchCount  : integer;   // how many random searches have we done without a better RMSE?
    SavedCellValue     : string;
    BoxPrevValue       : string;
    PageTabFontColor   : TColor;

    FirstParamToChange : integer;
    ParmSearch   : TParmSrch;
    ParmInfo     : TParameterInfo;
    BoundaryInfo : TBoundaryInfo;
    ConfigInfo   : TConfigInfo;

    InitialConditions : TCondArray;
    MeasuredFlow      : TMyArrayOfSingle;

    UserSelectedACell : boolean;
    UserPressedStop   : boolean;
    CfgInfoIsDirty    : boolean;
    SGParmIsDirty     : boolean;
    SGBoundaryIsDirty : boolean;
    SavingPriorToLoad : boolean;
    ChangingPage      : boolean;
    FormIsBeingCreated, FormIsBeingClosed : boolean;
    LastWasBeforeFirst                    : boolean;
    ParmDataIsGood, FlowDataIsGood        : boolean;
    MetDataIsGood, BoundaryDataIsGood     : boolean;

    MyReg : TRegistry;

    RunThread : TLBRMThread;

    procedure CfgVisualToCfgInternal;
    procedure ParmVisualToParmInternal;

    procedure UpdateDateBoxes(StartC, StartS, Last : longint; ForceMax : boolean);

    procedure LoadNewConfigFile;
    procedure LoadNewBoundFile;
    procedure LoadNewParmFile;

    procedure ClearParamBoxes;
    procedure FillParamBoxes;
    procedure ClearParamArrays;
    procedure ClearBoundaryBoxes;
    procedure FillBoundaryBoxes;
    procedure FillFileNameBoxes;
    procedure GetFormDates(var SSeq1, SSeq2, ESeq : longint);
    procedure BoundaryVisualToBoundaryInternal;
    procedure UpdateInitCond;
    procedure SortBoundaryGrid;
    procedure CheckStartDates;
    procedure DisableControlsWhileRunning;
    procedure EnableControlsAfterRunning;

    function CreateLBRMCfgFile : boolean;
    function PromptForDirtyFileSave(EditBox : TEdit) : word;
    function SaveOldFile(EditBox : TEdit) : boolean;
    function FindMaxDateRange(var StartC, StartS, Last : longint) : boolean;

    procedure RunTheLBRM;
    procedure ComputeStats;
    procedure LBRMThreadDone(Sender : TObject);
    procedure StopTheCalibration;

    procedure ChangeViaWin(s: string);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

uses MyConfirm, AddBound, CalAbtBox;

{$R *.DFM}
{$R BITMAPS.RES}
{$RANGECHECKS OFF}

//---------------------------------------------------------------------------
procedure TForm1.FormCreate(Sender: TObject);
var
  I     : integer;
  Flag1 : boolean;
  S     : string;

begin
   FormIsBeingCreated := TRUE;
   FormIsBeingClosed  := FALSE;
   UserSelectedACell  := FALSE;
   UserPressedStop    := FALSE;
   SavingPriorToLoad  := FALSE;
   PageTabFontColor   := clBlack;

   // Default and/or starting values for some global control variables
   ParmDataIsGood  := FALSE;
   FlowDataIsGood  := FALSE;
   MetDataIsGood   := FALSE;
   BoundaryDataIsGood := FALSE;
   CfgInfoIsDirty    := FALSE;
   SGBoundaryIsDirty  := FALSE;
   SGParmIsDirty      := FALSE;

   BoundaryInfo := TBoundaryInfo.Create;
   MeasuredFlow := TMyArrayOfSingle.Create;

   ClearParamArrays;    // assigns arrays to 0.0
   ClearParamBoxes;

   // Starting values for parameter change variables (global)
   RMSE        := 999.99999;
   Correlation := -9.9;
   if (RB_RMSE.Checked) then
      OptimalStat := RMSE
   else
      OptimalStat := Correlation;

   FirstParamToChange := 1;
   RandomSearch := FALSE;
   RandomSearchCount := 0;

   // Initial allocation of dynamic arrays for measured and modeled flow.
   // Arrays will be *resized* as appropriate.
//   MeasuredFlow := TMyArrayOfSingle.Create;
//   ModeledFlow  := TMyArrayOfSingle.Create;

   // Directory and file edit boxes
   EBWorkingDir.Text := '';
   EBParmFile.Text   := '';
   EBMetFile.Text    := '';
   EBFlowFile.Text   := '';
   EBOutputFile.Text := '';

   //
   // Set up the glyph (picture) for the start button
   //
   StartButton.Glyph.LoadFromResourceName(HInstance,'GOBTN');
   StartButton.Caption := 'Start';

   //
   // Initialize dates to 1/1/1950
   //
   DTPStart1.Date := EncodeDate(1950, 1, 1);
   DTPStart2.Date := EncodeDate(1950, 1, 1);
   DTPEnd.Date   := EncodeDate(1950, 1, 1);

   //
   // Set up the string grid containing boundary conditions
   //
   with SGBoundary do begin
      Cells[0,0] := 'Date';
      Cells[1,0] := 'Upper Soil';
      Cells[2,0] := 'Lower Soil';
      Cells[3,0] := 'Ground';
      Cells[4,0] := 'Surface';
      Cells[5,0] := 'Snowpack';
   end;

   //
   // Set up the string grid containing monthly insolation values that may
   // be used for computing "K"
   //
   with SGInsolationValues do begin
      Cells[0,0]  := 'Jan';
      Cells[0,1]  := 'Feb';
      Cells[0,2]  := 'Mar';
      Cells[0,3]  := 'Apr';
      Cells[0,4]  := 'May';
      Cells[0,5]  := 'Jun';
      Cells[0,6]  := 'Jul';
      Cells[0,7]  := 'Aug';
      Cells[0,8]  := 'Sep';
      Cells[0,9]  := 'Oct';
      Cells[0,10] := 'Nov';
      Cells[0,11] := 'Dec';
   end;

   //
   // Set up the string grid that contains the working parameter values (editable).
   //
   with SGMain do begin
      DefaultColWidth  := 86;
      ColWidths[0]     := 80;
      ColWidths[1]     := DefaultColWidth;
      ColWidths[2]     := DefaultColWidth;
      ColWidths[3]     := DefaultColWidth;
      ColWidths[4]     := 45;
      ColWidths[5]     := 47;

      Cells[0,0] := 'Parameter';
      Cells[1,0] := 'Minimum';
      Cells[2,0] := 'Maximum';
      Cells[3,0] := 'Current';
      Cells[4,0] := 'Digit';
      Cells[5,0] := 'MaxD';
   end;

   //
   // Set up the string grid containing the computed optimum values
   //
   SGOpt.Cells[0,0] := 'Optimal Set';

   //
   //  Get entries from the Windows registry, if possible.
   //
   MyReg := TRegistry.Create;
   try
     MyReg.RootKey := HKEY_CURRENT_USER;
     Flag1 := MyReg.OpenKey('\Software\GLERL\LBRMCalib', True);
     if (Flag1) then begin
        try
          I := MyReg.ReadInteger('WinTop');
          Top := I;
          I := MyReg.ReadInteger('WinLeft');
          Left := I;
          I := MyReg.ReadInteger('WinWidth');
          Width := I;
          I := MyReg.ReadInteger('WinHeight');
          Height := I;
        except
          Top := 100;
          Left := 150;
          Width := 450;
          Height := 535;
        end;

        //
        // Working directory
        //
        S := MyReg.ReadString('DirWorking');
        if (not DirectoryExists(S)) then S := '';
        EBWorkingDir.Text := S;

        //
        // File names
        //
        S := MyReg.ReadString('FileParm');
        if (not FileExists(S)) then S := '';
        EBParmFile.Text := S;

        S := MyReg.ReadString('FileBound');
        if (not FileExists(S)) then S := '';
        EBBoundaryFile.Text := S;

        S := MyReg.ReadString('FileMet');
        if (not FileExists(S)) then S := '';
        EBMetFile.Text := S;

        S := MyReg.ReadString('FileOutput');
        EBOutputFile.Text := S;

        S := MyReg.ReadString('FileFlow');
        if (not FileExists(S)) then S := '';
        EBFlowFile.Text := S;

        //
        // Configuration file name
        //
        S := MyReg.ReadString('FileConfig');
        if (not FileExists(S)) then S := '';
        EBConfigFile.Text := S;
        if (FileExists(S)) then begin
           LoadNewConfigFile;
        end;

        //
        // Active page
        //
        try
          S := MyReg.ReadString('ActivePage');
          if (CompareText(S,'TabSheet1')=0) then PageControl1.ActivePage := TabSheet1;
          if (CompareText(S,'TabSheet2')=0) then PageControl1.ActivePage := TabSheet2;
          if (CompareText(S,'TabSheet3')=0) then PageControl1.ActivePage := TabSheet3;
          if (CompareText(S,'TabSheet4')=0) then PageControl1.ActivePage := TabSheet4;
        except
          PageControl1.ActivePage := TabSheet1;
        end;
     end;
   finally
     MyReg.CloseKey;
     MyReg.Free;
   end;

   CheckStartDates;
   ActiveControl := StartButton;

   CfgVisualToCfgInternal;
   Lbl_OutputWarning.Visible := FALSE;
   if (Length(EBOutputFile.Text) = 0) then Lbl_OutputWarning.Visible := TRUE;

   FormIsBeingCreated := FALSE;
end;

//---------------------------------------------------------------------------
procedure TForm1.FormResize(Sender: TObject);
begin
   Memo1.Height := ClientHeight - 505;
end;

//---------------------------------------------------------------------------
procedure TForm1.FormShow(Sender: TObject);
var
   S : string;
begin
   //
   // Read the parameter and boundary files, if possible
   //
   S := EBParmFile.Text;
   if (FileExists(S)) then
      if (ReadParmFile(S, ParmInfo)) then begin
         ParmDataIsGood := TRUE;
         SGMain.Row := ParmInfo.CurrentParm;
      end;

   S := EBBoundaryFile.Text;
   if (FileExists(S)) then
      if (ReadBoundaryFile(S, BoundaryInfo)) then begin
         BoundaryDataIsGood := TRUE;
         FillBoundaryBoxes;
      end;

   //
   // Now fill the display items (string grids, etc) with values, if possible.
   //
   if (ParmDataIsGood)     then FillParamBoxes;
   if (BoundaryDataIsGood) then UpdateInitCond;
end;

//---------------------------------------------------------------------------
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
var
   Answer : word;
begin
   FormIsBeingClosed := TRUE;
   ActiveControl := Memo1;
   CanClose := TRUE;        // default value

   if (CfgInfoIsDirty) then begin
      PageControl1.ActivePage := TabSheet1;
      Answer := PromptForDirtyFileSave(EBConfigFile);  // ask (and save if they say yes)
      if (Answer = mrYes) then CanClose := TRUE;
      if (Answer = mrNo) then begin
         CanClose := TRUE;
         CfgInfoIsDirty := FALSE;
      end;
      if (Answer = mrCancel) then begin
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
      if (CfgInfoIsDirty) then begin   // if attempt to save failed
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
   end;

   if (SGBoundaryIsDirty) then begin
      Answer := PromptForDirtyFileSave(EBBoundaryFile);  // ask (and save if they say yes)
      if (Answer = mrYes) then CanClose := TRUE;
      if (Answer = mrNo) then begin
         CanClose := TRUE;
         SGBoundaryIsDirty := FALSE;
      end;
      if (Answer = mrCancel) then begin
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
      if (SGBoundaryIsDirty) then begin   // if attempt to save failed
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
   end;

   if (SGParmIsDirty) then begin
      Answer := PromptForDirtyFileSave(EBParmFile);  // ask (and save if they say yes)
      if (Answer = mrYes) then CanClose := TRUE;
      if (Answer = mrNo) then begin
         CanClose := TRUE;
         SGParmIsDirty := FALSE;
      end;
      if (Answer = mrCancel) then begin
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
      if (SGParmIsDirty) then begin   // if attempt to save failed
         CanClose := FALSE;
         FormIsBeingClosed := FALSE;
         exit;
      end;
   end;
end;

//---------------------------------------------------------------------------
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var
   S      : string;
   Flag1  : boolean;
begin
   MyReg := TRegistry.Create;
   try
     MyReg.RootKey := HKEY_CURRENT_USER;
     Flag1 := MyReg.OpenKey('\Software\GLERL\LBRMCalib', TRUE);
     if (Flag1) then begin
        MyReg.WriteInteger('WinTop',     Top);
        MyReg.WriteInteger('WinLeft',    Left);
        MyReg.WriteInteger('WinWidth',   Width);
        MyReg.WriteInteger('WinHeight',  Height);
        MyReg.WriteString ('FileConfig', EBConfigFile.Text);
        MyReg.WriteString ('DirWorking', EBWorkingDir.Text);
        MyReg.WriteString ('FileMet',    EBMetFile.Text);
        MyReg.WriteString ('FileFlow',   EBFlowFile.Text);
        MyReg.WriteString ('FileOutput', EBOutputFile.Text);
        MyReg.WriteString ('FileBound',  EBBoundaryFile.Text);
        MyReg.WriteString ('FileParm',   EBParmFile.Text);
        if (PageControl1.ActivePage = TabSheet1) then S := 'TabSheet1';
        if (PageControl1.ActivePage = TabSheet2) then S := 'TabSheet2';
        if (PageControl1.ActivePage = TabSheet3) then S := 'TabSheet3';
        if (PageControl1.ActivePage = TabSheet4) then S := 'TabSheet4';
        MyReg.WriteString ('ActivePage', S);
     end;
   finally
     MyReg.CloseKey;
     MyReg.Free;
   end;

   MeasuredFlow.Free;
   BoundaryInfo.Free;
end;

//--------------------------------------------
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
begin
   Screen.Cursor := crDefault;
   //
   // If the process is currently running then change cursor shape
   // if the mouse is over Panel1.
   //
   if (CompareText(StartButton.Caption, 'Stop') = 0) then begin
       if (Y < Panel1.Height) then Screen.Cursor := crNo;
   end;
end;

//-------------------------------------------------------------------------
procedure TForm1.PageControl1DrawTab(Control: TCustomTabControl;
  TabIndex: Integer; const Rect: TRect; Active: Boolean);
var
  T, L : integer;
  R : TRect;
begin
  R := Rect;
  R.Top := R.Top+4;
  Control.Canvas.Brush.Color := Panel1.Color;
  Control.Canvas.Font.Color  := PageTabFontColor;
  T := Rect.Top;
  L := Rect.Left + 3;
  if (Active) then begin
     T := T+4;
     L := L+3;
  end;
  DrawText(Control.Canvas.Handle, PChar(PageControl1.Pages[TabIndex].Caption),
           Length(PageControl1.Pages[TabIndex].Caption), R, DT_CENTER);
  Control.Canvas.TextRect(Rect, L, T, PageControl1.Pages[TabIndex].Caption);
end;

//--------------------------------------------
procedure TForm1.PageControl1Change(Sender: TObject);
begin
   ChangingPage := TRUE;
   if ((Sender as TPageControl).ActivePage = TabSheet3) then begin
      ActiveControl := BtnParmLoad;
      ActiveControl := Sender as TPageControl;
   end;
   ChangingPage := FALSE;
end;

//--------------------------------------------------------------
procedure TForm1.TabSheet3Show(Sender: TObject);
var
   Seq : longint;
   TDT : TDateTime;
begin
   TDT := DTPStart1.Date;
   Seq := DelphiDateToMyDate(TDT);
   CalStartDate.Caption := SeqToMDY(Seq);

   TDT := DTPStart2.Date;
   Seq := DelphiDateToMyDate(TDT);
   SimStartDate.Caption := SeqToMDY(Seq);

   TDT := DTPEnd.Date;
   Seq := DelphiDateToMyDate(TDT);
   SimCalEndDate.Caption := SeqToMDY(Seq);
end;

//----------------------------------------------
procedure TForm1.BtnLoadClick(Sender: TObject);
var
   X, Y        : integer;
   FName       : string;
   DlgTitle    : string;
   Answer      : word;
   DirtyFlag   : boolean;
   TheButton   : TButton;
   EditBox     : TEdit;
   LoadProc    : procedure of object;
begin
   TheButton   := Sender as TButton;
   DirtyFlag   := FALSE;
   DlgTitle    := 'Load File';
   EditBox     := nil;
   LoadProc    := nil;

   if (TheButton = BtnConfigLoad) then begin
      DirtyFlag   := CfgInfoIsDirty;
      EditBox     := EBConfigFile;
      DlgTitle    := 'Load New Configuration File';
      LoadProc    := LoadNewConfigFile;
   end;
   if (TheButton = BtnBoundLoad) then begin
      DirtyFlag   := SGBoundaryIsDirty;
      EditBox     := EBBoundaryFile;
      DlgTitle    := 'Load New Boundary File';
      LoadProc    := LoadNewBoundFile;
   end;
   if (TheButton = BtnParmLoad) then begin
      DirtyFlag   := SGParmIsDirty;
      EditBox     := EBParmFile;
      DlgTitle    := 'Load New Parameter File';
      LoadProc    := LoadNewParmFile;
   end;
   if (EditBox = nil) then exit;

   FName := EditBox.Text;
   if (DirtyFlag) then begin
      Answer := PromptForDirtyFileSave(EditBox);
      if ((Answer = mrCancel) OR (Answer = mrAbort)) then exit;
   end;

   OpenDialog1.Title := DlgTitle;

   OpenDialog1.InitialDir := ExtractFilePath(Application.ExeName);
   if (DirectoryExists(EBWorkingDir.Text)) then
      OpenDialog1.InitialDir := EBWorkingDir.Text;

   OpenDialog1.FileName := '';

   FName := EditBox.Text;
   if (FileExists(FName)) then begin
      OpenDialog1.FileName := FName;
      OpenDialog1.InitialDir := ExtractFilePath(FName);
   end;

   if (OpenDialog1.Execute) then begin
      FName := OpenDialog1.FileName;
      if (not FileExists(FName)) then begin
         X := Left + 50;
         Y := Top + 150;
         MessageDlgPos('The specified file does not exist!',
                   mtError, [mbOk], 0, X, Y);
         exit;
      end;
      EditBox.Text := FName;
      LoadProc;
   end;

   if (TheButton = BtnConfigLoad) then PageControl1.ActivePage := TabSheet1;
   if (TheButton = BtnBoundLoad)  then PageControl1.ActivePage := TabSheet2;
   if (TheButton = BtnParmLoad)   then PageControl1.ActivePage := TabSheet3;
end;

//-------------------------------------------------------
procedure TForm1.BtnSaveClick(Sender: TObject);
var
   FName : string;
begin
   if ((Sender as TButton) = BtnConfigSave) then begin
      FName := EBConfigFile.Text;
      if (Length(Trim(FName)) = 0) then begin    // filename is empty
         BtnSaveAsClick(BtnConfigSaveAs);
         exit;
      end;
      CfgVisualToCfgInternal;
      WriteConfigFile(FName, ConfigInfo);
      CfgInfoIsDirty := FALSE;
   end;
   if ((Sender as TButton) = BtnBoundSave) then begin
      FName := EBBoundaryFile.Text;
      if (Length(Trim(FName)) = 0) then begin    // filename is empty
         BtnSaveAsClick(BtnBoundSaveAs);
         exit;
      end;
      BoundaryVisualToBoundaryInternal;
      WriteBoundaryFile(FName, BoundaryInfo);
      SGBoundaryIsDirty := FALSE;
   end;
   if ((Sender as TButton) = BtnParmSave) then begin
      ParmVisualToParmInternal;
      FName := EBParmFile.Text;
      if (Length(Trim(FName)) = 0) then begin    // filename is empty
         BtnSaveAsClick(BtnParmSaveAs);
         exit;
      end;
      WriteParmFile(FName, ParmInfo);
      SGParmIsDirty := FALSE;
   end;
end;

//---------------------------------------------------------------------------
procedure TForm1.BtnSaveAsClick(Sender: TObject);
var
   EditBox   : TEdit;
begin
    EditBox     := nil;
    if ((Sender as TButton) = BtnConfigSaveAs) then EditBox := EBConfigFile;
    if ((Sender as TButton) = BtnBoundSaveAs)  then EditBox := EBBoundaryFile;
    if ((Sender as TButton) = BtnParmSaveAs)   then EditBox := EBParmFile;
    if (EditBox = nil) then exit;

    CfgInfoIsDirty := TRUE;
    SaveOldFile(EditBox);
end;

//---------------------------------------------------------------------------
procedure TForm1.BtnWorkingDirClick(Sender: TObject);
var
   S : string;
begin
   S := ExtractFilePath(EBConfigFile.Text);
   if (not DirectoryExists(S)) then GetDir(0,S);
   if SelectDirectory(S, [], 0) then begin
      EBWorkingDir.Text := StripTrailingSlash(S) + '\';
      chdir(S);
   end;
end;

//---------------------------------------------------------------------------
procedure TForm1.BtnMetFlowFileClick(Sender: TObject);
var
   SSeq1, SSeq2, ESeq, Seq1, Seq2, Last : longint;
   FName, AppDir, CfgDir, WorkDir, S : string;
   SeqTest : boolean;

   DoingMet, DoingFlow : boolean;
   DialogTitle : string;
   WarnText    : string;
   EditBox     : TEdit;
   GetStartEnd : function(S1:string; var L1, L2 : longint) : boolean;
begin
    DoingMet    := FALSE;
    DoingFlow   := FALSE;
    DialogTitle := '';
    WarnText    := '';
    EditBox     := nil;
    GetStartEnd := nil;

    if ((Sender as TButton) = BtnMetFile) then begin
       DoingMet    := TRUE;
       DoingFlow   := FALSE;
       DialogTitle := 'Choose Met Data File';
       WarnText    := 'Specified met data file does not exist!';
       EditBox     := EBMetFile;
       GetStartEnd := GetMetStartEnd;
    end;
    if ((Sender as TButton) = BtnFlowFile) then begin
       DoingMet    := FALSE;
       DoingFlow   := TRUE;
       DialogTitle := 'Choose Observed Flow Data File';
       WarnText    := 'Specified flow data file does not exist!';
       EditBox     := EBFlowFile;
       GetStartEnd := GetFlwStartEnd;
    end;

    if (EditBox = nil) then exit;

    //
    // Start/end dates
    //
    GetFormDates(SSeq1, SSeq2, ESeq);

    //
    // Default values
    //
    AppDir  := '';
    CfgDir  := '';
    WorkDir := '';
    AppDir := ExtractFilePath(Application.ExeName);
    S := AppDir;
    CfgDir := ExtractFilePath(EBConfigFile.Text);
    if (DirectoryExists(CfgDir))  then S := CfgDir;
    WorkDir := ExtractFilePath(EBWorkingDir.Text);
    if (DirectoryExists(WorkDir)) then S := WorkDir;
    OpenDialog1.Title      := DialogTitle;
    OpenDialog1.FileName   := '';
    OpenDialog1.InitialDir := S;

    //
    // Is existing file named o.k.?  If so use it as a default value.
    //
    FName := EditBox.Text;
    if (FileExists(FName)) then begin
       OpenDialog1.FileName := FName;
       OpenDialog1.InitialDir := ExtractFilePath(FName);
    end;

    //
    if (OpenDialog1.Execute) then begin
       EditBox.Text := OpenDialog1.FileName;
       if (DoingMet)  then MetDataIsGood := FALSE;
       if (DoingFlow) then FlowDataIsGood := FALSE;
       FName := EditBox.Text;
       if (not FileExists(FName)) then begin
          ChangeViaWin('~!~WARNING!!! ' + WarnText);
          exit;
       end;
       if (GetStartEnd(FName, Seq1, Last)) then begin
          SeqTest := FALSE;
          if (DoingMet)  then SeqTest := (Seq1 > SSeq2) OR (Last < ESeq);
          if (DoingFlow) then SeqTest := (Seq1 > SSeq1) OR (Last < ESeq);
          if (SeqTest) then begin
             if (FindMaxDateRange(Seq1, Seq2, Last)) then begin
                UpdateDateBoxes(Seq1, Seq2, Last, TRUE);
                UpdateInitCond;
             end;
             CheckStartDates;
          end;
          if (DoingMet)  then SeqTest := (Seq1 <= SSeq2) AND (Seq2 >= ESeq);
          if (DoingFlow) then SeqTest := (Seq1 <= SSeq1) AND (Seq2 >= ESeq);

          if (DoingMet  AND SeqTest) then MetDataIsGood := TRUE;
          if (DoingFlow AND SeqTest) then begin
             if ReadFlowFile(FName, SSeq1, ESeq, MeasuredFlow) then FlowDataIsGood := TRUE;
          end;
       end;
    end;
end;

//---------------------------------------------------------------------------
procedure TForm1.BtnOutFileClick(Sender: TObject);
var
    FName, AppDir, CfgDir, WorkDir, S : string;
begin
    //
    // Default values
    //
    AppDir  := '';
    CfgDir  := '';
    WorkDir := '';
    AppDir := ExtractFilePath(Application.ExeName);

    S := AppDir;
    CfgDir := ExtractFilePath(EBConfigFile.Text);
    if (DirectoryExists(CfgDir))  then S := CfgDir;
    WorkDir := ExtractFilePath(EBWorkingDir.Text);
    if (DirectoryExists(WorkDir)) then S := WorkDir;

    OpenDialog1.Title      := 'Choose File for LBRM output';
    OpenDialog1.FileName   := S + 'OUTPUT.DAT';
    OpenDialog1.InitialDir := S;

    //
    // Is existing file named o.k.?  If so use it as a default value
    //
    FName := EBOutputFile.Text;
    S := StripTrailingSlash(ExtractFilePath(EBOutputFile.Text));
    if (DirectoryExists(S)) then begin
       OpenDialog1.FileName := FName;
       OpenDialog1.InitialDir := ExtractFilePath(FName);
    end;

    //
    if (OpenDialog1.Execute) then begin
       EBOutputFile.Text := OpenDialog1.FileName;
    end;

end;

//--------------------------------------------------------------------------
procedure TForm1.BtnAdjustDateClick(Sender: TObject);
var
   StartC, StartS, Last : longint;
begin
   if (FindMaxDateRange(StartC, StartS, Last)) then begin
       UpdateDateBoxes(StartC, StartS, Last, TRUE);
       UpdateInitCond;
       SGParmIsDirty := TRUE;
       end
   else begin
       ChangeViaWin('Unable to determine maximum date range.');
       ChangeViaWin('One or more files were unreadable.');
   end;
   CheckStartDates;
end;

//-------------------------------------------------------------------
procedure TForm1.BtnAddBoundClick(Sender: TObject);
var
   I, J         : integer;
   SameEntryNum : integer;
   Seq, Seq2    : longint;
   SameDate     : boolean;
   S            : string;
   ABDlg        : TAddBoundDlg;
   R            : TStringList;
begin
   ABDlg := TAddBoundDlg.Create(Self);

   ABDlg.BoundDates.SetSize(SGBoundary.RowCount-1);
   for J:=1 to SGBoundary.RowCount-1 do begin
       S := SGBoundary.Cells[0,J];
       ABDlg.BoundDates[J-1] := MdyToSeq(S);
   end;

   if (ABDlg.ShowModal = mrOk) then begin
      Seq := DelphiDateToMyDate(ABDlg.BoundDate.Date);

      //
      // Do we already have an entry for that date?  If so, we already
      // verified with the user that we wish to overwrite, so do that.
      //
      SameDate := FALSE;
      SameEntryNum := 0;
      for J:=1 to SGBoundary.RowCount-1 do begin
          S := SGBoundary.Cells[0,J];
          Seq2 := MdyToSeq(S);
          if (Seq = Seq2) then begin
             SameDate := TRUE;
             SameEntryNum := J;
          end;
      end;
      if (SameDate) then begin
         for I:=1 to 5 do
            SGBoundary.Cells[I,SameEntryNum] := ABDlg.StringGrid1.Cells[I-1,1];
         end
      else begin
         S := SeqToMDY(Seq);
         R := TStringList.Create;
         R.Add(S);
         R.Append(ABDlg.StringGrid1.Cells[0,1]);
         R.Append(ABDlg.StringGrid1.Cells[1,1]);
         R.Append(ABDlg.StringGrid1.Cells[2,1]);
         R.Append(ABDlg.StringGrid1.Cells[3,1]);
         R.Append(ABDlg.StringGrid1.Cells[4,1]);
         I := SGBoundary.RowCount;
         SGBoundary.RowCount := SGBoundary.RowCount + 1;
         SGBoundary.Rows[I].Clear;
         SGBoundary.Rows[I].AddStrings(R);
         R.Free;
      end;
      SortBoundaryGrid;
      FillBoundaryBoxes;
      SGBoundaryIsDirty := TRUE;
   end;
   ABDlg.Free;
   UpdateInitCond;
end;

//-------------------------------------------------------------------
procedure TForm1.BtnDelBoundClick(Sender: TObject);
var
   I, J, K, L : integer;
   MCD : TMyConfirmDlg;
begin
   I := SGBoundary.Row;
   MCD := TMyConfirmDlg.Create(Self);
   MCD.LblDate.Caption := SGBoundary.Cells[0,I];
   if (MCD.ShowModal = mrYes) then begin
      for J:=I to SGBoundary.RowCount-2 do begin
         K := J+1;
         for L:=0 to SGBoundary.ColCount-1 do begin
            SGBoundary.Cells[L,J] := SGBoundary.Cells[L,K];
         end;
      end;
      SGBoundary.RowCount := SGBoundary.RowCount - 1;
      SGBoundaryIsDirty := TRUE;
   end;
   BoundaryVisualToBoundaryInternal;
   UpdateInitCond;
end;

//--------------------------------------------
procedure TForm1.EBFileBoxEnter(Sender: TObject);
var
   TEB : TEdit;
begin
   TEB := Sender as TEdit;
   if (TEB = EBConfigFile)   then  BoxPrevValue := EBConfigFile.Text;
   if (TEB = EBWorkingDir)   then  BoxPrevValue := EBWorkingDir.Text;
   if (TEB = EBMetFile)      then  BoxPrevValue := EBMetFile.Text;
   if (TEB = EBFlowFile)     then  BoxPrevValue := EBFlowFile.Text;
   if (TEB = EBOutputFile)   then  BoxPrevValue := EBOutputFile.Text;
   if (TEB = EBBoundaryFile) then  BoxPrevValue := EBBoundaryFile.Text;
   if (TEB = EBParmFile)     then  BoxPrevValue := EBParmFile.Text;
end;

//--------------------------------------------
procedure TForm1.EBFileBoxKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
var
   TEB : TEdit;
begin
   TEB := Sender as TEdit;
   if (Key = VK_ESCAPE) then TEB.Text := BoxPrevValue;
   if (Key = VK_RETURN) then begin
      if (TEB = EBConfigFile)   then ActiveControl := BtnConfigLoad;
      if (TEB = EBWorkingDir)   then ActiveControl := BtnWorkingDir;
      if (TEB = EBMetFile)      then ActiveControl := BtnMetFile;
      if (TEB = EBFlowFile)     then ActiveControl := BtnFlowFile;
      if (TEB = EBOutputFile)   then ActiveControl := BtnOutFile;
      if (TEB = EBBoundaryFile) then ActiveControl := BtnBoundLoad;
      if (TEB = EBParmFile)     then ActiveControl := BtnParmLoad;
   end;
end;

//--------------------------------------------
procedure TForm1.EBFileBoxExit(Sender: TObject);
var
   X, Y      : integer;
   DirtyFlag : boolean;
   FName     : string;
   EMsg, S   : string;
   Answer    : word;
   EditBox   : TEdit;
   LoadProc  : procedure of object;
begin
   X := Left + 50;
   Y := Top + 150;
   EditBox   := Sender as TEdit;
   DirtyFlag := FALSE;           // eliminate compiler warning
   LoadProc  := nil;             // eliminate compiler warning

   if (EditBox = EBConfigFile) then begin
      DirtyFlag := CfgInfoIsDirty;
      EMsg      := 'configuration file';
      LoadProc  := LoadNewConfigFile;
   end;
   if (EditBox = EBBoundaryFile) then begin
      DirtyFlag := SGBoundaryIsDirty;
      EMsg      := 'boundary conditions file';
      LoadProc  := LoadNewBoundFile;
   end;
   if (EditBox = EBParmFile) then begin
      DirtyFlag := SGParmIsDirty;
      EMsg      := 'parameter file';
      LoadProc  := LoadNewParmFile;
   end;

   FName := TRIM(EditBox.Text);
   if (Length(FName) = 0) then exit;
   if (CompareText(BoxPrevValue, FName) = 0) then exit;
   if (DirtyFlag) then begin
      Answer := PromptForDirtyFileSave(EditBox);
      if ((Answer = mrCancel) OR (Answer = mrAbort)) then exit;
   end;

   Lbl_OutputWarning.Visible := FALSE;
   if (Length(EBOutputFile.Text) = 0) then Lbl_OutputWarning.Visible := TRUE;

   if (FileExists(FName)) then
      LoadProc
   else begin
      S := 'The specified ' + EMsg + ' does not exist!';
      MessageDlgPos(S, mtError, [mbOk], 0, X, Y);
      EditBox.Text := BoxPrevValue;
      exit;
   end;
end;

//--------------------------------------------
procedure TForm1.EBParmFileExit(Sender: TObject);
var
   StartC, StartS, Last : integer;
   FName  : string;
begin
   ParmDataIsGood := FALSE;
   FName := EBParmFile.Text;
   if (not FileExists(FName)) then begin
      ChangeViaWin('~!~WARNING!!! Specified parameter file does not exist!');
      ClearParamBoxes;
      DateSequence(1,1,1900,StartC);
      DateSequence(1,1,1900,StartS);
      DateSequence(1,1,1900,Last);
      UpdateDateBoxes(StartC, StartS, Last, TRUE);
      exit;
   end;
   if (ReadParmFile(FName, ParmInfo)) then begin
      FillParamBoxes;
      StartC := ParmInfo.CDate;
      StartS := ParmInfo.SDate;
      Last   := ParmInfo.EDate;
      UpdateDateBoxes(StartC, StartS, Last, TRUE);
      ParmDataIsGood := TRUE;
      SGMain.Row := ParmInfo.CurrentParm;
   end;
   TabSheet3.Hide;
   TabSheet3.Show;       // forces trickle down that resyncs date info
   SGParmIsDirty := FALSE;
   Label_OptimumVal.Caption := 'Best result so far:  *none*';
end;


//-------------------------------------------------------------------
function TForm1.PromptForDirtyFileSave(EditBox : TEdit) : word;
var
   X, Y       : integer;
   Answer     : word;
   DialogText : string;
   DTextPart2 : string;
begin
   if (EditBox = EBConfigFile)   then DialogText := 'configuration';
   if (EditBox = EBBoundaryFile) then DialogText := 'boundary';
   if (EditBox = EBParmFile)     then DialogText := 'parameter';

   if (FormIsBeingClosed) then
      DTextPart2 := 'closing the application?'
   else
      DTextPart2 := 'loading a new file?';

   X := Left + 50;
   Y := Top + 150;
   Answer := MessageDlgPos('You have not saved the updated ' + DialogText +
                           ' file information.' + #13#10 +
                           'Do you want to do so before ' + DTextPart2,
                           mtConfirmation, [mbYes, mbNo, mbCancel], 0, X, Y);
   Result := Answer;
   if ((Answer = mrNo) OR (Answer = mrCancel)) then exit;
   if (Answer = mrYes) then begin
      if (not SaveOldFile(EditBox)) then Result := mrAbort;
      exit;
   end;
end;

//-------------------------------------------------------------------
function TForm1.SaveOldFile(EditBox : TEdit) : boolean;
var
   FName, Dir  : string;
   DialogTitle : string;
begin
    DialogTitle      := '';
    Result           := FALSE;

    GetDir(0, Dir);

    if (EditBox = EBConfigFile) then begin
       CfgVisualToCfgInternal;
       DialogTitle := 'Save Configuration File As:';
    end;
    if (EditBox = EBBoundaryFile) then begin
       BoundaryVisualToBoundaryInternal;
       DialogTitle := 'Save Boundary Conditions File As:';
    end;
    if (EditBox = EBParmFile) then begin
       ParmVisualToParmInternal;
       DialogTitle := 'Save Parameter File As:';
    end;

    //
    // Default values
    //
    SaveDialog1.Title      := DialogTitle;

    SaveDialog1.InitialDir := ExtractFilePath(Application.ExeName);
    if (DirectoryExists(EBWorkingDir.Text)) then
       SaveDialog1.InitialDir := EBWorkingDir.Text;
    SaveDialog1.FileName := '';

    if (FileExists(EditBox.Text)) then begin
      SaveDialog1.InitialDir := ExtractFilePath(EditBox.Text);
      SaveDialog1.FileName   := ExtractFileName(EditBox.Text);
    end;

    //
    if (SaveDialog1.Execute) then begin
       FName := SaveDialog1.FileName;
       EditBox.Text := FName;
       if (EditBox = EBConfigFile) then begin
          Result := WriteConfigFile(FName, ConfigInfo);
          if (Result) then CfgInfoIsDirty := FALSE;
       end;
       if (EditBox = EBBoundaryFile) then begin
          Result := WriteBoundaryFile(FName, BoundaryInfo);
          if (Result) then SGBoundaryIsDirty := FALSE;
       end;
       if (EditBox = EBParmFile) then begin
          Result := WriteParmFile(FName, ParmInfo);
          if (Result) then SGParmIsDirty := FALSE;
       end;
    end;
    chdir(Dir);
end;

//-------------------------------------------------------------------
procedure TForm1.SGMainSelectCell(Sender: TObject; ACol, ARow: Integer;
                                  var CanSelect: Boolean);
begin
   if (UserSelectedACell) then exit;     // do not allow circular selections
   UserSelectedACell  := TRUE;           // This is the "controlling" grid
   SGOpt.Row          := ARow;
   FirstParamToChange := ARow;
   SavedCellValue     := SGMain.Cells[ACol,ARow];
   UserSelectedACell  := FALSE;          // Go back to "no grid has control"
end;

//-------------------------------------------------------------------
procedure TForm1.SGOptSelectCell(Sender: TObject; ACol, ARow: Integer;
                                 var CanSelect: Boolean);
begin
   if (UserSelectedACell) then exit;     // do not allow circular selections
   UserSelectedACell  := TRUE;           // This is the "controlling" grid
   SGMain.Row         := ARow;
   FirstParamToChange := ARow;
   UserSelectedACell  := FALSE;          // Go back to "no grid has control"
end;

//-------------------------------------------------------------------
procedure TForm1.SGBoundarySelectCell(Sender: TObject; ACol, ARow: Integer;
                                      var CanSelect: Boolean);
begin
   if (UserSelectedACell) then exit;     // do not allow circular selections
   UserSelectedACell  := TRUE;           // This is the "controlling" grid
   SavedCellValue     := SGBoundary.Cells[ACol,ARow];
   UserSelectedACell  := FALSE;          // Go back to "no grid has control"
end;

//--------------------------------------------
procedure TForm1.SGMainClick(Sender: TObject);
begin
   if (SGMain.Row <> ParmInfo.CurrentParm) then SGParmIsDirty := TRUE;
end;

//--------------------------------------------
procedure TForm1.SGMainKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
   C, R : longint;
begin
   // If the user just wants to cancel the editing on a particular cell...
   if (Key = VK_ESCAPE) then begin
      C := SGMain.Col;
      R := SGMain.Row;
      SGMain.Cells[C,R] := SavedCellValue;
      exit;
   end;

   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
   if (Key = VK_RETURN) then begin
      ParmVisualToParmInternal;
      FillParamBoxes;
   end;
end;

//-------------------------------------------------------------------
procedure TForm1.SGBoundaryKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
var
   C, R : longint;
begin
   // If the user just wants to cancel the editing on a particular cell...
   if (Key = VK_ESCAPE) then begin
      C := SGBoundary.Col;
      R := SGBoundary.Row;
      SGBoundary.Cells[C,R] := SavedCellValue;
      exit;
   end;

   if (not FormIsBeingCreated) then SGBoundaryIsDirty := TRUE;
   if (Key = VK_RETURN) then begin
      BoundaryVisualToBoundaryInternal;
      FillBoundaryBoxes;
      SortBoundaryGrid;
   end;
end;

//--------------------------------------------
procedure TForm1.EB_KChange(Sender: TObject);
begin
   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
end;

//--------------------------------------------
procedure TForm1.SGInsolationValuesKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState);
begin
   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
end;

//--------------------------------------------------
procedure TForm1.SGBoundaryExit(Sender: TObject);
begin
    if (not SGBoundaryIsDirty) then exit;
    BoundaryVisualToBoundaryInternal;
//    if (OKToReorderBoundaryBoxes) then FillBoundaryBoxes;
    UpdateInitCond;
end;

//-------------------------------------------------------
procedure TForm1.DTPStart1Change(Sender: TObject);
var
   Seq : longint;
   TDT : TDateTime;
begin
   TDT := DTPStart1.Date;
   Seq := DelphiDateToMyDate(TDT);
   CalStartDate.Caption := SeqToMDY(Seq);
   CheckStartDates;
   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
end;

//-------------------------------------------------------
procedure TForm1.DTPStart2Change(Sender: TObject);
var
   Seq : longint;
   TDT : TDateTime;
begin
   TDT := DTPStart2.Date;
   Seq := DelphiDateToMyDate(TDT);
   SimStartDate.Caption := SeqToMDY(Seq);
   CheckStartDates;
   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
end;

//-------------------------------------------------------
procedure TForm1.DTPEndChange(Sender: TObject);
var
   Seq : longint;
   TDT : TDateTime;
begin
   TDT := DTPEnd.Date;
   Seq := DelphiDateToMyDate(TDT);
   SimCalEndDate.Caption := SeqToMDY(Seq);
   if (not FormIsBeingCreated) then SGParmIsDirty := TRUE;
end;


//--------------------------------------------
procedure TForm1.BtnClearLogClick(Sender: TObject);
begin
   Memo1.Lines.Clear;
end;

//--------------------------------------------
procedure TForm1.BtnSaveLogClick(Sender: TObject);
var
   I : integer;
   FName, S : string;
   F : TextFile;
begin
   //
   // Default values
   //
   SaveDialog1.Title := 'Save Log File As:';
   SaveDialog1.FileName := 'CalibLog.txt';
   SaveDialog1.InitialDir := ExtractFilePath(EBWorkingDir.Text);

   //
   if (SaveDialog1.Execute) then begin
      FName := SaveDialog1.FileName;
      AssignFile(F, FName);
      ReWrite(F);
      for I:=0 to Memo1.Lines.Count-1 do begin
         S := Memo1.Lines[I];
         writeln(F, S);
      end;
      CloseFile(F);
   end;
end;

//--------------------------------------------
procedure TForm1.BtnPrintLogClick(Sender: TObject);
var
   I, J, X, Y, Y2 : integer;
   S  : string;
begin
   PrintDialog1.Execute;
   Printer.BeginDoc;

   with Printer.Canvas.Font do begin
      Name := 'Arial';
      Size := 8;
   end;

   J := 0;
   for I:=0 to Memo1.Lines.Count-1 do begin
      S := Memo1.Lines[I];
      inc(J);
      X := 10;
      Y := 10 + J * Printer.Canvas.TextHeight(S);     // top of text
      Y2 := Y + Printer.Canvas.TextHeight(S);         // bottom of text
      if (Y2 > Printer.PageHeight-10) then begin
         Printer.NewPage;
         J := 1;
         Y := 10 + J * Printer.Canvas.TextHeight(S);     // top of text
      end;
      Printer.Canvas.TextOut(X, Y, S);
   end;

   Printer.EndDoc;
end;

//--------------------------------------------
procedure TForm1.DisableControlsWhileRunning;
begin
   MainFile.Enabled    := FALSE;
   Panel1.Enabled      := FALSE;
   BtnClearLog.Enabled := FALSE;
   BtnSaveLog.Enabled  := FALSE;
   BtnPrintLog.Enabled := FALSE;

   //
   // Manually "gray out" the page selection tabs to visually cue the user
   // that s/he cannot change pages while the calibration is ongoing.
   //
   PageTabFontColor := clGray;
   PageControl1.Repaint;

   //
   // Manually "gray out" control buttons on the 3rd page of the tabbed sheets.
   // We only have to do this one page because that's the only visible one while
   // the calibration is running.
   //
   BtnParmLoad.Enabled    := FALSE;
   BtnParmSave.Enabled    := FALSE;
   BtnParmSaveAs.Enabled  := FALSE;
   RB_RMSE.Enabled := FALSE;
   RB_Corr.Enabled := FALSE;
end;

//--------------------------------------------
procedure TForm1.EnableControlsAfterRunning;
begin
   MainFile.Enabled    := TRUE;
   Panel1.Enabled      := TRUE;
   BtnClearLog.Enabled := TRUE;
   BtnSaveLog.Enabled  := TRUE;
   BtnPrintLog.Enabled := TRUE;

   //
   // Manually "black in" the page selection tabs to visually cue the user
   // that s/he can now change pages again.
   //
   PageTabFontColor  := clBlack;
   PageControl1.Repaint;

   //
   // Manually "black in" control buttons on the 3rd page of the tabbed sheets.
   // We only have to do this one page because that's the only one on which
   // we previously "grayed out" controls.
   //
   BtnParmLoad.Enabled    := TRUE;
   BtnParmSave.Enabled    := TRUE;
   BtnParmSaveAs.Enabled  := TRUE;
   RB_RMSE.Enabled := TRUE;
   RB_Corr.Enabled := TRUE;

   Panel1.OnMouseMove := nil;
end;


//--------------------------------------------
procedure TForm1.EBFileNameOnChange(Sender: TObject);
begin
  if (not FormIsBeingCreated) then CfgInfoIsDirty := TRUE;

  Lbl_OutputWarning.Visible := FALSE;
  if (Length(EBOutputFile.Text) = 0) then Lbl_OutputWarning.Visible := TRUE;
end;


//---------------------------------------------------------------------------
procedure TForm1.LoadNewConfigFile;
var
   FName  : string;
begin
   FName := EBConfigFile.Text;
   if (not FileExists(FName)) then begin
      if (not FormIsBeingCreated) then
         ChangeViaWin('~!~WARNING!!! Specified configuration file does not exist!');
      exit;
   end;
   if (ReadConfigFile(FName, ConfigInfo)) then begin
      FillFileNameBoxes;
   end;

   CfgInfoIsDirty := FALSE;
end;

//---------------------------------------------------------------------------
procedure TForm1.LoadNewBoundFile;
var
   FName, S, WD  : string;
   FIBC_Save : boolean;
begin
   FName := EBBoundaryFile.Text;
   S := ExtractFilePath(FName);
   if (Length(S) = 0) then begin
      WD := ConfigInfo.WorkDir;
      if (not FileExists(WD)) then exit;
      FName := WD + FName;

      FIBC_Save := FormIsBeingCreated;
      FormIsBeingCreated := TRUE;
      EBBoundaryFile.Text := FName;
      FormIsBeingCreated := FIBC_Save;
   end;

   if (not FileExists(FName)) then begin
      ChangeViaWin('~!~WARNING!!! Specified boundary conditions file does not exist!');
      ClearBoundaryBoxes;
      exit;
   end;
   if (ReadBoundaryFile(FName, BoundaryInfo)) then begin
      FillBoundaryBoxes;
      BoundaryDataIsGood := TRUE;
   end;
   BoundaryVisualToBoundaryInternal;
   SortBoundaryGrid;
   UpdateInitCond;
   SGBoundaryIsDirty := FALSE;
   CfgInfoIsDirty := TRUE;
end;

//---------------------------------------------------------------------------
procedure TForm1.LoadNewParmFile;
var
   StartC, StartS, Last : integer;
   FName, S, WD  : string;
   FIBC_Save : boolean;
begin
   ClearParamBoxes;
   ParmDataIsGood := FALSE;
   FName := EBParmFile.Text;

   S := ExtractFilePath(FName);
   if (Length(S) = 0) then begin
      WD := ConfigInfo.WorkDir;
      if (not FileExists(WD)) then exit;
      FName := WD + FName;

      FIBC_Save := FormIsBeingCreated;
      FormIsBeingCreated := TRUE;
      EBParmFile.Text    := FName;
      FormIsBeingCreated := FIBC_Save;
   end;

   if (not FileExists(FName)) then begin
      ChangeViaWin('~!~WARNING!!! Specified parameter file does not exist!');
      DateSequence(1, 1, 1900, StartC);
      ParmInfo.CDate := StartC;
      ParmInfo.SDate := StartC;
      ParmInfo.EDate := StartC;
      StartS         := StartC;
      Last           := StartC;
      UpdateDateBoxes(StartC, StartS, Last, TRUE);
      TabSheet3.Hide;
      TabSheet3.Show;       // forces trickle down that resyncs date info
      exit;
   end;
   if (ReadParmFile(FName, ParmInfo)) then begin
      FillParamBoxes;
      StartC := ParmInfo.CDate;
      StartS := ParmInfo.SDate;
      Last   := ParmInfo.EDate;
      UpdateDateBoxes(StartC, StartS, Last, TRUE);
      ParmDataIsGood := TRUE;
      SGMain.Row := ParmInfo.CurrentParm;
   end;

   TabSheet3.Hide;
   TabSheet3.Show;       // forces trickle down that resyncs date info

   SGParmIsDirty  := FALSE;
   CfgInfoIsDirty := TRUE;
   Label_OptimumVal.Caption := 'Best result so far:  *none*';
end;

//--------------------------------------------------------------------------
procedure TForm1.ClearBoundaryBoxes;
var
   I, J      : integer;
   FIBC_Save : boolean;
begin
   FIBC_Save := FormIsBeingCreated;
   FormIsBeingCreated := TRUE;    // disables SGBoundaryIsDirty => TRUE stuff

   for J:=1 to SGBoundary.RowCount-1 do begin
     for I:=0 to SGBoundary.ColCount-1 do SGBoundary.Cells[I,J] := ' ';
   end;

   FormIsBeingCreated := FIBC_Save;
end;

//--------------------------------------------------------------------------
procedure TForm1.ClearParamBoxes;
var
   I, J      : integer;
   FIBC_Save : boolean;
begin
   FIBC_Save := FormIsBeingCreated;
   FormIsBeingCreated := TRUE;    // disables SGParmIsDirty => TRUE stuff

   for J:=1 to 9 do begin
     for I:=1 to 5 do SGMain.Cells[I,J] := ' ';
     SGOpt.Cells[0,J] := ' ';
   end;

   for J:=0 to 11 do SGInsolationValues.Cells[1,J] := ' ';
   EB_Area.Text := '';
   EB_K.Text    := '';
   EB_B1.Text   := '';
   EB_B2.Text   := '';
   EB_USZC.Text := '2.0';    // default value

   DTPStart1.Date := StrToDate('1/1/1950');
   DTPStart2.Date := StrToDate('1/1/1950');
   DTPEnd.Date    := StrToDate('1/1/1950');

   FormIsBeingCreated := FIBC_Save;
end;

//--------------------------------------------------------------------------
procedure TForm1.FillFileNameBoxes;
var
    X, Y            : integer;
    SomeWereBad     : boolean;
    S, S2, CfgPath  : string;
begin
    S := EBConfigFile.Text;
    if (Length(S) > 0) then CfgPath := ExtractFilePath(S);

    EBMetFile.Text      := '';
    EBBoundaryFile.Text := '';
    EBParmFile.Text     := '';
    EBOutputFile.Text   := '';
    EBWorkingDir.Text   := '';
    EBFlowFile.Text     := '';

    SomeWereBad := FALSE;

    S := ConfigInfo.WorkDir;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if ((Length(S) > 0) AND (DirectoryExists(S))) then
        begin
        ConfigInfo.WorkDir := S;
        EBWorkingDir.Text := ConfigInfo.WorkDir;
        end
    else begin
        ConfigInfo.WorkDir := CfgPath;
        EBWorkingDir.Text := ConfigInfo.WorkDir;
    end;

    S := ConfigInfo.MetFile;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if ((Length(S) > 0) AND (FileExists(S))) then
        begin
        ConfigInfo.MetFile := S;
        EBMetFile.Text := ConfigInfo.MetFile;
        end
    else begin
        ConfigInfo.MetFile := '';
        EBMetFile.Text := ConfigInfo.MetFile;
        if (Length(S) > 0) then SomeWereBad := TRUE;
    end;

    S := ConfigInfo.BoundFile;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if ((Length(S) > 0) AND (FileExists(S))) then
        begin
        ConfigInfo.BoundFile := S;
        EBBoundaryFile.Text := ConfigInfo.BoundFile;
        end
    else begin
        ConfigInfo.BoundFile := '';
        EBBoundaryFile.Text := ConfigInfo.BoundFile;
        if (Length(S) > 0) then SomeWereBad := TRUE;
    end;

    S := ConfigInfo.ParmFile;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if ((Length(S) > 0) AND (FileExists(S))) then
        begin
        ConfigInfo.ParmFile := S;
        EBParmFile.Text := ConfigInfo.ParmFile;
        end
    else begin
        ConfigInfo.ParmFile := '';
        EBParmFile.Text := ConfigInfo.ParmFile;
        if (Length(S) > 0) then SomeWereBad := TRUE;
    end;

    S := ConfigInfo.FlowFile;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if ((Length(S) > 0) AND (FileExists(S))) then
        begin
        ConfigInfo.FlowFile := S;
        EBFlowFile.Text := ConfigInfo.FlowFile;
        end
    else begin
        ConfigInfo.FlowFile := '';
        EBFlowFile.Text := ConfigInfo.FlowFile;
        if (Length(S) > 0) then SomeWereBad := TRUE;
    end;

    S := ConfigInfo.LBRMFile;
    S2 := ExtractFilePath(S);
    if ((Length(S2) < 1) AND (Length(S) > 0)) then S := CfgPath + S;
    if (Length(S) > 0) then
        begin
        ConfigInfo.LBRMFile := S;
        EBOutputFile.Text := ConfigInfo.LBRMFile;
        end
    else begin
        ConfigInfo.LBRMFile := '';
        EBOutputFile.Text := ConfigInfo.LBRMFile;
    end;

    Lbl_OutputWarning.Visible := FALSE;
    if (Length(EBOutputFile.Text) = 0) then Lbl_OutputWarning.Visible := TRUE;

    if (FileExists(ConfigInfo.BoundFile)) then LoadNewBoundFile;
    if (FileExists(ConfigInfo.ParmFile))  then LoadNewParmFile;

    if (SomeWereBad) then begin
      S := 'One or more of the files specified in the configuration' + #13#10 +
           'file do not exist.  Those fields are left blank.';
      X := Left + 50;
      Y := Top + 150;
      MessageDlgPos(S, mtCustom, [mbOk], 0, X, Y);
    end;
end;

//--------------------------------------------------------------------------
procedure TForm1.FillParamBoxes;
var
   J   : integer;
   Seq : longint;
   Temp : TTabSheet;
   FIBC_Save : boolean;
begin
   for J:=1 to 9 do begin
      SGMain.Cells[1,J] := FormatFloat(' 0.000E+00;0.000E+00', ParmInfo.ParmMin[J]);
      SGMain.Cells[2,J] := FormatFloat(' 0.000E+00;0.000E+00', ParmInfo.ParmMax[J]);
      SGMain.Cells[3,J] := FormatFloat(' 0.000E+00;0.000E+00', ParmInfo.ParmNow[J]);
      SGMain.Cells[4,J] := IntToStr(ParmInfo.DigitNow[J]);
      SGMain.Cells[5,J] := IntToStr(ParmInfo.DigitMax[J]);
      SGOpt.Cells[0,J]  := FormatFloat(' 0.000E+00;0.000E+00', ParmInfo.ParmOpt[J]);
   end;

   for J:=0 to 11 do begin
      SGInsolationValues.Cells[1,J] := FormatFloat(' 0.000E+00;0.000E+00', ParmInfo.Insolation[J+1]);
   end;

   //
   // Re-use the "FormIsBeingCreated" flag to keep the "SGParmIsDirty" flag from
   // being forced to TRUE by the event handlers for the following 4 edit boxes
   // and the date boxes.
   // They are designed to force the "SGParmIsDirty" flag to TRUE when the *user*
   // changes their value, not when it is changed as a result of loading a file.
   //
   FIBC_Save := FormIsBeingCreated;
   FormIsBeingCreated := TRUE;

   EB_Area.Text := FormatFloat(' 0.00000E+00', ParmInfo.Area);
   EB_K.Text    := FormatFloat(' 0.00000E+00', ParmInfo.KCons);
   EB_B1.Text   := FormatFloat(' 0.000000', ParmInfo.B1);
   EB_B2.Text   := FormatFloat(' 0.000000', ParmInfo.B2);
   EB_USZC.Text := FormatFloat(' 0.000000', ParmInfo.USZC);

   Seq := ParmInfo.CDate;
   DTPStart1.Date := MyDateToDelphiDate(Seq);   // calib start date
   DTPStart1.OnChange(DTPStart1);
   Seq := ParmInfo.SDate;
   DTPStart2.Date := MyDateToDelphiDate(Seq);   // simulation start date
   DTPStart2.OnChange(DTPStart2);
   Seq := ParmInfo.EDate;
   DTPEnd.Date    := MyDateToDelphiDate(Seq);   // sim/calib end date
   DTPEnd.OnChange(DTPEnd);

   FormIsBeingCreated := FIBC_Save;

   //
   // Force an OnShow event for TabSheet3.  Go back to whatever the active
   // page was after doing so.
   //
   Temp := PageControl1.ActivePage;
   TabSheet3.Show;
   PageControl1.ActivePage := Temp;
end;

//--------------------------------------------------------------------------
procedure TForm1.FillBoundaryBoxes;
var
   I, J, K, N : integer;
   L1, L2  : longint;
   A   : array[1..5] of single;
   S   : string;
   Dt  : TMyDate;
   SD  : TMyArrayOfLongInt;
   TBL : TMyArrayOfInt;
begin
   I := BoundaryInfo.Count;
   SGBoundary.ScrollBars := ssNone;
   SGBoundary.RowCount := I+1;
   if (I > 13) then begin
      I := 13;
      SGBoundary.ScrollBars := ssVertical;
   end;

   Dt := TMyDate.Create;

   //
   // Sort into chronological order
   //
   SD  := TMyArrayOfLongInt.Create;
   TBL := TMyArrayOfInt.Create;
   SD.SetSize(I);
   TBL.SetSize(I);
   for J:=0 to I-1 do begin
      SD[J] := BoundaryInfo[J].SeqNum;
      TBL[J] := J;
   end;
   for J:=I-1 downto 1 do begin
      for K:=0 to J-1 do begin
         L1 := SD[TBL[K]];
         L2 := SD[TBL[K+1]];
         if (L1 > L2) then begin
            N := TBL[K+1];
            TBL[K+1] := TBL[K];
            TBL[K]   := N;
         end;
      end;
   end;

   //
   // Fill in the display
   //
   I := BoundaryInfo.Count;
   for J:=0 to I-1 do begin
      Dt.SetSeq(BoundaryInfo[TBL[J]].SeqNum);
      A[1] := BoundaryInfo[TBL[J]].USZM;
      A[2] := BoundaryInfo[TBL[J]].LSZM;
      A[3] := BoundaryInfo[TBL[J]].GZM;
      A[4] := BoundaryInfo[TBL[J]].SURF;
      A[5] := BoundaryInfo[TBL[J]].SNOW;
      SGBoundary.Cells[0,J+1] := DateToMDY(Dt);
      for K:=1 to 5 do begin
         if (A[K] > -98) then S := FormatFloat('0.000', A[K])
         else                 S := ' ';
         SGBoundary.Cells[K,J+1] := S;
      end;
   end;
   Dt.Free;

end;

//--------------------------------------------------------------------------
procedure TForm1.SortBoundaryGrid;
var
   I, J, K, NumLines : integer;
   L1, L2  : longint;
   S   : string;
begin
   SGBoundary.Visible := FALSE;
   NumLines := SGBoundary.RowCount;

   //
   // Do the sorting/swapping
   //
   for J:=NumLines-1 downto 2 do begin
      for K:=1 to J-1 do begin
         S  := SGBoundary.Cells[0,K];
         L1 := MDYToSeq(S);
         S  := SGBoundary.Cells[0,K+1];
         L2 := MDYToSeq(S);
         if (L1 > L2) then begin
            for I:=0 to SGBoundary.Rows[K].Count-1 do begin
               S := SGBoundary.Rows[K].Strings[I];
               SGBoundary.Rows[K].Strings[I] := SGBoundary.Rows[K+1].Strings[I];
               SGBoundary.Rows[K+1].Strings[I] := S;
            end;
         end;
      end;
   end;

   //
   //  Update the internal array of boundary conditions based on display.
   //
   BoundaryVisualToBoundaryInternal;

   SGBoundary.Visible := TRUE;
end;


//--------------------------------------------------------------------------
procedure TForm1.ClearParamArrays;
var
   J : integer;
begin
   for J:=1 to 9 do begin
      ParmInfo.ParmNow[J] := 0.0;
      ParmInfo.ParmMin[J] := 0.0;
      ParmInfo.ParmMax[J] := 0.0;
      ParmInfo.ParmOpt[J] := 0.0;
      ParmInfo.DigitNow[J] := 0;
      ParmInfo.DigitMax[J] := 0;
   end;

   for J:=1 to 5 do begin
      InitialConditions[J] := 0.0;
   end;
end;

//--------------------------------------------------------------------------
procedure TForm1.CfgVisualToCfgInternal;
begin
   ConfigInfo.WorkDir   := EBWorkingDir.Text;
   ConfigInfo.ParmFile  := EBParmFile.Text;
   ConfigInfo.BoundFile := EBBoundaryFile.Text;
   ConfigInfo.MetFile   := EBMetFile.Text;
   ConfigInfo.FlowFile  := EBFlowFile.Text;
   ConfigInfo.LBRMFile  := EBOutputFile.Text;
end;

//--------------------------------------------------
procedure TForm1.BoundaryVisualToBoundaryInternal;
var
   I, J, K : integer;
   A       : array[1..5] of single;
   BI      : TBoundInfo;
begin
    // Make sure that all of the boundary conditions on the visual
    // control are reflected in the internal data structure.
    BoundaryInfo.Clear;    // empty the data structure
    for I:=1 to SGBoundary.RowCount-1 do begin
       BI.SeqNum := -999;
       BI.USZM   := -99.999;
       BI.LSZM   := -99.999;
       BI.GZM    := -99.999;
       BI.SURF   := -99.999;
       BI.SNOW   := -99.999;
       try
         BI.SeqNum := MDYToSeq(SGBoundary.Cells[0,I]);
         for J:=1 to 5 do begin
            A[J] := -999.999;
            if (Length(Trim(SGBoundary.Cells[J,I])) <> 0) then
                val(SGBoundary.Cells[J,I], A[J], K);
         end;
         BI.USZM := A[1];
         BI.LSZM := A[2];
         BI.GZM  := A[3];
         BI.SURF := A[4];
         BI.SNOW := A[5];
       finally
         if (BI.SeqNum > 0) then BoundaryInfo.Add(BI);
       end;
    end;
end;

//--------------------------------------------------------------------------
procedure TForm1.ParmVisualToParmInternal;
var
   J : integer;
   X : single;
begin
   try
     for J := 1 to 9 do begin
       ParmInfo.ParmMin[J]  := StrToFloat(SGMain.Cells[1,J]);
       ParmInfo.ParmMax[J]  := StrToFloat(SGMain.Cells[2,J]);
       ParmInfo.ParmNow[J]  := StrToFloat(SGMain.Cells[3,J]);
       ParmInfo.ParmOpt[J]  := StrToFloat(SGOpt.Cells[0,J]);
       ParmInfo.DigitNow[J] := StrToInt(SGMain.Cells[4,J]);
       ParmInfo.DigitMax[J] := StrToInt(SGMain.Cells[5,J]);
     end;
   except
     ChangeViaWin('~!~Error in parameters specified on form.');
     Raise;
     exit;
   end;

   try
      Val(EB_Area.Text, X, J);
      if (J=0) then ParmInfo.Area := X;
   except
   end;

   try
      Val(EB_K.Text, X, J);
      if (J=0) then ParmInfo.KCons := X;
   except
   end;

   try
      Val(EB_B1.Text, X, J);
      if (J=0) then ParmInfo.B1 := X;
   except
   end;

   try
      Val(EB_B2.Text, X, J);
      if (J=0) then ParmInfo.B2 := X;
   except
   end;

   try
      Val(EB_USZC.Text, X, J);
      if (J=0) then ParmInfo.USZC := X;
   except
   end;

   ParmInfo.CDate := DelphiDateToMyDate(DTPStart1.Date);
   ParmInfo.SDate := DelphiDateToMyDate(DTPStart2.Date);
   ParmInfo.EDate := DelphiDateToMyDate(DTPEnd.Date);
   ParmInfo.CurrentParm := SGMain.Row;
end;


//--------------------------------------------------------------------------
function TForm1.FindMaxDateRange(var StartC, StartS, Last : longint) : boolean;
var
   MetS, MetE, FlwS, FlwE  : longint;       // sequence numbers
   FName : string;
begin
   Result := FALSE;  // default return value is that we failed to complete

   FName := EBMetFile.Text;
   if (not FileExists(FName)) then exit;
   FName := EBFlowFile.Text;
   if (not FileExists(FName)) then exit;

   //
   // Get allowable start/end date range for each input data file.
   // The end date is the earlier of the two files.
   //
   FName := EBMetFile.Text;
   if (not GetMetStartEnd(FName, MetS, MetE)) then exit;
   FName := EBFlowFile.Text;
   if (not GetFLWStartEnd(FName, FlwS, FlwE)) then exit;

   StartC := FlwS;
   StartS := MetS;

   Last := MetE;
   if (FlwE < Last) then Last := FlwE;

   //
   //  If the flow data starts prior to the met data, then adjust the
   //  start date for flow to be the same as the met data.  This is
   //  because the flow data start date determines the allowable start
   //  for the calibration period.  But that cannot start before the LBRM
   //  runs begin, which are determined by the met data start date.  Thus
   //  we will adjust the calibration start date to the LBRM start date.
   //
   if (StartC < StartS) then StartC := StartS;

   Result := TRUE;    // completed successfully
end;


//--------------------------------------------------------------------------
procedure TForm1.UpdateDateBoxes(StartC, StartS, Last : longint;
                                 ForceMax    : boolean);
var
   X, Y : integer;
   FStart1, FStart2, FEnd, F1, F2, F3 : longint;
   D1, M1, Y1 : word;
   D2, M2, Y2 : word;
   D3, M3, Y3 : word;
   FormBlank : boolean;
begin
   //
   // Save the original values of First and Last just in
   // case we need them later.  Usually we won't.
   //
   F1 := StartC;
   F2 := StartS;
   F3 := Last;

   FormBlank := FALSE;
   DecodeDate(DTPStart1.Date, Y1, M1, D1);
   DecodeDate(DTPStart2.Date, Y2, M2, D2);
   DecodeDate(DTPEnd.Date,    Y3, M3, D3);
   if ((Y1=1950) AND (M1=1) AND (D1=1)  AND
       (Y2=1950) AND (M2=1) AND (D2=1)  AND
       (Y3=1950) AND (M3=1) AND (D3=1)) then FormBlank := TRUE;
   if (FormBlank) then ForceMax := TRUE;

   //
   // Get the current date entries from the form
   //
   if (not FormBlank) then GetFormDates(FStart1, FStart2, FEnd);

   //
   // If ForceMax is TRUE then set the form date variables to the passed-in values.
   //
   if (ForceMax) then begin
      FStart1 := StartC;
      FStart2 := StartS;
      FEnd    := Last;
   end;

   //
   // Set the form variables to the period which is common (intersection)
   // between the form and the max values passed in.
   // Note that if ForceMax was TRUE then they are already set to the
   // same values so the common period will be the max range.
   //
   if (StartC < FStart1) then StartC := FStart1; // adjust "StartC" to match edit box
   if (StartS < FStart2) then StartS := FStart2; // adjust "StartS" to match edit box
   if (Last   > FEnd)    then Last   := FEnd;    // adjust "Last" to match edit box

   //
   // Make sure that the start date is on/before the end date.
   // If, for some weird reason it is not, then arbitrarily set the start/end
   // to the maximum range.
   // This is that rare occasion when we need the original values back
   // again, cuz we just changed them.
   //
   // As long as the form has already been created, tell the user you did it.
   // If the form is in the process of being created, however, the call to
   // MessageDlgPos will cause an access violation error, so save that for
   // the OnFormShow event.  *THEN* we will kick out the informative message.
   //
   LastWasBeforeFirst := FALSE;
   if ((StartC > Last) OR (StartS > Last)) then begin
      if (StartC > Last) then StartC := F1;
      if (StartS > Last) then StartS := F2;
      Last := F3;
      if (FormIsBeingCreated) then
          LastWasBeforeFirst := TRUE
      else begin
         X := Left + 50;
         Y := Top + 150;
         MessageDlgPos('File dates did not intersect the start/end dates' +#13+#10+
                       ' that you specified.  Dates were set to the file maximums.',
                       mtInformation, [mbOk], 0, X, Y);
      end;
   end;

   //
   // Update the start/end date edit boxes with the allowable values
   // (or the existing values which have been assigned into the same variables)
   //
   DTPStart1.Date := MyDateToDelphiDate(StartC);
   DTPStart2.Date := MyDateToDelphiDate(StartS);
   DTPEnd.Date    := MyDateToDelphiDate(Last);

   CheckStartDates;
end;

//--------------------------------------------------------------------------
procedure TForm1.GetFormDates(var SSeq1, SSeq2, ESeq : longint);
begin
    try
      SSeq1 := DelphiDateToMyDate(DTPStart1.Date);
      SSeq2 := DelphiDateToMyDate(DTPStart2.Date);
      ESeq  := DelphiDateToMyDate(DTPEnd.Date);
    except
      SSeq1 := -999;
      SSeq2 := -999;
      ESeq  := -999;
    end;
end;

//--------------------------------------------------------------------------
procedure TForm1.CheckStartDates;
var
   Seq1, Seq2 : longint;
begin
   Seq1 := DelphiDateToMyDate(DTPStart1.Date);    // calibration start date
   Seq2 := DelphiDateToMyDate(DTPStart2.Date);    // simulation start date

   StartDiffLbl1.Caption := '';
   StartDiffLbl2.Caption := '';
   if (Seq1-Seq2 < 365) then begin
      StartDiffLbl1.Caption := 'We strongly recommend that the calibration start date';
      StartDiffLbl2.Caption := 'be at least one year after the simulation start date.';
   end;

   UpdateInitCond;
end;

//--------------------------------------------------------------------------
procedure TForm1.StartButtonClick(Sender: TObject);
var
   I, D, Err, WH, X, Y : integer;
   FSD1, FSD2, FED     : longint;
   Seq1, Seq2          : longint;
   P                   : single;
   FName, Dir1, Dir2   : string;
begin
   //
   // If the user has already told the process to stop, but it hasn't
   // quite finished yet.
   //
   if (CompareText(StartButton.Caption, 'Stopping') = 0) then exit;

   //
   // If the process is currently running freely
   //
   if (CompareText(StartButton.Caption, 'Stop') = 0) then begin
      StartButton.Caption    := 'Stopping';
      StartButton.Enabled    := FALSE;     // don't allow it to be clicked again
      UserPressedStop        := TRUE;

      try
        RunThread.Terminate;
      except
        // Sometimes an "access violation" error is generated here, but it doesn't
        // seem to cause any damage.  I don't know *why* it is generated.  But
        // if it happens we will just ignore it and continue.
        exit;
      end;

      exit;
   end;

   //
   // If the process is waiting to be started
   //
   if (CompareText(StartButton.Caption, 'Start') = 0) then begin
      PageControl1.ActivePage := TabSheet3;

      //
      // Make sure all the Dir/File page entries are filled in
      //
      Dir1 := ExtractFilePath(EBConfigFile.Text);
      Dir2 := ExtractFilePath(EBWorkingDir.Text);
      if (not DirectoryExists(Dir2)) then EBWorkingDir.Text := Dir1;

      //
      //
      FName := EBFlowFile.Text;
      if (Length(FName) = 0) then begin
         X := Left + 50;
         Y := Top + 150;
         MessageDlgPos('No actual flow data file specified.  Aborting.', mtError,
                       [mbOk], 0, X, Y);
         exit;
      end;
      if (not FileExists(FName)) then begin
         X := Left + 50;
         Y := Top + 150;
         MessageDlgPos('File specified for actual flow data does not exist.  Aborting.',
                       mtError, [mbOk], 0, X, Y);
         exit;
      end;
      GetFormDates(FSD1, FSD2, FED);
      GetFlwStartEnd(FName, Seq1, Seq2);
      if ((Seq1 > FSD1) OR (Seq2 < FED)) then begin
         ChangeViaWin('Error: Not enough data in file ' + FName + '!');
         exit;
      end;

      if (not ReadFlowFile(FName, FSD1, FED, MeasuredFlow)) then begin
         X := Left + 50;
         Y := Top + 150;
         MessageDlgPos('Error reading measured streamflow.  Aborting.', mtError,
                       [mbOk], 0, X, Y);
         exit;
      end;

      try
        ParmVisualToParmInternal;     // save values from display into the arrays
      except
        exit;
      end;

      for I:=1 to 9 do begin
         D := ParmInfo.DigitNow[I];
         P := RoundToDigit(ParmInfo.ParmNow[I], D);
         if (P < ParmInfo.ParmMin[I]) then P := RoundToDigit(ParmInfo.ParmMin[I], D);
         if (P > ParmInfo.ParmMax[I]) then P := RoundToDigit(ParmInfo.ParmMax[I], D);
         ParmInfo.ParmNow[I] := P;
         ParmInfo.ParmOpt[I] := P;
      end;
      FillParamBoxes;      // update Parameters on display


      FName := TRIM(EBConfigFile.Text);
      if ((Length(FName) < 1) OR CfgInfoIsDirty) then begin
          PageControl1.ActivePage := TabSheet1;
          BtnSaveAsClick(BtnConfigSaveAs);
          if (CfgInfoIsDirty) then exit;
          PageControl1.ActivePage := TabSheet3;
      end;

      //
      // Create the LBRM.CFG file for use by the next Fortran DLL procedures
      //
      if (not CreateLBRMCfgFile) then exit;

      //
      // Create/initialize the object for parameter searching
      //
      ParmSearch := TParmSrch.Create;
      ParmSearch.SetStartParm(FirstParamToChange);
      ParmSearch.SetCurrentParm(FirstParamToChange);
      ParmSearch.SetAllParmInfo(ParmInfo);

      InitialRun  := TRUE;
      RMSE        := 999.99999;
      Correlation := -9.9;
      if (RB_RMSE.Checked) then
         OptimalStat := RMSE
      else
         OptimalStat := Correlation;

      DisableControlsWhileRunning;

      UserPressedStop        := FALSE;
      StartButton.Caption := 'Stop';
      StartButton.Glyph.LoadFromResourceName(HInstance, 'StopBtn');

      ChangeViaWin('-------------------------------------');

      //
      // Find array dimensions
      //    DS = Days_of_Simulation      i.e. days to run LBRM
      //    BC = Boundary_Condition_count
      //
      DS := FED - FSD2 + 1;
      if not (GetBoundCount(EBBoundaryFile.Text, Seq1, Seq2, BC)) then begin
          ChangeViaWin('Error reading boundary conditions file. Aborting!');
          StopTheCalibration;
          exit;
      end;
      try
        GetDir(0, Dir1);
        Dir2 := StripTrailingSlash(EBWorkingDir.Text);
        chdir(Dir2);
        LBRM_ArraySizes(Err, WH, BC, DS);
        chdir(Dir1);
      except
        ChangeViaWin('Error reading LBRM array sizes.');
        StopTheCalibration;
        exit;
      end;

      //
      // Set the array sizes (i.e. allocate the RAM)
      //
      GetMem(iBCD,    BC*sizeof(longint));
      GetMem(AUpper,  BC*sizeof(single));
      GetMem(ALower,  BC*sizeof(single));
      GetMem(AGround, BC*sizeof(single));
      GetMem(ASurf,   BC*sizeof(single));
      GetMem(ASnow,   BC*sizeof(single));

      GetMem(Tmx, DS*sizeof(smallint));
      GetMem(Tmn, DS*sizeof(smallint));
      GetMem(Prc, DS*sizeof(smallint));

      GetMem(ORun, DS*sizeof(single));
      GetMem(OUsz, DS*sizeof(single));
      GetMem(OLsz, DS*sizeof(single));
      GetMem(OGzm, DS*sizeof(single));
      GetMem(OSs,  DS*sizeof(single));
      GetMem(OSnw, DS*sizeof(single));

      //
      // Assign the values of BC and DS to the "global" variables in
      // unit LBRMGlob so that the LBRM thread can access them.
      //
      LBRMGlob.BC := BC;
      LBRMGlob.DS := DS;

      try
        GetDir(0, Dir1);
        Dir2 := StripTrailingSlash(EBWorkingDir.Text);
        chdir(Dir2);
        I := EBHidden.Handle;
        LBRM_ReadData(Err, I, BC, DS, iBCD^, AUpper^, ALower^,
                      AGround^, ASurf^, ASnow^, Tmx^, TMn^, Prc^);
        if (Err <> 0) then exit;
        chdir(Dir1);
      except
        ChangeViaWin('Error reading LBRM input data.');
        StopTheCalibration;
      end;

      RunTheLBRM;
      exit;
   end;

end;


//------------------------------------------------------------------------
function TForm1.CreateLBRMCfgFile : boolean;
var
   FName : string;
   F     : TextFile;
begin
   Result := FALSE;   // default in case of early exit

   FName  := StripTrailingSlash(EBWorkingDir.Text) + '\LBRM.CFG';
   try
     AssignFile(F, FName);
   except
     ChangeViaWin('~!~Error writing LBRM configuration file');
     exit;
   end;

   try
     Rewrite(F);
     Writeln(F, 'PARMFILE=' + EBParmFile.Text);
     Writeln(F, 'BCFILE='   + EBBoundaryFile.Text);
     Writeln(F, 'METFILE='  + EBMetFile.Text);
     Writeln(F, 'OUTFILE='  + EBOutputFile.Text);
     Writeln(F, 'DATAFILE=' + EBFlowFile.Text);
     Writeln(F, 'WORKDIR='  + EBWorkingDir.Text);
     CloseFile(F);
   except
     CloseFile(F);
     ChangeViaWin('~!~Error writing LBRM configuration file');
     exit;
   end;

   Result := TRUE;   // successful completion
end;


//-------------------------------------------------------------------------
procedure TForm1.RunTheLBRM;
var
   CP, X, Y  : integer;
   KC        : single;
begin
   FillParamBoxes;      // update parameters on display

   ParmSearch.GetCurrentParm(CP);
   SGMain.Col := 3;
   SGOpt.Col  := 0;
   SGMain.Row := CP;
   SGOpt.Row  := CP;

   //
   // Do we need to recalculate the evaptranspiration constant?
   // Only done if Tbase or AlbedS is the parameter that was changed, except
   // that if we just finished changing AlbedS, then the latest value was
   // thrown out and that is the value that was used to calculate the constant.
   // Therefore, we need to do it the first time we run the LBRM with a new
   // value for parameter 3 (Alpha-P).  For simplicity of the code, we'll just
   // go ahead and do it for EVERY iteration chnging parameter 3.
   //
   if ((CP = 1) OR (CP = 2) OR (CP = 3)) then
      LBRMGlob.RecalculateCons := TRUE
   else
      LBRMGlob.RecalculateCons := FALSE;

   KC := ParmInfo.KCons;
   if (LBRMGlob.RecalculateCons) then ParmInfo.KCons := 0.0;
   if (not WriteParmFile(EBParmFile.Text, ParmInfo)) then begin
      X := Left + 50;
      Y := Top + 150;
      MessageDlgPos('Error writing parameter file ('+ EBParmFile.Text + ') for LBRM',
                    mtError, [mbOk], 0, X, Y);
      exit;
   end;
   ParmInfo.KCons := KC;

   Application.ProcessMessages;

   {Here's where we run the LBRM (Fortran program) }
   RunThread := TLBRMThread.Create;
   RunThread.EditBoxHndl  := EBHidden.Handle;
   RunThread.KCons        := ParmInfo.KCons;
   RunThread.CfgFileName  := StripTrailingSlash(EBWorkingDir.Text) + '\LBRM.CFG';
   RunThread.ParmFileName := EBParmFile.Text;
   RunThread.BCFileName   := EBBoundaryFile.Text;
   RunThread.MetFileName  := EBMetFile.Text;
   RunThread.OutFileName  := EBOutputFile.Text;
   RunThread.FlowFileName := EBFlowFile.Text;
   RunThread.WorkingDir   := EBWorkingDir.Text;
   RunThread.OnTerminate  := LBRMThreadDone;  // set the termination routine
   RunThread.Resume;                          // start the Execute method
end;

//-------------------------------------------------------------------------
procedure TForm1.LBRMThreadDone(Sender : TObject);
var
   I, CP, RS : longint;
   WasBetter, Done : boolean;
   S, S1, S2 : string;
begin
   RS := RunThread.ReturnStatus;
   Application.ProcessMessages;
   ParmInfo.KCons := RunThread.KCons;
   RunThread := nil;

   if (RS <> 0) then begin
       if (RS =  1) then ChangeViaWin('LBRM terminated prematurely.');
       if (RS = -2) then ChangeViaWin('Error in results from LBRM.');
       if (RS = -9) then ChangeViaWin('LBRM caused an exception.');
       StopTheCalibration;
       Panel1.Enabled := TRUE;
       exit;
   end;
   SGParmIsDirty := TRUE;
   WasBetter := FALSE;
   ComputeStats;
   ParmInfo.RMSE := RMSE;
   ParmInfo.Correlation := Correlation;

   //
   // echo RMSE, correlation, and parameter values to the memo box
   //
   ParmSearch.GetCurrentParm(CP);
   ParmInfo.CurrentParm := CP;
   S  := '[' + FormatFloat(' ###0.00000', RMSE) + ',' +
               FormatFloat(' #0.0000', Correlation) + '] =>';
   for I:=1 to 9 do begin
      S1 := FormatFloat(' 0.000E+00', ParmInfo.ParmNow[I]);
      if ((I = CP) AND (not InitialRun)) then
          S2 := ' [' + S1 + '] '
      else
          S2 := '  ' + S1 + '  ';
      S := S + S2;
   end;
   ChangeViaWin(S);

   //
   // Was this the very first run of the LBRM?  If so, then don't test the
   // RMSE, etc.  Just increment the parameter and go again.
   //
   if (InitialRun) then begin
      RMSE        := 999.999;
      Correlation := -9.9;
      if (RB_RMSE.Checked) then
         OptimalStat := RMSE
      else
         OptimalStat := Correlation;
      S := FloatToStrF(OptimalStat, ffFixed, 6, 5);
      Label_OptimumVal.Caption := 'Best result so far:  *none*';
      for I:=1 to 9 do ParmInfo.ParmOpt[I] := ParmInfo.ParmNow[I];
      FillParamBoxes;
      InitialRun := FALSE;
      ParmSearch.GetNextParmSet(Done);
      if (Done) then begin
         StopTheCalibration;
         Panel1.Enabled := TRUE;
         exit;
      end;
      ParmSearch.GetAllParmInfo(ParmInfo);
      Application.ProcessMessages;
      RunTheLBRM;
      exit;
   end;

   //
   // Did we get a statistic?  If OptimalStat<0 then computation failed
   // (most likely because there was no flow data to compare to).
   //
   if ((RMSE < 0) OR (Correlation < -1)) then begin
      StartButton.Caption    := 'Start';
      exit;
   end;

   //
   // Did we get a better fit?
   //
   if ( ((RB_RMSE.Checked) AND (RMSE < OptimalStat))  OR
        ((RB_Corr.Checked) AND (Correlation > OptimalStat)) ) then begin
      if (RB_RMSE.Checked) then
         OptimalStat := RMSE
      else
         OptimalStat := Correlation;
      S := FloatToStrF(OptimalStat, ffFixed, 6, 5);
      Label_OptimumVal.Caption := 'Best result so far: ' + TRIM(S);
      for I:=1 to 9 do ParmInfo.ParmOpt[I] := ParmInfo.ParmNow[I];
      FillParamBoxes;
      ParmVisualToParmInternal;
      WasBetter := TRUE;
   end;

   //
   // Ensure that the latest values are stored in "ParmSearch".
   //
   ParmSearch.SetAllParmInfo(ParmInfo);

   //
   //  Get the next parameter set.  If the returned value of Done is TRUE,
   //  then we have fully optimized the parameters and can stop.
   //
   ParmSearch.GetNextParmSet(WasBetter, Done);
   ParmSearch.GetAllParmInfo(ParmInfo);
   if (Done) then begin
      StopTheCalibration;
      exit;
   end;

   //
   // If the user said to stop, then do so.
   //
   if (CompareText(StartButton.Caption, 'Stopping') = 0) then begin
       StopTheCalibration;
       exit;
   end;

   Application.ProcessMessages;

   //
   // Start the next run of the LBRM.  Since it's a separate thread we
   // can do it from here without getting an infinite recursive loop going.
   //
   RunTheLBRM;
end;


//--------------------------------------------------------------------------
procedure TForm1.StopTheCalibration;
var
   I, Err, WH : integer;
   S, S1  : string;
begin
    S := ' ';
    ChangeViaWin(S);

    if (UserPressedStop) then
       S := 'User stopped the calibration!  Best parameter set:'
    else
       S := 'Calibration completed!  Final calibration parameter set:';
    ChangeViaWin(S);

    S := ' ';
    for I:=1 to 9 do begin
       ParmInfo.ParmNow[I] := ParmInfo.ParmOpt[I];
       S1 := FormatFloat(' 0.000E+00', ParmInfo.ParmNow[I]) + '  ';
       S := S + S1;
    end;
    ChangeViaWin(S);

    S1 := FormatFloat(' ###0.00000', RMSE);
    S := 'RMSE (cm over the basin) = ' + S1;
    ChangeViaWin(S);
    S1 := FormatFloat(' ###0.00000', Correlation);
    S := 'Correlation coefficient = ' + S1;
    ChangeViaWin(S);

    //
    // Write the output file
    //
    S := EBOutputFile.Text;
    if (Length(S) > 0) then begin
       WH := EBHidden.Handle;
       LBRM_WriteData(Err, WH, DS, ORun^, OUsz^, OLsz^, OGzm^, OSs^, OSnw^);
    end;

    //
    // Free the allocated RAM
    //
    FreeMem(iBCD);
    FreeMem(AUpper);
    FreeMem(ALower);
    FreeMem(AGround);
    FreeMem(ASurf);
    FreeMem(ASnow);

    FreeMem(Tmx);
    FreeMem(Tmn);
    FreeMem(Prc);

    FreeMem(ORun);
    FreeMem(OUsz);
    FreeMem(OLsz);
    FreeMem(OGzm);
    FreeMem(OSs);
    FreeMem(OSnw);

    //

    FillParamBoxes;
    ParmSearch.Free;     // destroy the parameter search object
    Screen.Cursor := crDefault;

    EnableControlsAfterRunning;

    StartButton.Glyph.LoadFromResourceName(HInstance, 'GoBtn');
    StartButton.Caption    := 'Start';
    StartButton.Enabled    := TRUE;
end;

//--------------------------------------------------------------------------
// Compute the RMSE and correlation of the computed runoff vs measured runoff.
// It is irrelevant for this computation, but for reference: both are in cm.
//
procedure TForm1.ComputeStats;
var
   I, J1, J2, Cnt  : longint;
   Mn, Dy, Yr      : longint;
   FSD1, FSD2, FED : longint;     // form start and end dates (DateSequence numbers)
   MdlFlw          : single;
   SSE, Diff, X, Y : single;
   Sum_X, Sum_X2   : single;
   Sum_Y, Sum_Y2   : single;
   Sum_XY          : single;
   Sxx, Syy, Sxy   : single;

{
   SSE, Diff, X, Y : double;
   Sum_X, Sum_X2   : double;
   Sum_Y, Sum_Y2   : double;
   Sum_XY          : double;
   Sxx, Syy, Sxy   : double;
}

   DoTheDebugFile : boolean;
   Fdbg     : TextFile;
   VNum     : integer;
   FName, S : string;

begin
   RMSE := 999.99999;      // default value (missing).
                           // Note that RMSE is a form global variable.
   Correlation := -9.9;    // default value (missing).
                           // Note that Correlation is a form global variable.

   DoTheDebugFile := FALSE;     // set to TRUE if you want the file(s) created
//   DoTheDebugFile := TRUE;      // set to TRUE if you want the file(s) created

   //
   // Get the form start/end dates and convert to sequence numbers
   //    FSD1 = calibration start date
   //    FSD2 = LBRM (modeled flow) start date
   //    FED  = end date for both
   //
   try
      FSD1 := DelphiDateToMyDate(DTPStart1.Date);
      FSD2 := DelphiDateToMyDate(DTPStart2.Date);
      FED  := DelphiDateToMyDate(DTPEnd.Date);
   except
      exit;
   end;

   //  for debugging purposes.  Open the text file.
   if (DoTheDebugFile) then begin
     VNum := 1;
     while (VNum > 0) do begin
        S := IntToStr(VNum);
        FName := 'StatDebug' + S + '.TXT';
        if FileExists(FName) then
           inc(VNum)
        else begin
           VNum := -1;
           AssignFile(Fdbg, FName);
           Rewrite(Fdbg);
           writeln(Fdbg, 'MM/DD/YYYY Modeled Measured');
        end;
     end;
   end;

   //
   //  compute the sum of squared errors
   //
   SSE := 0.0;   // sum of squared errors
   Cnt := 0;     // count of how many days used (w/o missing data)
   for I := FSD1 to FED do begin
      J1 := I - FSD1;
      J2 := I - FSD2;           // index is 0..n-1
      MdlFlw := ORun^[J2] / ParmInfo.Area * 100;   // cubic meters -> cm over watershed
      if (DoTheDebugFile) then begin
         SequenceDate(Dy, Mn, Yr, I);
         writeln(Fdbg, Mn:2,'/',Dy:2,'/',Yr:2,' ', MdlFlw:9:3, ' ', MeasuredFlow[J1]:9:3);
      end;
      if ((MeasuredFlow[J1] > -1) AND (MdlFlw > -1)) then begin
         Diff := MeasuredFlow[J1] - MdlFlw;
         SSE := SSE + (Diff*Diff);
         inc(Cnt);
      end;
   end;

   //
   //  Compute RMSE.  Values are in cm.
   //
   if (Cnt > 0) then RMSE := SQRT(SSE/(Cnt*1.0));

   //
   //  Compute the correlation coefficient
   //
   Sum_X  := 0.0;
   Sum_X2 := 0.0;
   Sum_Y  := 0.0;
   Sum_Y2 := 0.0;
   Sum_XY := 0.0;
   Cnt := 0;
   for I := FSD1 to FED do begin
      J1 := I - FSD1;
      J2 := I - FSD2;
      X  := MeasuredFlow[J1];
      Y  := ORun^[J2] / ParmInfo.Area * 100;   // cubic meters -> cm over watershed
      if ((X > -1) AND (Y > -1)) then begin
         Sum_X  := Sum_X  + X;
         Sum_Y  := Sum_Y  + Y;
         Sum_X2 := Sum_X2 + X*X;
         Sum_Y2 := Sum_Y2 + Y*Y;
         Sum_XY := Sum_XY + X*Y;
         inc(Cnt);
      end;
   end;
   Correlation := -9.9;
   if (Cnt > 0) then begin
      Sxx := Sum_X2 - (Sum_X*Sum_X)/Cnt;
      Syy := Sum_Y2 - (Sum_Y*Sum_Y)/Cnt;
      Sxy := Sum_XY - (Sum_X*Sum_Y)/Cnt;
      Correlation := Sxy / (SQRT(Sxx*Syy));
   end;

   if (DoTheDebugFile) then begin
     writeln(Fdbg, 'SSE = ', SSE);
     writeln(Fdbg, 'Cnt = ', Cnt);
     writeln(Fdbg, 'RMSE = ', RMSE);
     CloseFile(Fdbg);
   end;
end;


//---------------------------------------------------------------------------
//  Changes the value of the EBHidden.text object via Windows messaging
//  facility.  This ensures that the object is accessed only 1 way at a time.
//  Previously, this program could try to change the value directly while
//  the object was already in a state of being changed via a windows message.
//  This could potentially cause access violation problems.
//
procedure TForm1.ChangeViaWin(s: string);
var
   P : pChar;
begin
 try
   P := PChar(S);       // convert to PChar for Win API call
   SetWindowTextA(EBHidden.handle, P);
   Application.ProcessMessages;
  except
    ShowMessage('Exception in ChangeViaWin');
  end;
end;

//--------------------------------------------------------------------------
//  This procedure is used to respond to changes in the text of the hidden
//  edit box.  These changes in the text could come from anywhere, though
//  many or most of them will be initiated by the Fortran code of the forecast
//  package.  The Fortran code generates a SetWindowText message which sets the
//  text of this edit box, and we respond accordingly.  In order to facilitate
//  this capability, and make it more flexible, I've defined special meanings
//  for some prefixes.  If the first and third characters of the text string
//  are ~ (tilde) then the second character (in between the tildes) will be
//  interpreted as a special prefix code, which determines how the rest of the
//  text is interpreted.  If the second character is not one of the recognized
//  special prefix codes, then the whole line will be treated as though we did
//  not see anything special about it (i.e. the whole line written normally).
//
//  Prefix codes are:
//     ^ : overwrite the previous line of text in the memo box with this text
//     & : the text is to be written to the log file, but not the screen
//     * : the text is to be written to the screen, but not the log file
//     ! : the text is an error message to be displayed in a dialog box
//     T : the text is a new caption (title) for the form
//
procedure TForm1.EBHiddenChange(Sender: TObject);
var
    X, Y : integer;
    S_All, S_4n, SC1, SC2, SC3, LText  : string;
    ProcessSpecial, Special1, Special3 : boolean;
begin
  try
    Application.ProcessMessages;
    S_All  := EBHidden.Text;              // characters 1-n
    S_4n := Copy(EBHidden.Text, 4, 999);  // characters 4-n
    SC1 := Copy(EBHidden.Text, 1, 1);     // character 1 only
    SC2 := Copy(EBHidden.Text, 2, 1);     // the special prefix char (?)
    SC3 := Copy(EBHidden.Text, 3, 1);     // character 3 only
    LText := '';                          // text to write to log file

    //
    // Test for special characters as first and third characters of the string.
    // If they are both tildes, then test character 2.  If it IS one of the
    // special characters then process the data accordingly and set the flag.
    //
    ProcessSpecial := FALSE;
    Special1 := FALSE;
    Special3 := FALSE;
    if (CompareText(SC1, '~') = 0) then Special1 := TRUE;
    if (CompareText(SC3, '~') = 0) then Special3 := TRUE;
    if (Special1 AND Special3) then begin
       ProcessSpecial := TRUE;

       // I've defined "^" to mean that the rest of the text should
       // over-write the current last line.  This is used to simulate the
       // behavior we had in the DOS-based program where we wrote the
       // current status (percent complete, date, etc) over and over on
       // the same line without scrolling.
       if (CompareText(SC2, '^') = 0) then begin
          Memo1.Lines[Memo1.Lines.Count-1] := S_4n;
       end;

       // I've defined "&" to mean that the rest of the text should be
       // written to the log file, but not to the screen.  Useful for
       // debugging info and other summary stuff.
       if (CompareText(SC2, '&') = 0) then begin
          LText := S_4n;
       end;

       // I've defined "*" to mean that the rest of the text should be
       // written to the screen, but not to the log file.  Useful for
       // on-screen progress info and other multiple-write stuff.
       if (CompareText(SC2, '*') = 0) then begin
          LText := '';
       end;

       // I've defined "!" to mean that the rest of the text is an error
       // message which will be displayed modally in a dialog box.
       if (CompareText(SC2, '!') = 0) then begin
          X := Left + 50;
          Y := Top + 150;
          MessageDlgPos(S_4n, mtError, [mbOk], 0, X, Y);
          LText := '';
       end;

       // I've defined "T" to mean that the rest of the text is a
       // new caption ("Title") for the interface.
       if (CompareText(SC2, 'T') = 0) then begin
          Application.MainForm.Caption := S_4n;
          LText := '';
       end;
    end;

    //
    // "Normal" text without special meaning is echoed to both the screen
    // and the log file.
    //
    if (not ProcessSpecial) then begin
       Memo1.Lines.add(S_All);
       LText := S_All;
    end;

    Application.ProcessMessages;

  except
    ShowMessage('Exception in EBHidden.text update');
  end;

end;

//--------------------------------------------------
procedure TForm1.UpdateInitCond;
var
   I, J, K, Seq1, Seq2 : longint;
   X  : single;
   S  : string;
begin
    //
    // Update initial conditions array as appropriate.
    //
    Seq1 := DelphiDateToMyDate(DTPStart2.Date);    // specified LBRM simiulation start date

    for I:=1 to 5 do InitialConditions[I] := -9998.99;

    Label_Initial.Caption := 'Initial Conditions are not available for simulation start date!';

    for J:=1 to SGBoundary.RowCount-1 do begin
        Seq2 := MDYToSeq(SGBoundary.Cells[0,J]);
        if (Seq2 = Seq1) then begin
           for I:=1 to 5 do begin
              S := SGBoundary.Cells[I,J];
              if (Length(Trim(S)) > 0) then begin
                 try
                    val(S, X, K);
                    if (K = 0) then
                       InitialConditions[I] := X
                    else
                       InitialConditions[I] := -9999.99;
                 except
                 end;
              end;
           end;
           Label_Initial.Caption := '';
           for I:=1 to 5 do begin
              if (InitialConditions[I] < 0) then
                 Label_Initial.Caption := 'Initial Conditions are not available for simulation start date!';
           end;
        end;
    end;

end;

//--------------------------------------------------
procedure TForm1.FileOpenClick(Sender: TObject);
begin
   BtnLoadClick(BtnConfigLoad);
end;

//--------------------------------------------------
procedure TForm1.FileClearClick(Sender: TObject);
begin
   // Clear all items
   EBConfigFile.Text   := '';
   EBWorkingDir.Text   := '';
   EBMetFile.Text      := '';
   EBFlowFile.Text     := '';
   EBOutputFile.Text   := '';
   EBBoundaryFile.Text := '';
   EBParmFile.Text     := '';
   ClearParamBoxes;
   ClearBoundaryBoxes;
   BtnClearLogClick(BtnClearLog);
   CfgInfoIsDirty    := FALSE;
   SGParmIsDirty     := FALSE;
   SGBoundaryIsDirty := FALSE;
end;

//--------------------------------------------------
procedure TForm1.FileExitClick(Sender: TObject);
begin
   Close;
end;

//--------------------------------------------------
procedure TForm1.HelpAboutClick(Sender: TObject);
begin
    CalibAboutBox.ShowModal;
end;

//---------------------------------------------------
procedure TForm1.SGMainColumn1DrawCell(Sender: TObject; ACol,
  ARow: Integer; Rect: TRect; State: TGridDrawState);
var
   I, T, L : integer;
   S       : string;
   R       : TRect;
begin
   //
   // If it is in columns 2-n then we don't do anything.
   //
   if (ACol > 0) then exit;

   //
   // If it is in row 1 then we draw the text string 'Parameter'
   //
   if (ARow = 0) then begin
      S := 'Parameter';
      R := Rect;
      R.Top := R.Top + 4;
      T := Rect.Top + 2;
      L := Rect.Left + 4;
      with Sender as TDrawGrid do begin
         Canvas.Font.Color := Font.Color;    // same as the parent form
         DrawText(Canvas.Handle, PChar(S), Length(S), R, DT_CENTER);
         Canvas.TextRect(Rect, L, T, S);
      end;
      exit;
   end;

   //
   // Draw the bitmaps
   //
   I := ARow - 1;
   R := Rect;
   R.Left := R.Left + 20;
   R.Top  := R.Top  + 1;
   with Sender as TDrawGrid do
      ImageList1.Draw(Canvas, R.Left, R.Top, I);
end;

end.
