unit SourceCodeRepMain;

{$I ../../../Synopse.inc}

interface

uses
  {$IFNDEF FPC}
  Windows,
  {$ELSE}
  LCLIntf,
  LCLType,
  LMessages,
  {$ENDIF}
  SynCommons,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Graphics,
  Controls,
  Forms,
  Dialogs,
  StdCtrls,
  Clipbrd;

const
  VERSION = '1.18';

type

  { TMainForm }

  TMainForm = class(TForm)
    mmoStatus: TMemo;
    lbl1: TLabel;
    lbl2: TLabel;
    mmoDescription: TMemo;
    btnFossilSynch: TButton;
    btnFullSynch: TButton;
    btnGitSynch: TButton;
    btnRefreshStatus: TButton;
    btnGitShell: TButton;
    btnFossilShell: TButton;
    btnTests: TButton;
    btnCopyLink: TButton;
    btnGitAll: TButton;
    btnSynProject: TButton;
    btnSynPdf: TButton;
    btnDMustache: TButton;
    btnLVCL: TButton;
    chkCopyLink: TCheckBox;
    chkFossilPush: TCheckBox;
    chkFossilPull: TCheckBox;
    procedure FormCreate(Sender: TObject);
    procedure btnFullSynchClick(Sender: TObject);
    procedure btnFossilSynchClick(Sender: TObject);
    procedure btnGitSynchClick(Sender: TObject);
    procedure btnRefreshStatusClick(Sender: TObject);
    procedure btnGitShellClick(Sender: TObject);
    procedure btnFossilShellClick(Sender: TObject);
    procedure btnTestsClick(Sender: TObject);
    procedure btnCopyLinkClick(Sender: TObject);
  private
    fBatPath: TFileName;
    fFossilRepository: TFileName;
    fDevPath: TFileName;
    fGitExe: TFileName;
    fGitRepository: TFileName;
    function Exec(const folder, exe, arg1, arg2, arg3, arg4, arg5: TFileName;
      exeisshell: boolean=true; wait: boolean=true): boolean;
    procedure ReadStatus;
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;

implementation

uses
  mORMotService; // for cross-platform RunProcess()

{$IFNDEF FPC}
  {$R *.dfm}
{$ELSE}
  {$R *.lfm}
{$ENDIF}

{$ifdef MSWINDOWS}
  {$R ..\..\..\vista.RES} // includes Win10 manifest - use .RES for linux cross-compilation
const
  SHELL = '.bat';
  SHELLEXE = 'cmd.exe';
  GITDEF = 'git.exe';
  REPFOSSIL = 'd:\dev\fossil\lib';
  REPLIB = 'd:\dev\lib';
  REPGITHUB = 'd:\dev\github\';
{$else}
const
  SHELL = '.sh';
  GITDEF = '/usr/bin/git';
var
  REPFOSSIL: TFileName;
  REPLIB: TFileName;
  REPGITHUB: TFileName;
{$endif}

function TMainForm.Exec(const folder, exe, arg1, arg2, arg3, arg4, arg5: TFileName;
  exeisshell, wait: boolean): boolean;
var
  bak, path: TFileName;
  {$ifdef MSWINDOWS}
  function q(const a: TFileName): TFileName;
  begin
    result := '"' + a + '"'; // paranoid quote for safety
  end;
  {$else}
  type q = TFileName;
  {$endif}
begin
  if folder <> '' then begin
    bak := GetCurrentDir;
    SetCurrentDir(folder);
  end;
  if exeisshell then
    path := fBatPath + exe + SHELL
  else
    path := exe;
  screen.Cursor := crHourGlass;
  try
    result := RunProcess(path, q(arg1), wait, q(arg2), q(arg3), q(arg4), q(arg5)) = 0;
  finally
    if bak <> '' then
      SetCurrentDir(bak);
    screen.Cursor := crDefault;
  end;
end;

procedure TMainForm.ReadStatus;
var
  statusfile: TFileName;
  status: RawUTF8;
begin
  statusfile := fBatPath + 'status.txt';
  DeleteFile(statusfile);
  if not Exec(fFossilRepository, 'FossilStatus', statusfile, '', '', '', '') then
    status := 'error executing FossilStatus script'
  else
    status := StringFromFile(statusfile);
  {$ifdef MSWINDOWS}
  if PosEx(#13#10, status) = 0 then
    status := StringReplaceAll(status, #10, #13#10);
  {$endif}
  mmoStatus.Text := UTF8ToString(status);
end;

procedure TMainForm.FormCreate(Sender: TObject);
{$ifdef MSWINDOWS}
begin
{$else}
var
  dev: TFileName;
begin
  dev := GetSystemPath(spUserDocuments) + 'dev/';
  REPFOSSIL := dev + 'fossil/lib';
  REPLIB := dev + 'lib';
  REPGITHUB := dev + 'github/';
  btnFossilShell.caption := 'Fossil diff';
  btnGitShell.caption := 'Git diff';
{$endif MSWINDOWS}
  fBatPath := ExeVersion.ProgramFilePath;
  if not FileExists(fBatPath + 'FossilStatus' + SHELL) then // from exe sub-folder?
    fBatPath := ExtractFilePath(ExcludeTrailingPathDelimiter(fBatPath));
  if not FileExists(fBatPath + 'FossilStatus' + SHELL) then // from exe sub-folder?
    fBatPath := ExtractFilePath(ExcludeTrailingPathDelimiter(fBatPath));
  if not FileExists(fBatPath + 'FossilStatus' + SHELL) then
    ShowMessage('Missing *' + SHELL +' files');
  fFossilRepository := GetEnvironmentVariable('SYN_FOSSILREPO_PATH');
  if fFossilRepository = '' then
    fFossilRepository := REPFOSSIL;
  fDevPath := GetEnvironmentVariable('SYN_DEVPATH');
  if fDevPath = '' then
    if DirectoryExists(REPLIB) then
      fDevPath := REPLIB else
      fDevPath := fFossilRepository;
  fGitExe := GetEnvironmentVariable('GIT_PATH');
  if fGitExe = '' then begin
    {$ifdef MSWINDOWS}
    fGitExe := 'c:\Program Files (x86)\Git\bin\git.exe';
    if not FileExists(fGitExe) then
    {$endif}
      fGitExe := GITDEF;
  end;
  fGitRepository := GetEnvironmentVariable('SYN_GITREPO_PATH');
  if fGitRepository = '' then
    fGitRepository := REPGITHUB + 'mORMot';
  if not DirectoryExists(fFossilRepository) then begin
    ShowMessage('Please set Fossil Repository Name or environment variable SYN_FOSSILREPO_PATH');
    Close;
  end else if not DirectoryExists(fGitRepository) then begin
    ShowMessage('Please set Git Repository Path or environment variable SYN_GITREPO_PATH');
    Close;
  end else if ((fGitExe <> GITDEF) and not FileExists(fGitExe)) or
       ((fGitExe = GITDEF) and
        not Exec(fGitRepository, GITDEF, 'status', '', '', '', '', {isshell=}false)) then begin
    ShowMessage('Please install Git or set environment variable GIT_PATH');
    Close;
  end else
    ReadStatus;
end;

procedure TMainForm.btnFullSynchClick(Sender: TObject);
begin
  btnFossilSynch.Click;
  btnGitSynch.Click;
  if chkCopyLink.Checked then
    btnCopyLink.Click;
end;

procedure TMainForm.btnFossilSynchClick(Sender: TObject);
var
  Desc: string;
  DescFile: TFileName;
  VersionNumber: integer;
  VersionText: RawUTF8;
begin
  Desc := trim(mmoDescription.Text);
  if Desc = '' then begin
    ShowMessage('Missing description');
    mmoDescription.SetFocus;
    exit;
  end;
  if chkFossilPull.Checked then
    Exec(fFossilRepository, 'FossilUpdate', '', '', '', '', '');
  VersionText := UnQuoteSQLString(StringFromFile(fDevPath + PathDelim + 'SynopseCommit.inc'));
  VersionText := GetCSVItem(pointer(VersionText), 2, '.');
  VersionNumber := GetCardinalDef(pointer(VersionText), 255);
  inc(VersionNumber);
  VersionText := '''' + VERSION + '.' + UInt32ToUtf8(VersionNumber) + ''''#13#10;
  FileFromString(VersionText, fDevPath + PathDelim +'SynopseCommit.inc');
  FileFromString(VersionText, fFossilRepository + PathDelim + 'SynopseCommit.inc');
  DescFile := fBatPath + 'desc.txt';
  FileFromString('{' + ToUTF8(VersionNumber) + '} ' + Desc, DescFile);
  Exec(fFossilRepository, 'FossilCommit', DescFile, IntToStr(ord(chkFossilPush.Checked)), fFossilRepository, '', '');
  btnRefreshStatus.Click;
end;

procedure TMainForm.btnGitSynchClick(Sender: TObject);
var
  Desc, status: string;
  DescFile, BatchFile, GitHub: TFileName;
  i,n: integer;
begin
  Desc := trim(mmoDescription.Text);
  if Desc = '' then begin
    status := mmoStatus.Text;
    i := pos('comment:', status);
    if i > 0 then begin
      delete(status, 1, i + 8);
      with TStringList.Create do
      try
        Text := trim(status);
        status := Strings[0];
        for i := 1 to Count - 1 do
          if copy(Strings[i], 1, 3) = '   ' then
            status := status + ' ' + trim(Strings[i])
          else
            break;
      finally
        Free;
      end;
      i := pos('(user: ', status);
      if i > 0 then
        SetLength(status, i - 1);
      i := pos('} ', status);
      if (i > 0) and (i < 10) then
        delete(status, 1, i + 1); // trim left '{256} '
      mmoDescription.Text := trim(status);
    end
    else begin
      ShowMessage('Missing description');
      mmoDescription.SetFocus;
    end;
    exit;
  end;
  if not DirectoryExists(fGitRepository) then begin
    ShowMessage('Please set Git Repository Name');
    exit;
  end;
  DescFile := fBatPath + 'desc.txt';
  FileFromString(Desc, DescFile);
  GitHub := ExtractFilePath(fGitRepository);
  n := 0;
  if (Sender = btnGitAll) or (Sender = btnSynProject) then
    inc(n,SynchFolders(fFossilRepository, GitHub + 'SynProject', true, true, true));
  if (Sender = btnGitAll) or (Sender = btnSynPdf) then
    inc(n,SynchFolders(fFossilRepository, GitHub + 'SynPDF', false, true, true));
  if (Sender = btnGitAll) or (Sender = btnDMustache) then
    inc(n,SynchFolders(fFossilRepository, GitHub + 'dmustache', false, true, true));
  if (Sender = btnGitAll) or (Sender = btnLVCL) then
    inc(n,SynchFolders(fFossilRepository, GitHub + 'LVCL', false, true, true));
  if (Sender = btnGitAll) or (Sender = btnGitSynch) then
    inc(n,SynchFolders(fFossilRepository, GitHub + 'mORMot', true, true, true));
  {$I-} Writeln(n,' file(s) synched to GitHub'#13#10); {$I+}
  if Sender = btnGitAll then
    BatchFile := 'GitCommitAll'
  else if Sender = btnSynProject then
    BatchFile := 'GitCommitSynProject'
  else if Sender = btnSynPdf then
    BatchFile := 'GitCommitSynPdf'
  else if Sender = btnDMustache then
    BatchFile := 'GitCommitDMustache'
  else if Sender = btnLVCL then
    BatchFile := 'GitCommitLVCL'
  else
    BatchFile := 'GitCommit';
  Exec(fGitRepository, BatchFile, fFossilRepository, fGitRepository, fGitExe, DescFile, fDevPath);
  mmoDescription.SetFocus; // ReadStatus not necessary if git only
end;

procedure TMainForm.btnRefreshStatusClick(Sender: TObject);
begin
  ReadStatus;
  mmoDescription.SetFocus;
  mmoDescription.SelectAll;
end;

procedure TMainForm.btnGitShellClick(Sender: TObject);
begin
  {$ifdef MSWINDOWS}
  Exec(fGitRepository, SHELLEXE, '', '', '', '', '');
  {$else}
  Exec(fGitRepository, '/usr/bin/meld', fGitRepository, fDevPath, '', '', '', false, false);
  {$endif}
end;

procedure TMainForm.btnFossilShellClick(Sender: TObject);
begin
  {$ifdef MSWINDOWS}
  Exec(fFossilRepository, SHELLEXE, '', '', '', '', '');
  {$else}
  Exec(fFossilRepository, '/usr/bin/meld', fFossilRepository, fDevPath, '', '', '', false, false);
  {$endif}
end;

procedure TMainForm.btnTestsClick(Sender: TObject);
begin
  {$ifdef MSWINDOWS}
  Exec(fDevPath, 'compilpil', '', '', '', '', '');
  {$endif}
end;

procedure TMainForm.btnCopyLinkClick(Sender: TObject);
var
  i: integer;
  status: string;
begin
  status := mmoStatus.Lines.Text;
  i := pos('checkout:', status);
  if i < 0 then
    exit;
  inc(i, 10);
  while (i < length(status)) and (status[i] <= ' ') do
    inc(i);
  Clipboard.AsText := 'https://synopse.info/fossil/info/' + copy(status, i, 10);
end;

end.

