{ ------------------------------------------------------------- }
{ Purpose : NextGrid 6                                          }
{ By      : Bojan Nikolic                                       }
{ For     : BergSoft                                            }
{ ------------------------------------------------------------- }
{ Copyright (C) BergSoft.net 2016. All Rights Reserved.         }
{ ------------------------------------------------------------- }
unit NxGridSerialization6;

interface

uses
  { Standard }
  Classes, Types, Messages, Windows, Graphics, Clipbrd, StdCtrls, TypInfo,
  {$IFDEF UI_TYPES}UITypes,{$ENDIF}
  { Additional }
  Controls, ExtCtrls, Forms, IniFiles, Registry,
  { Next Standard 6 }
  NxIntf6, NxClasses6, NxTypes6, NxConsts6,
  { NextGrid 6 }
  NxColumns6, NxCells6;

const
  vrSaveGridToXML = '1.2';

type
  { TNxGridSerialization }

  TNxGridSerialization = class
  private
    FEncoding: TNxEncoding;
    FMultiLineChar: WideChar;
    FOwner: TControl;
    FSeparatorChar: WideChar;
    FSuffix: WideString;
  protected
    procedure ReadFromINI(Name: WideString; IniFile: TCustomIniFile; ID: string);
    procedure WriteToINI(Name: WideString; IniFile: TCustomIniFile; ID: string);
  public
    constructor Create(AOwner: TControl);
    { Load Methods }
    procedure LoadFromCSV(const FileName: WideString);
    procedure LoadFromINI(const FileName: WideString);
    procedure LoadFromRegistry(const KeyName: WideString; RootKey: HKEY = HKEY_CURRENT_USER);
    procedure LoadFromXML(const FileName: WideString; Kind: TNxDataKind = dtLayout);
    { Save Methods }
    procedure SaveToCSV(const FileName: WideString);
    procedure SaveToINI(const FileName: WideString);
    procedure SaveToJSON(const FileName: WideString);
    procedure SaveToRegistry(const KeyName: WideString; RootKey: HKEY = HKEY_CURRENT_USER);
    procedure SaveToXML(const FileName: WideString; Kind: TNxDataKind = dtLayout);
    { Properties }
    property Encoding: TNxEncoding read FEncoding write FEncoding;
    property MultiLineChar: WideChar read FMultiLineChar write FMultiLineChar;
    property Owner: TControl read FOwner;
    property SeparatorChar: WideChar read FSeparatorChar write FSeparatorChar;
    property Suffix: WideString read FSuffix write FSuffix;
  end;

implementation

uses
  { Standard }
  SysUtils, Dialogs,
  { Next Standard }
  NxStdCtrls6, NxXMLDoc6,
  { Next Grid }
  NxCustomGrid6, NxVirtualGrid6;

{ TNxGridSerialization }

constructor TNxGridSerialization.Create(AOwner: TControl);
begin
  FOwner := AOwner;
  FEncoding := ekUnicode;
  FSeparatorChar := ',';
  FSuffix := '';
  FMultiLineChar := '|';
end;

procedure TNxGridSerialization.LoadFromCSV(const FileName: WideString);
var
  Doc: TNxCSVDocument6;
begin
  { Init. object }
  Doc := TNxCSVDocument6.Create(nil);

  try
    Doc.SeparatorChar := SeparatorChar;
    Doc.MultiLineChar := MultiLineChar;

    with Owner as TNxCustomGrid6 do
    begin
      Doc.ColCount := Columns.Count;
      Doc.RowCount := RowCount;
      Doc.OnSetValue := DoCSVSetValue;
      Doc.OnAddRow := DoAddRow;
    end;

    Doc.LoadFromCSV(FileName, Encoding);
  finally
    { Release }
    Doc.Free;
  end;
end;

procedure TNxGridSerialization.ReadFromINI(Name: WideString;
  IniFile: TCustomIniFile; ID: string);
var
  i, Position: Integer;
begin
  with Owner as TNxCustomGrid6 do
  begin
    for i := 0 to Pred(Columns.Count) do
    begin
      if IniFile.ReadString('Columns Sort Kind' + ID, 'Column' + IntToStr(i), 'Ascending') = 'Ascending'
        then Columns[i].SortKind := skAscending
        else Columns[i].SortKind := skDescending;

      Columns[i].Visible := IniFile.ReadBool('Visible Columns' + ID, 'Column' + IntToStr(i), True);
      Columns[i].Sorted := IniFile.ReadBool('Sorted' + ID, 'Column' + IntToStr(i), False);
      Columns[i].Width := IniFile.ReadInteger('Columns Sizes' + ID, 'Column' + IntToStr(i), Columns[i].Width);

      Position := IniFile.ReadInteger('Positions' + ID, 'Column' + IntToStr(i), i);
      Columns[i].Position := Position;
    end;
  end;
end;

procedure TNxGridSerialization.LoadFromINI(const FileName: WideString);
var
  IniFile: TIniFile;
begin
  try
    IniFile := TIniFile.Create(FileName);
    ReadFromINI(FileName, IniFile, Suffix);
  finally
    FreeAndNil(IniFile);
  end;
end;

procedure TNxGridSerialization.LoadFromRegistry(const KeyName: WideString;
  RootKey: HKEY);
var
  IniFile: TRegistryIniFile;
begin
  try
    IniFile := TRegistryIniFile.Create(KeyName);
    IniFile.RegIniFile.RootKey := RootKey;
    ReadFromINI(KeyName, IniFile, Suffix);
  finally
    FreeAndNil(IniFile);
  end;
end;

procedure TNxGridSerialization.LoadFromXML(const FileName: WideString;
  Kind: TNxDataKind = dtLayout);
var
  XMLDoc: TNxXMLDocument6;
  XMLNode: INxXMLNode;
  Index: Integer;
  Column: TNxColumn6;

  procedure ReadCells(Node: INxXMLNode; CellList: TNxCellsList);
  var
    i: Integer;
    CellNode: INxXMLNode;
  begin
    for i := 0 to Pred(Node.ChildCount) do
    begin
      if Node.ChildNodes[i].NodeName = 'Cell' then
      begin
        CellNode := Node.ChildNodes[i];
        XMLNodeToObject(CellNode, CellList[i].ToObject);
      end;
    end;
  end;

  procedure ReadNode(Node: INxXMLNode; Row: INxRow);
  var
    i: Integer;
    NewRow: INxRow;
  begin
    for i := 0 to Node.ChildCount - 1 do
    begin

      if Node.ChildNodes.Nodes[i].NodeName = 'Row' then
      begin
        if Row = nil then
          NewRow := TNxVirtualGrid6(Owner).AddRow
        else
          NewRow := Row.AddChildRow(1);

        ReadCells(Node.ChildNodes.Nodes[i], (NewRow as INxCellsRow).Cells);

        if Node.ChildNode[i].HasChildren then
          ReadNode(Node.ChildNodes.Nodes[i], NewRow);
      end;

    end;

  end;

begin
  XMLDoc := TNxXMLDocument6.Create(nil);

  try
    XMLDoc.LoadFromFile(FileName);

    case Kind of
      dtLayout:
      begin
        XMLNode := XMLDoc.DocumentElement.ChildNodes.Nodes['Columns'];

        if Assigned(XMLNode) then
        begin
          for Index := 0 to XMLNode.ChildCount - 1 do
          begin

            Column := TNxCustomGrid6(FOwner).Columns[Index];
            XMLNodeToObject(XMLNode.ChildNodes[Index], Column);

          end;
        end;
      end;

      dtContent:
      begin

        if XMLDoc.DocumentElement.HasChildren then
        begin
          { `Rows` Node }
          ReadNode(XMLDoc.DocumentElement.ChildNodes.Nodes[0], nil);
        end;

      end;

    end;

  finally
    XMLDoc.Free;
  end;

end;

procedure TNxGridSerialization.SaveToCSV(const FileName: WideString);
var
  Doc: TNxCSVDocument6;
begin
  { Init. object }
  Doc := TNxCSVDocument6.Create(nil);

  try
    Doc.SeparatorChar := SeparatorChar;
    Doc.MultiLineChar := MultiLineChar;

    with Owner as TNxCustomGrid6 do
    begin
      Doc.ColCount := Columns.Count;
      Doc.RowCount := RowCount;
      Doc.OnGetValue := DoCSVGetValue;
    end;

    Doc.SaveToCSV(FileName, Encoding);
  finally
    { Release }
    Doc.Free;
  end;
end;

procedure TNxGridSerialization.SaveToINI(const FileName: WideString);
var
  IniFile: TIniFile;
begin
  try
    IniFile := TIniFile.Create(FileName);
    WriteToINI(FileName, IniFile, Suffix);
  finally
    FreeAndNil(IniFile);
  end;
end;

{$HINTS OFF}
procedure TNxGridSerialization.SaveToJSON(const FileName: WideString);
var
  i: Integer;
  JSON: TNxJSON6;
  JSONObject: TNxJSONObject;
  JSONArray: TNxJSONArray;
begin
  JSON := TNxJSON6.Create;
  try
    with Owner as TNxCustomGrid6 do
    begin
      { Root object }
      JSONObject := JSON.AddObject;

      { Top properties }
      JSONObject.AddProperty(Columns, 'Count', 'ColCount');

//      JSONArray := JSONObject.AddArray('Columns');

      for i := 0 to Pred(ColCount) do
      begin
      end;
    end;
  finally
    JSON.Free;
  end;
end;
{$HINTS ON}

procedure TNxGridSerialization.SaveToRegistry(const KeyName: WideString;
  RootKey: HKEY);
var
  IniFile: TRegistryIniFile;
begin
  try
    IniFile := TRegistryIniFile.Create(KeyName);
    IniFile.RegIniFile.RootKey := RootKey;
    WriteToINI(KeyName, IniFile, Suffix);
  finally
    FreeAndNil(IniFile);
  end;
end;

procedure TNxGridSerialization.SaveToXML(const FileName: WideString;
  Kind: TNxDataKind);
var
  XMLDoc: TNxXMLDocument6;
  GridNode, ColsNode, ANode, CellsNode, RowNode: INxXMLNode;
  i: Integer;

  procedure WriteCells(Node: INxXMLNode; Cells: TNxCellsList);
  var
    i: Integer;
    CellNode: INxXMLNode;
    Instance: TObject;
  begin
    for i := 0 to Pred(Cells.Count) do
    begin
      CellNode := Node.ChildNodes.Add('Cell');
      Instance := Cells[i].ToObject;
      LoadProperties(Instance, CellNode);
    end;
  end;

  procedure WriteChildren(Node: INxXMLNode; ParentRow: INxRow);
  var
    r: Integer;
    ChildNode: INxXMLNode;
  begin
    for r := 0 to Pred(ParentRow.ChildRowCount) do
    begin
      ChildNode := TNxXMLNode.Create('Row');
      Node.ChildNodes.Add(ChildNode);

      WriteCells(ChildNode, (ParentRow.ChildRow[r] as INxCellsRow).Cells);

      if ParentRow.ChildRow[r].HasChildren then
      begin
        WriteChildren(ChildNode, ParentRow.ChildRow[R]);
      end;
    end;
  end;

begin
  { Init. object }
  XMLDoc := TNxXMLDocument6.Create(nil);

  try
    XMLDoc.DocumentElement := TNxXMLNode.Create(Owner.ClassName);
    XMLDoc.DocumentElement.Attributes['Type'] := GetEnumName(TypeInfo(TNxDataKind), Ord(Kind));
    XMLDoc.DocumentElement.Attributes['Version'] := vrSaveGridToXML;

    case Kind of

      dtLayout:
      begin
        GridNode := XMLDoc.ObjectToNode(Owner);

        XMLDoc.DocumentElement.ChildNodes.Add(GridNode);

        ColsNode := TNxXMLNode.Create('Columns');

        XMLDoc.DocumentElement.ChildNodes.Add(ColsNode);

        with Owner as TNxCustomGrid6 do
          for i := 0 to Pred(Columns.Count) do
          begin
            ANode := XMLDoc.ObjectToNode(TObject(Columns[i]));
            ColsNode.ChildNodes.Add(ANode);
          end;

      end;

      dtContent:
      begin
        CellsNode := TNxXMLNode.Create('Rows');

        XMLDoc.DocumentElement.ChildNodes.Add(CellsNode);

        for i := 0 to TNxCustomGrid6(Owner).ChildRowCount - 1 do
        begin
          RowNode := TNxXMLNode.Create('Row');

          CellsNode.ChildNodes.Add(RowNode);

          WriteCells(RowNode, (TNxCustomGrid6(Owner).ChildRow[i] as  INxCellsRow).Cells);

          if TNxCustomGrid6(Owner).ChildRow[i].HasChildren then
          begin
            WriteChildren(RowNode, TNxCustomGrid6(Owner).ChildRow[i]);
          end;

        end;
      end;
    end;

    XMLDoc.SaveToFile(FileName);
  finally
    XMLDoc.Free;
  end;
end;

procedure TNxGridSerialization.WriteToINI(Name: WideString;
  IniFile: TCustomIniFile; ID: string);
var
  i: Integer;
begin
  with Owner as TNxCustomGrid6 do
  begin
    IniFile.WriteInteger('Common' + ID, 'ColumnsCount', Columns.Count);
    for i := 0 to Pred(Columns.Count) do
    begin
      if Columns[i].SortKind = skAscending
        then IniFile.WriteString('Columns Sort Kind' + ID, 'Column' + IntToStr(i), 'Ascending')
        else IniFile.WriteString('Columns Sort Kind' + ID, 'Column' + IntToStr(i), 'Descending');
      IniFile.WriteBool('Visible Columns' + ID, 'Column' + IntToStr(i), Columns[i].Visible);
      IniFile.WriteBool('Sorted' + ID, 'Column' + IntToStr(i), Columns[i].Sorted);
      IniFile.WriteInteger('Positions' + ID, 'Column' + IntToStr(i), Columns[i].Position);
      IniFile.WriteInteger('Columns Sizes' + ID, 'Column' + IntToStr(i), Columns[i].Width);
    end;
  end;

  IniFile.UpdateFile;
end;

end.
