unit uEditorTasks;
{$INCLUDE 'DirSync.inc'}

interface

uses
  Windows, SysUtils, Classes, Generics.Collections, Generics.Defaults,
  {$IFDEF FAR3} Plugin3, {$ELSE} PluginW, {$ENDIF} FarKeysW, FarColor,
  uSystem, uFiles, uFAR, uLog,
  uDirSyncConsts, uDirSync, uSynchronizeDirectories, uMessages;

type
  TEditorTasks = class
  private
    fEditorInfo: TEditorInfo;
    fSyncTask: TSynchronizeTask;
  protected
    function GetEditorID: integer;
    procedure SetEditorID(const Value: integer);
    function GetFileInfo(out Info: TDifferenceFileItem): boolean;
    property SyncTask: TSynchronizeTask read fSyncTask;
  public
    type
      TEditorTask = (etNone, etCopyToLeft, etCopyToRight, etCompare, etEditLeft, etEditRight, etFileInfo);
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    function Execute: integer;
    function ExecuteTask(ATask: TEditorTask): boolean;
    function AskUserWhichTask: TEditorTask;
    function CompareFiles: boolean;
    function FileInfo: boolean;
    function EditFile(IsLeftDirectory: boolean): boolean;
    function ChangeCopyDirection(CopyToLeft: boolean): boolean;
    property EditorID: integer read GetEditorID write SetEditorID;
  end;

implementation

{ TEditorHelper }

function TEditorTasks.AskUserWhichTask: TEditorTask;

  procedure SetMenuItem(var MenuItem: TFarMenuItem; TextID: TMessages {$IFDEF FAR3} ; Flags: TMenuItemFlags {$ENDIF} );
    begin
      FillChar(MenuItem, Sizeof(MenuItem), 0);
      MenuItem.TextPtr := GetMsg(TextID);
      {$IFDEF FAR3}
      MenuItem.Flags := Flags;
      {$ELSE}
      MenuItem.Selected := 0;
      MenuItem.Checked := 0;
      MenuItem.Separator := 0;
      {$ENDIF}
    end;

var
  MenuItems: packed array[0..5] of TFarMenuItem;
begin
  SetMenuItem(MenuItems[0], MOverwriteLeft {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  SetMenuItem(MenuItems[1], MOverwriteRight {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  SetMenuItem(MenuItems[2], MCompare {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  SetMenuItem(MenuItems[3], MEditLeft {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  SetMenuItem(MenuItems[4], MEditRight {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  SetMenuItem(MenuItems[5], MShowFileInfo {$IFDEF FAR3} , MIF_NONE {$ENDIF} );
  case FarApi.Menu( {$IFDEF FAR3} PLUGIN_GUID, MENU_EDITOR_GUID {$ELSE} FarApi.ModuleNumber {$ENDIF} , -1, -1, 0, FMENU_WRAPMODE, GetMsg(MTitle), nil, nil, nil, nil, @MenuItems[0], Length(MenuItems)) of
    0:   Result := etCopyToLeft;
    1:   Result := etCopyToRight;
    2:   Result := etCompare;
    3:   Result := etEditLeft;
    4:   Result := etEditRight;
    5:   Result := etFileInfo;
    else Result := etNone;
  end;
end;

function TEditorTasks.ChangeCopyDirection(CopyToLeft: boolean): boolean;
var
  DestDifferenceType: TDifferenceType;
  SrcDifferenceTypes: set of TDifferenceType;
  CurrentLine, SelStart, SelLength, OriginalLine: TIntPtr;
  DiffInfo: TDifferenceFileItem;
  Line: string;
  IsBlock: boolean;
begin
  //Log('ChangeCopyDirection(%d)', [Integer(CopyToLeft)]);
  if CopyToLeft then begin
    DestDifferenceType := dtOlderLeft;
    SrcDifferenceTypes := [dtOlderRight, dtDifferent];
  end
  else begin
    DestDifferenceType := dtOlderRight;
    SrcDifferenceTypes := [dtOlderLeft, dtDifferent];
  end;
  IsBlock := fEditorInfo.BlockType <> BTYPE_NONE;
  //Log('- IsBlock = %d', [Integer(IsBlock)]);
  OriginalLine := fEditorInfo.CurLine;
  if IsBlock then
    CurrentLine := fEditorInfo.BlockStartLine
  else
    CurrentLine := OriginalLine;
  repeat
    //Log('- Loop started');
    //Log('  - CurrentLine = %d', [CurrentLine]);
    if IsBlock then
      if CurrentLine >= fEditorInfo.TotalLines then
        Break
      else begin
        TFarEditor.SetCursorPosition(EditorID, CurrentLine);
        Inc(CurrentLine);
      end;
    if not TFarEditor.GetString(EditorID, -1, Line, SelStart, SelLength) then
      Break;
    //Log('  - Got string: "%s"', [Line]);
    //Log('  - SelStart = %d, SelLength = %d', [SelStart, SelLength]);
    if IsBlock then
      if SelLength < 0 then
        Break
      else if SelStart > 1 then
        Continue
      else
        Line := Copy(Line, SelStart, SelLength);
    //Log('  - Selected string: "%s"', [Line]);
    DiffInfo := TDifferenceFileItem.CreateFromString(Line);
    try
      if DiffInfo <> nil then
        if DiffInfo.DifferenceType in SrcDifferenceTypes then begin
          DiffInfo.DifferenceType := DestDifferenceType;
          TFarEditor.SetString(EditorID, -1, DiffInfo.AsString);
        end;
    finally
      FreeAndNil(DiffInfo);
    end;
    //Log('  - Loop ended');
  until not IsBlock;
  if IsBlock then
    TFarEditor.SetCursorPosition(EditorID, OriginalLine);
  //Log('Done');
  Result := True;
end;

procedure TEditorTasks.Clear;
begin
  FillChar(fEditorInfo, Sizeof(fEditorInfo), 0);
  fSyncTask := nil;
end;

function TEditorTasks.CompareFiles: boolean;
var
  Info: TDifferenceFileItem;
begin
  Result := False;
  if SyncTask <> nil then
    if GetFileInfo(Info) then begin
      SyncTask.VisualCompare(SyncTask.LeftDirectory + Info.FileName, SyncTask.RightDirectory + Info.FileName);
      Result := True;
    end;
end;

constructor TEditorTasks.Create;
begin
  inherited Create;
  Clear;
end;

destructor TEditorTasks.Destroy;
begin
  inherited;
end;

function TEditorTasks.EditFile(IsLeftDirectory: boolean): boolean;
var
  Info: TDifferenceFileItem;
  FN: string;
begin
  Result := False;
  if SyncTask <> nil then
    if GetFileInfo(Info) then begin
      if IsLeftDirectory then
        FN := SyncTask.LeftDirectory + Info.FileName
      else
        FN := SyncTask.RightDirectory + Info.FileName;
    SyncTask.Edit(FN);
    Result := True;
    end;
end;

function TEditorTasks.Execute: integer;
begin
  Result := {$IFDEF FAR3} 0 {$ELSE} Integer(INVALID_HANDLE_VALUE) {$ENDIF} ;
  if SyncTask <> nil then
    ExecuteTask(AskUserWhichTask);
end;

function TEditorTasks.ExecuteTask(ATask: TEditorTask): boolean;
begin
  Result := False;
  if SyncTask <> nil then
    case ATask of
      etNone:        Result := False;
      etCopyToLeft:  Result := ChangeCopyDirection(True);
      etCopyToRight: Result := ChangeCopyDirection(False);
      etCompare:     Result := CompareFiles;
      etEditLeft:    Result := EditFile(True);
      etEditRight:   Result := EditFile(False);
      etFileInfo:    Result := FileInfo;
    end;
end;

function TEditorTasks.FileInfo: boolean;

  function GetColumns(const Col1, Col2, Col3: string): string;
    begin
      Result := Format('%-20.20s %20.20s %20.20s', [Col1, Col2, Col3]);
    end;

  function GetFileSizeStr(const FileName: string): string;
    var
      Size: int64;
    begin
      Size := GetFileSize(FileName);
      if Size >= 1000000000000 then
        Result := FormatCurr('###,###,##0" MiB"', Size div $100000)
      else if Size >= 0 then
        Result := FormatCurr('###,###,###,##0', Size)
      else
        Result := GetMsgPas(MUnknown);
    end;

  function GetFileDateStr(Index: integer; const FileName: string): string;
    var
      Dates: array[0..2] of TDateTime;
    begin
      if GetFileDateTime(FileName, Dates[0], Dates[1], Dates[2]) then
        Result := FormatDateTime('dd.mm.yyyy hh:nn:ss', Dates[Index])
      else
        Result := GetMsgPas(MUnknown);
    end;

var
  Info: TDifferenceFileItem;
  LeftFN, RightFN: string;
  Msg: array[0..10] of string;
begin
  Result := False;
  if SyncTask <> nil then
    if GetFileInfo(Info) then begin
      LeftFN := SyncTask.LeftDirectory + Info.FileName;
      RightFN := SyncTask.RightDirectory + Info.FileName;
      Msg[ 0] := GetMsgPas(MTitle);
      Msg[ 1] := GetMsgPas(MShowingFileInfo);
      Msg[ 2] := Info.FileName;
      Msg[ 3] := '';
      Msg[ 4] := GetColumns('',                               GetMsgPas(MLeftFile),      GetMsgPas(MRightFile));
      Msg[ 5] := GetColumns(GetMsgPas(MFileSize),             GetFileSizeStr(LeftFN),    GetFileSizeStr(RightFN));
      Msg[ 6] := GetColumns(GetMsgPas(MFileCreationTime),     GetFileDateStr(0, LeftFN), GetFileDateStr(0, RightFN));
      Msg[ 7] := GetColumns(GetMsgPas(MFileModificationTime), GetFileDateStr(1, LeftFN), GetFileDateStr(1, RightFN));
      Msg[ 8] := GetColumns(GetMsgPas(MFileAccessTime),       GetFileDateStr(2, LeftFN), GetFileDateStr(2, RightFN));
      Msg[ 9] := '';
      Msg[10] := GetMsgPas(MOk);
      TFarUtils.ShowMessage(Msg, 0, 1);
      Result := True;
    end;
end;

function TEditorTasks.GetEditorID: integer;
begin
  if {$IFDEF FAR3} (fEditorInfo.StructSize = 0) or {$ENDIF} (SyncTask = nil) then
    Result := -1
  else
    Result := fEditorInfo.EditorID;
end;

function TEditorTasks.GetFileInfo(out Info: TDifferenceFileItem): boolean;
var
  s: string;
begin
  Result := False;
  if SyncTask <> nil then
    if TFarEditor.GetString(EditorID, fEditorInfo.CurLine, s) then begin
      Info := TDifferenceFileItem.CreateFromString(s);
      Result := Info <> nil;
    end;
end;

procedure TEditorTasks.SetEditorID(const Value: integer);
var
  ix: integer;
begin
  if not TFarEditor.GetInfo(Value, fEditorInfo) then
    Clear
  else if not SyncTasks.FindByEditorID(fEditorInfo.EditorID, fSyncTask, ix) then
    Clear;
end;

end.
