unit SubfilesU;
   (* 
   WorldVistA Configuration Utility
   (c) 8/2008 Kevin Toppenberg
   Programmed by Kevin Toppenberg, Eddie Hagood  
   
   Family Physicians of Greeneville, PC
   1410 Tusculum Blvd, Suite 2600
   Greeneville, TN 37745
   kdtop@yahoo.com
                                                 
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  *)   

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  StrUtils, MainU,
  Dialogs, StdCtrls, ExtCtrls, Grids, ComCtrls, Buttons;

type
  TSubfileForm = class(TForm)
    Panel1: TPanel;
    TreeView: TTreeView;
    SubFileGrid: TStringGrid;
    Splitter1: TSplitter;
    SubFileLabel: TLabel;
    RightPanel: TPanel;
    ButtonPanel: TPanel;
    ApplyBtn: TBitBtn;
    RevertBtn: TBitBtn;
    DoneBtn: TBitBtn;
    LeftPanel: TPanel;
    Panel5: TPanel;
    AddBtn: TBitBtn;
    DeleteBtn: TBitBtn;
    procedure TreeViewChange(Sender: TObject; Node: TTreeNode);
    procedure TreeViewChanging(Sender: TObject; Node: TTreeNode; var AllowChange: Boolean);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure SubFileGridSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean);
    procedure AddBtnClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure RevertBtnClick(Sender: TObject);
    procedure ApplyBtnClick(Sender: TObject);
    procedure SubFileGridClick(Sender: TObject);
    procedure DoneBtnClick(Sender: TObject);
    procedure DeleteBtnClick(Sender: TObject);
  private
    { Private declarations }
    Root  : TTreeNode;
    FSubFileNum : string;
    FParentIENS : string;
    IENS_Store : TStringList;
    BlankFileInfo : TStringList;
    //FLastSelectedRow : integer;
    FLastSelectedNode : TTreeNode;
    AllSubRecords : TStringList;
    LastAddNum : integer;
    IgnoreSelections : boolean;
    CurrentSubFileData : TStringList;   
    GridInfo : TGridInfo;
    ModifiedForm : boolean;  //Used to see if current record is unsaved
    procedure CompileChanges(Grid : TStringGrid; CurrentUser,Changes : TStringList);
    procedure PostChanges(Grid : TStringGrid; IENS : string; SilentMode : boolean = false);
    procedure GetAllSubRecords(SubFileNum, ParentIENS : string; SubRecsList : TStringList);
    procedure InitTreeView;
    Procedure LoadTreeView(AllSubRecords : TStringList);
    Procedure ClearTreeView;
//    procedure ClearGrid;
    function GetIENS(Node: TTreeNode) : string;
    function StoreIENS(IENS: string) : integer;
  public
    { Public declarations }
    procedure PrepForm(subFileNum : string; ParentIENS : string);
  end;

const
  MSG_SUB_FILE = 'SubFile';

implementation

uses
  ORNet, ORFn, ORCtrls,
  Trpcb,  //needed for .ptype types
  ToolWin, SelDateTimeU, SetSelU, LookupU, PostU, FMErrorU;

{$R *.dfm}

  procedure TSubfileForm.PrepForm(subFileNum : string; ParentIENS : string);
  //Format is: FileNum^IENS^FieldNum^ExternalValue^DDInfo...
  begin
    GridInfo.Grid := SubFileGrid;
    GridInfo.Data := CurrentSubFileData;  
    GridInfo.BasicMode := false;
    GridInfo.FileNum := subFileNum;
    GridInfo.IENS := ParentIENS;
    GridInfo.ApplyBtn := ApplyBtn;
    GridInfo.RevertBtn := RevertBtn;
    MainForm.RegisterGridInfo(GridInfo);
  
    BlankFileInfo.Clear;
    IENS_Store.Clear;
    FSubFileNum := subFileNum;
    FParentIENS := ParentIENS;
    self.caption := 'Edit Sub-File Entries in Subfile #' + subFileNum;
    ClearTreeView;
    InitTreeView;
  end;

  procedure TSubfileForm.InitTreeView;
  begin
    IgnoreSelections := true;
    GetAllSubRecords(FSubFileNum,FParentIENS, AllSubRecords);
    MainForm.ClearGrid(SubFileGrid);
    LoadTreeView(AllSubRecords);
    Root.Expand(true);
    IgnoreSelections := false;
  end;
  
  Procedure TSubfileForm.LoadTreeView(AllSubRecords : TStringList);
  //Format is: FullIENS^.01Value

  var i : integer;
      dataLine : integer;
      oneEntry,value,Name,IENS : string;
  begin
    ClearTreeView;
    for i := 1 to AllSubRecords.Count-1 do begin    //0 is 1^Success
      oneEntry := AllSubRecords.Strings[i];
      IENS := Piece(oneEntry,'^',1);
      value := Piece(oneEntry,'^',2);
      dataLine := StoreIENS(IENS);
      Name := value;
      //Name := value + '^'+ IENS;
      //TreeView.Items.AddChild(Root,Name);
      TreeView.Items.AddChildObject(Root,Name,Pointer(dataLine));
    end;
  End;

  Procedure TSubfileForm.ClearTreeView;
  begin  
    TreeView.Items.Clear;
    IENS_Store.Clear;
    Root := TreeView.Items.Add(nil,'Subrecords');
    //if Root.HasChildren then Root.DeleteChildren;
//    ClearGrid;
    MainForm.ClearGrid(SubFileGrid);
    
  end;
  

  procedure TSubfileForm.TreeViewChanging(Sender: TObject; Node: TTreeNode;
    var AllowChange: Boolean);
  begin
    ApplyBtnClick(self);
  end;
  
  procedure TSubfileForm.TreeViewChange(Sender: TObject; Node: TTreeNode);
  begin
    FLastSelectedNode := Node;
    GridInfo.IENS := GetIENS(Node);
    MainForm.GetOneRecord(GridInfo.FileNum, GridInfo.IENS, GridInfo.Data, BlankFileInfo);
    MainForm.LoadAnyGridFromInfo(GridInfo);
  end;

  function TSubfileForm.GetIENS(Node: TTreeNode) : string;
  var dataLine : integer;
  begin
    if Node= nil then exit;
    dataLine := integer(Node.Data);
    if dataLine < IENS_Store.Count then begin
      result := IENS_Store.Strings[dataLine];
    end else result := '';
  end;

  function TSubfileForm.StoreIENS(IENS: string) : integer;
  begin
    result := IENS_Store.Add(IENS);
  end;
  
  
  procedure TSubfileForm.GetAllSubRecords(SubFileNum, ParentIENS : string; SubRecsList : TStringList);
  var  cmd,RPCResult : string;
  begin
    SubRecsList.Clear;
    RPCBrokerV.remoteprocedure := 'TMG CHANNEL';
    RPCBrokerV.Param[0].Value := '.X';  // not used
    RPCBrokerV.param[0].ptype := list;
    cmd := 'GET SUB RECS LIST' + '^' + SubFileNum + '^' + ParentIENS;
    RPCBrokerV.Param[0].Mult['"REQUEST"'] := cmd;
    RPCBrokerV.Call;
    RPCResult := RPCBrokerV.Results[0];    //returns:  error: -1;  success=1
    if piece(RPCResult,'^',1)='-1' then begin
      FMErrorForm.Memo.Lines.Assign(RPCBrokerV.Results);
      FMErrorForm.PrepMessage;
      FMErrorForm.ShowModal;
    end else begin
      SubRecsList.Assign(RPCBrokerV.Results);
    end;
  end;
  
  procedure TSubfileForm.FormCreate(Sender: TObject);
  begin
    AllSubRecords := TStringList.Create;
    BlankFileInfo := TStringList.Create;
    IENS_Store := TStringList.Create;
    CurrentSubFileData := TStringList.Create;  
    GridInfo := TGridInfo.Create;

    ModifiedForm := False;   
  end;

  procedure TSubfileForm.FormDestroy(Sender: TObject);
  begin
    AllSubRecords.Free;
    BlankFileInfo.Free;
    IENS_Store.Free;
    CurrentSubFileData.Free;
    MainForm.UnRegisterGridInfo(GridInfo);
    GridInfo.Free;
  end;

  procedure TSubfileForm.SubFileGridSelectCell(Sender: TObject; ACol, ARow: Integer; var CanSelect: Boolean); 
  begin
    GridInfo.Message := MSG_SUB_FILE;
    MainForm.GridSelectCell(Sender, ACol, ARow, CanSelect);
  end;

  
  procedure TSubfileForm.AddBtnClick(Sender: TObject);
  var Name,IENS : string;
      tempNode : TTreeNode;
      dataLine : integer;
  begin
    RevertBtn.Enabled := True;
    ApplyBtn.Enabled := True;
    ModifiedForm := True;
    Inc(LastAddNum);
    IENS := '+' + IntToStr(LastAddNum) + ',' + FParentIENS;
    Name := '<NEW>';
    dataLine := StoreIENS(IENS);
    tempNode := TreeView.Items.AddChildObject(Root,Name,Pointer(dataLine));
    MainForm.GetOneRecord(FSubfileNum,IENS,CurrentSubFileData, BlankFileInfo);
    Root.expand(true);
    TreeView.Select(tempNode);
  end;

  procedure TSubfileForm.FormShow(Sender: TObject);
  begin
    LastAddNum := 0;
  end;

  procedure TSubfileForm.RevertBtnClick(Sender: TObject);
  begin
    ModifiedForm := False;
    MainForm.LoadAnyGridFromInfo(GridInfo);
  end;

  procedure TSubfileForm.ApplyBtnClick(Sender: TObject);
  var  IENS : string;
  begin
    ModifiedForm := False;
    IENS := GetIENS(FLastSelectedNode);
    PostChanges(SubFileGrid,IENS);
  end;

  procedure TSubfileForm.SubFileGridClick(Sender: TObject);
  //var sel : TGridRect;
  //    temp : boolean;
  begin
    ModifiedForm := True;
    RevertBtn.Enabled := True;
    ApplyBtn.Enabled := True;
    {//kt Eddie, what was the purpose of this?  Causes unexpected click
          when returning from sub-sub file...
    Sel := SubFileGrid.Selection;
    if Sel.Top <> FLastSelectedRow then begin
      SubFileGridSelectCell(SubFileGrid, Sel.Left, Sel.Top, temp);    
    end;
    }
  end;

  
  procedure TSubfileForm.PostChanges(Grid : TStringGrid; IENS : string; SilentMode : boolean);
  
    function NewIENS(oldIENS : string; PostResults : TStringList) : string;
    //format of PostResults is:  oldIENS^newIENS
    var i : integer;
        oneEntry : string;
        newIENS,
        parentIENS : string;
    begin
      result := '';
      newIENS := piece(oldIENS,',',1);   // +1,123, --> +1
      parentIENS := MidStr(oldIENS,length(newIENS)+1,99);
      newIENS := piece(newIENS,'+',2);   // +1 --> 1
      for i := 1 to PostResults.Count-1 do begin  //0 is 1^Success
        oneEntry := PostResults.Strings[i];
        if piece(oneEntry,'^',1) <> newIENS then continue;
        result := piece(oneEntry,'^',2) + parentIENS;
        break;                
      end;
    end;
    
  var Changes : TStringList;
      PostResult : TModalResult;
  begin
    Changes := TStringList.Create;
    CompileChanges(Grid,CurrentSubFileData,Changes);
    if Changes.Count>0 then begin      
      if SilentMode = false then begin
        PostForm.PrepForm(Changes);
        PostResult := PostForm.ShowModal;
      end else begin
        PostResult := PostForm.SilentPost(Changes);
      end;  
      if PostResult in [mrOK,mrNone] then begin
        RevertBtn.Enabled := false;
        ApplyBtn.Enabled := false;
        if PostResult = mrOK then InitTreeView;
      end else if PostResult = mrNo then begin  //mrNo is signal of post Error
        // show error...
      end;
    end;  
    Changes.Free;
  end;

  procedure TSubfileForm.CompileChanges(Grid : TStringGrid; CurrentUser,Changes : TStringList);
  //Output format:
  // FileNum^IENS^FieldNum^FieldName^newValue^oldValue

  var row : integer;
      Entry : tFileEntry;
      oneEntry : string;
  begin
    for row := 1 to Grid.RowCount-1 do begin
      Entry := MainForm.GetLineInfo(Grid,CurrentSubFileData,row);
      if Entry.oldValue <> Entry.newValue then begin
        if (Entry.newValue <> CLICK_FOR_SUBS) and
          (Entry.newValue <> COMPUTED_FIELD) and
          (Entry.newValue <> CLICK_TO_EDIT) then begin   
          oneEntry := Entry.FileNum + '^' + Entry.IENS + '^' + Entry.Field + '^' + Entry.FieldName;
          oneEntry := oneEntry + '^' + Entry.newValue + '^' + Entry.oldValue;
          Changes.Add(oneEntry);
        end;  
      end;
    end;
  end;

  
  procedure TSubfileForm.DoneBtnClick(Sender: TObject);
  var Changes : TStringList;
  begin
    if ModifiedForm = True then begin
      Changes := TStringList.Create;
      CompileChanges(SubFileGrid,CurrentSubFileData,Changes);
      if Changes.Count>0 then begin
        ApplyBtnClick(self);
        ModalResult := mrNo;
      end else begin
        ModalResult := mrNo;    
      end;    
    end else begin
      ModalResult := mrNo;      
    end;   
  end;

  procedure TSubfileForm.DeleteBtnClick(Sender: TObject);
  var  IENS : string;
       row,ARow : integer;
       response: integer;
  begin
    //The rows can be rearranged, so row 1 will not reliably hold
    //the .01 field.  And if there is a .001 field, it might be
    //shown above the .01 field etc.
    response := messagedlg('Are you sure you want to delete ' + TreeView.Selected.Text,mtWarning,[mbYes,mbNo],0);
    if response = mrYes then begin
      ARow := 0;
      for row := 1 to SubFileGrid.RowCount-1 do begin
        if SubFileGrid.Cells[0,row]='.01' then begin
          ARow := row; break;
        end;  
      end;      
      if ARow > 0 then begin
        SubFileGrid.Cells[2,ARow] := '@';   //columns can't be rearranged (for now)
        IENS := GetIENS(FLastSelectedNode);
     //I wonder what Fileman will say if the .01 field has '@'
     //  and there are other fields with changes also.  I might
     //  complain about making changes and a deletion at the same
     //  time.  Perhaps we ought to have a custom delete function
     //  that deletes everything from the CompiledChanges except for
     //  the .01 record.  Let's wait and see if this is a problem or
     //  not first.
        PostChanges(SubfileGrid,IENS,true);  //<-- true = SilentMode
      end else begin
       MessageDlg('Unable to find row containing .01 field',mtError,[mbOK],0);
      end;  
    end;  
  end;

  

end.

