unit Keyman.Developer.System.ProjectTemplate;

interface

uses
  System.SysUtils,

  kpsfile,
  PackageInfo,
  UKeymanTargets,
  utilfiletypes;

type
  EProjectTemplate = class(Exception);

  TProjectTemplate = class
  private
    FID: string;
    FName: string;
    FBasePath: string;
    FAuthor: string;
    FCopyright: string;
    FVersion: string;
    FBCP47Tags: string;
    FProjectType: TKeymanProjectType;
    FTargets: TKeymanTargets;
    FFullCopyright: string;

  protected
    const
      SFolder_Source = 'source';
      SFolder_Build = 'build';

      SFile_ReadmeMD = 'README.md';     // in root
      SFile_HistoryMD = 'HISTORY.md';   // in root
      SFile_LicenseMD = 'LICENSE.md';   // in root
      SFile_WelcomeHTM = 'welcome.htm'; // in source/
      SFile_ReadmeHTM = 'readme.htm';   // in source/

    function GetFilename(Base: string): string;

    function DataPath: string; virtual;

    function GetPackageFilename: string; virtual;
    function GetProjectFilename: string; virtual;

    procedure WriteRepositoryMetadata;
    procedure WriteDocumentation;
    procedure Transform(const SourceFile: string; DestFile: string = '');
    procedure SetPackageLanguageMetadata(kps: TKpsFile; languages: TPackageKeyboardLanguageList);

    property BasePath: string read FBasePath;
    property ID: string read FID;
  public
    constructor Create(const BasePath, ID: string; ATargets: TKeymanTargets);

    procedure Generate; virtual; abstract;

    property ProjectType: TKeymanProjectType read FProjectType;

    property Targets: TKeymanTargets read FTargets;

    property Name: string read FName write FName;
    property Copyright: string read FCopyright write FCopyright;
    property FullCopyright: string read FFullCopyright write FFullCopyright;
    property Author: string read FAuthor write FAuthor;
    property Version: string read FVersion write FVersion;
    property BCP47Tags: string read FBCP47Tags write FBCP47Tags;

    property ProjectFilename: string read GetProjectFilename;
    property PackageFilename: string read GetPackageFilename;
  end;

implementation

uses
  System.Classes,
  System.StrUtils,

  BCP47Tag,
  Keyman.System.CanonicalLanguageCodeUtils,
  Keyman.System.LanguageCodeUtils,
  KeyboardParser,
  KeymanVersion,
  Keyman.Developer.System.Project.kpsProjectFile,
  Keyman.Developer.System.Project.ProjectFile,
  Keyman.Developer.System.Project.ProjectFiles,
  RedistFiles,
  TouchLayout,
  utilstr,
  VisualKeyboard;

{ TProjectTemplate }

constructor TProjectTemplate.Create(const BasePath, ID: string; ATargets: TKeymanTargets);
begin
  inherited Create;
  FBasePath := IncludeTrailingPathDelimiter(ExpandFileName(BasePath));
  FID := ID;
  FVersion := '1.0';
  FTargets := ATargets;
end;

function TProjectTemplate.GetFilename(Base: string): string;
begin
  if Base = '' then
    Result := ''
  else if ExtractFileExt(Base) = Base then
    Result := FBasePath + FID + '\' + SFolder_Source + '\' + FID + Base
  else
    Result := FBasePath + FID + '\' + SFolder_Source + '\' + Base;
end;

function TProjectTemplate.GetPackageFilename: string;
begin
  Result := GetFilename(Ext_PackageSource);
end;

function TProjectTemplate.GetProjectFilename: string;
begin
  // Project file goes in parent folder
  Result := FBasePath + FID + '\' + FID + Ext_ProjectSource;
end;

procedure TProjectTemplate.WriteDocumentation;
begin
  // Write readme.htm and welcome.htm
  Transform(SFolder_Source + '\' + SFile_WelcomeHTM);
  Transform(SFolder_Source + '\' + SFile_ReadmeHTM);
end;

procedure TProjectTemplate.SetPackageLanguageMetadata(kps: TKpsFile; languages: TPackageKeyboardLanguageList);
var
  tag, tags, s: string;
  bcp47tag: TBCP47Tag;
  pkl: TPackageKeyboardLanguage;
begin
  tags := Trim(FBCP47Tags);
  tag := StrToken(tags, ' ');
  while tag <> '' do
  begin
    pkl := TPackageKeyboardLanguage.Create(kps);

    bcp47tag := TBCP47Tag.Create(tag);
    try
      //bcp47tag.Canonicalize; #2080
      pkl.ID := bcp47tag.Tag;

      if not TLanguageCodeUtils.BCP47Languages.TryGetValue(bcp47tag.Language, pkl.Name) then
      begin
        pkl.Name := bcp47tag.Language;
      end;

      if bcp47tag.Script <> '' then
      begin
        if TLanguageCodeUtils.BCP47Languages.TryGetValue(bcp47tag.Script, s) then
          pkl.Name := pkl.Name + ' ('+s+')';
      end;
    finally
      bcp47tag.Free;
    end;

    languages.Add(pkl);

    tag := StrToken(tags, ' ');
  end;
end;

procedure TProjectTemplate.WriteRepositoryMetadata;
begin
  Transform(SFile_HistoryMD);
  Transform(SFile_LicenseMD);
  Transform(SFile_ReadmeMD);
end;

function TProjectTemplate.DataPath: string;
begin
  Result := GetRedistProjectTemplatePath;
end;

procedure TProjectTemplate.Transform(const SourceFile: string; DestFile: string = '');
var
  s: string;
  ss: TStringStream;

  function GetLanguageTagListForKeyboardInfo: string;
  var
    tags, tag: string;
    bcp47tag: TBCP47Tag;
  begin
    // We won't worry about line breaks...
    tags := Trim(FBCP47Tags);
    tag := StrToken(tags, ' ');
    Result := '';
    while tag <> '' do
    begin
      if Result <> '' then
        Result := Result + ', ';

      bcp47tag := TBCP47Tag.Create(tag);
      try
        bcp47tag.Tag := TCanonicalLanguageCodeUtils.FindBestTag(bcp47tag.Tag, False, False);
        Result := Result + '"' + bcp47tag.Tag + '"';
      finally
        bcp47tag.Free;
      end;
      tag := StrToken(tags, ' ');
    end;
  end;

  function GetPlatformsDotListForReadme: string;
  var
    kt: TKeymanTarget;
  begin
    Result := '';
    for kt := Low(TKeymanTarget) to High(TKeymanTarget) do
    begin
      if kt = ktAny then
        Continue;
      if (kt in FTargets) or (ktAny in FTargets) then
        Result := Result + ' * ' + SKeymanTargetNames[kt] + #13#10;
    end;
  end;

begin
  if DestFile = '' then
    DestFile := SourceFile;

  ss := TStringStream.Create('', TEncoding.UTF8);
  try
    ss.LoadFromFile(DataPath + SourceFile);
    s := ss.DataString;
  finally
    ss.Free;
  end;

  s := ReplaceStr(s, '$NAME', FName);
  s := ReplaceStr(s, '$VERSION', FVersion);
  s := ReplaceStr(s, '$COPYRIGHT', FCopyright);
  s := ReplaceStr(s, '$FULLCOPYRIGHT', FFullCopyright);
  s := ReplaceStr(s, '$AUTHOR', FAuthor);
  s := ReplaceStr(s, '$DATE', FormatDateTime('yyyy-mm-dd', Now));
  if Pos('$LANGUAGES_KEYBOARD_INFO', s) > 0 then
    s := ReplaceStr(s, '$LANGUAGES_KEYBOARD_INFO', GetLanguageTagListForKeyboardInfo);
  if Pos('$PLATFORMS_DOTLIST_README', s) > 0 then
    s := ReplaceStr(s, '$PLATFORMS_DOTLIST_README', GetPlatformsDotListForReadme);

  ss := TStringStream.Create(s, TEncoding.UTF8);
  try
    ss.SaveToFile(FBasePath + FID + '\' + DestFile);
  finally
    ss.Free;
  end;
end;

end.
