unit UnitExtraTags;

{$mode delphi}

interface

{
Provides functionality for form frmCustomTags
Goal is to make management of extra tags easier. It is especially useful to
visualize tag management and to delete tags. The latter is important as an
extra tag is defined for all constants/tracers/etc, but is not printed
in text editor, when the value is left on the default ''.

Typical flow of this unit is:
  1) When form to edit custom tag is opened, one instance of TExtraTagsOverview
     is created
  2) During the creation it loads all data from ExtraTagRecords in ExtraTags
     objects and sorts out the UniqueNames
  3) Afterwards ExtraTags object can be manipulated, which only changes
     UniqueNames in TExtraTagsOverview
  4) Finally, user can save changes. TExtraTagsOverview reads out ExtraTags,
     puts changes in records stored in memory and let them be saved to files
}

uses
  Classes, SysUtils, erg_types, erg_base;

type
  TExtraTagsOverview = class;
  TExtraTags = class;
  TTagRecordsArray = Array of TExtraTagsRecord;

  //Overview contains all records for constants or tracers, etc.
  TExtraTagsOverview = class
  private
    TextRecords: Array of TExtraTagsRecord;
    UniqueNames: TStringList;
    procedure SortOutUniqueNames;
  public
    ExtraTags: Array of TExtraTags;
    constructor Create(ExtraTagsRecord: Array of TExtraTagsRecord);
    function getTagNames: TStringList;
    function getUpdatedTagsRecords: TTagRecordsArray;
    function countParameters: Integer;
  end;

  //A class to manipulate data
  TExtraTags = class
  private
    CopyTagNames: TStringList;
    TagValues: TStringList;
  protected
    procedure update;
  public
    TagNames: TStringList; //as a pointer to UniqueNames in overview
    constructor Create(TagNamesData: TStringList);
    procedure setTag(TagName: String; TagValue: String);
    function delTag(TagName: String): Boolean;
    procedure changeTagName(TagName: String; NewTagName: String);
    function getTagValue(TagName: String; var TagValue: String): Boolean; overload;
    function getTagValue(index: Integer; var TagValue: String): Boolean; overload;
    function Count: Integer;
  end;


implementation

constructor TExtraTagsOverview.Create(ExtraTagsRecord: Array of TExtraTagsRecord);
var
  i_rec, j_tag: Integer;
begin
  //TextRecords refers to extra tags description in text files
  SetLength(TextRecords, Length(ExtraTagsRecord));
  for i_rec := 0 to Length(ExtraTagsRecord) -1 do
    copyExtraTagsRecord(ExtraTagsRecord[i_rec], TextRecords[i_rec]);

  //Filter out unique names
  UniqueNames := TStringList.Create;
  SortOutUniqueNames;

  //Create ExtraTag objects of all constants/tracers/etc.
  SetLength(ExtraTags, Length(ExtraTagsRecord));
  for i_rec := 0 to Length(ExtraTagsRecord) -1 do
  begin
    ExtraTags[i_rec] := TExtraTags.Create(UniqueNames);
    for j_tag := 0 to TextRecords[i_rec].TagNames.Count -1 do
      ExtraTags[i_rec].setTag(TextRecords[i_rec].TagNames[j_tag], TextRecords[i_rec].TagValues[j_tag]);
  end;
end;

procedure TExtraTagsOverview.SortOutUniqueNames;
var
  i_rec, j_tag: Integer;
begin
  UniqueNames.Clear;
  for i_rec := 0 to length(TextRecords) -1 do
    for j_tag := 0 to TextRecords[i_rec].TagNames.Count -1 do
      if UniqueNames.IndexOf(TextRecords[i_rec].Tagnames[j_tag]) < 0 then
        UniqueNames.Add(TextRecords[i_rec].Tagnames[j_tag]);
end;

function TExtraTagsOverview.getTagNames: TStringList;
var
  CopyList: TStringList;
  i_tag: Integer;
begin
  CopyList := TStringList.Create;
  for i_tag := 0 to UniqueNames.Count -1 do
    CopyList.Add(UniqueNames[i_tag]);
  EXIT(CopyList);
end;

function TExtraTagsOverview.getUpdatedTagsRecords: TTagRecordsArray;
var
  i_rec, j_tag: Integer;
begin
  //internal administration of tag records
  for i_rec := 0 to length(TextRecords) -1 do  //go through all constants/etc.
  begin
    TextRecords[i_rec].TagNames.Clear;
    TextRecords[i_rec].TagValues.Clear;

    ExtraTags[i_rec].update; //for if tags have been added/removed
    for j_tag := 0 to ExtraTags[i_rec].Count -1 do
    begin
      if ExtraTags[i_rec].TagValues[j_tag] <> '' then
      begin
        TextRecords[i_rec].TagNames.Add(ExtraTags[i_rec].TagNames[j_tag]);
        TextRecords[i_rec].TagValues.Add(ExtraTags[i_rec].TagValues[j_tag]);
      end;
    end;
  end;

  EXIT(TextRecords);
end;

function TExtraTagsOverview.countParameters: Integer;
begin
  EXIT( Length(ExtraTags) );
end;

constructor TExtraTags.Create(TagNamesData: TStringList);
var
  i_tag: Integer;
begin
  TagNames := TagNamesData;

  if not Assigned(TagNames) then
    TagNames := TStringList.Create;

  CopyTagNames := TStringList.Create;
  TagValues := TStringList.Create;

  for i_tag := 0 to TagNames.Count - 1 do
  begin
    CopyTagNames.Add(TagNames[i_tag]);
    TagValues.Add('');
  end;
end;

procedure TExtraTags.update;
var
  NewCopyNames, NewValues: TStringList;
  i_tag, i_old: Integer;
  tagExists: Boolean;
begin
  NewCopyNames := TStringList.Create;
  NewValues := TStringList.Create;

  for i_tag := 0 to TagNames.Count - 1 do
  begin
    i_old := CopyTagNames.IndexOf(TagNames[i_tag]);
    tagExists := (i_old > -1);
    if tagExists then
      NewValues.Add(TagValues[i_old])
    else
      NewValues.Add('');
    NewCopyNames.Add(TagNames[i_tag]);
  end;

  CopyTagNames.Free;
  CopyTagNames := NewCopyNames;

  TagValues.Free;
  Tagvalues := NewValues;

end;

procedure TExtraTags.setTag(TagName: String; TagValue: String);
var
  i_tag: Integer;
  tagAlreadyExists: Boolean;
begin
  update;

  i_tag := TagNames.IndexOf(TagName);
  tagAlreadyExists := (i_tag > -1);

  if (tagAlreadyExists) then
    TagValues[i_tag] := TagValue
  else
  begin
    TagNames.Add(TagName);
    CopyTagNames.Add(TagName);
    TagValues.Add(TagValue);
  end;
end;

function TExtraTags.delTag(TagName: String): Boolean;
var
  i_tag: Integer;
  tagAlreadyExists: Boolean;
begin
  update;

  i_tag := TagNames.IndexOf(TagName);
  tagAlreadyExists := (i_tag > -1);

  if (tagAlreadyExists) then
  begin
    TagNames.Delete(i_tag);
    CopyTagNames.Delete(i_tag);
    TagValues.Delete(i_tag);
    Exit(TRUE);
  end;
  EXIT(FALSE);
end;

procedure TExtraTags.changeTagName(TagName: String; NewTagName: String);
var
  i_tag: Integer;
  tagAlreadyExists, newNameAlreadyExists: Boolean;
begin
  update;

  i_tag := TagNames.IndexOf(TagName);
  tagAlreadyExists := (i_tag > -1);
  newNameAlreadyExists := (TagNames.IndexOf(NewTagName) > -1);


  if (tagAlreadyExists) and not newNameAlreadyExists then
  begin
    TagNames[i_tag] := NewTagName;
    CopyTagNames[i_tag] := NewTagName;
  end;
end;

function TExtraTags.getTagValue(TagName: String; var TagValue: String): Boolean;
var
  i_tag: Integer;
  tagExists: Boolean;
begin
  update;

  i_tag := TagNames.IndexOf(TagName);
  tagExists := (i_tag > -1);

  if tagExists then
  begin
    TagValue := TagValues[i_tag];
    exit(TRUE);
  end;
  TagValue := '';
  exit(FALSE);
end;

function TExtraTags.getTagValue(index: Integer; var TagValue: String): Boolean;
begin
  update;

  if (TagNames.Count > index) then
  begin
    tagValue := TagValues[index];
    exit(TRUE);
  end;
  TagValue := '';
  exit(FALSE);
end;

function TExtraTags.Count: Integer;
begin
  EXIT(TagNames.Count);
end;

end.

