unit MultiselectMoveTestCase;

interface
uses
  System.Generics.Collections,
  TestFramework;


type
  TMultiselectMoveTest = class(TTestCase)
  private
    FList: TList<Integer>;
  protected
    procedure SetUp; override;
    procedure TearDown; override;

    procedure CheckList(AExpected: array of Integer);
    procedure Move(ASelection: array of Integer; ATargetIndex: Integer);

    property List: TList<Integer> read FList;
  published
    procedure TestSetUp;

    procedure TestSingleDown;
    procedure TestSingleUp;
    procedure TestSingleMin;
    procedure TestSingleMax;

    procedure TestMultipleDown;
    procedure TestMultipleUp;
    procedure TestMultipleMin;
    procedure TestMultipleMax;

    procedure TestGapDown;
    procedure TestGapUp;
    procedure TestGapMiddle;
    procedure TestGapMin;
    procedure TestGapMax;
  end;


implementation
uses
  System.SysUtils;


{ TMultiselectMoveTest }
procedure TMultiselectMoveTest.SetUp;
var
  value: Integer;

begin
  inherited SetUp;

  FList := TList<Integer>.Create;
  for value := 0 to 9 do
    FList.Add(value);
end;


procedure TMultiselectMoveTest.TearDown;
begin
  FreeAndNil(FList);

  inherited TearDown;
end;


procedure TMultiselectMoveTest.CheckList(AExpected: array of Integer);
var
  listValues: TStringBuilder;
  expectedValues: TStringBuilder;
  value: Integer;

begin
  listValues := nil;
  expectedValues := nil;
  try
    listValues := TStringBuilder.Create;
    for value in List do
      listValues.Append(value).Append(',');

    expectedValues := TStringBuilder.Create;
    for value in AExpected do
      expectedValues.Append(value).Append(',');

    CheckEquals(expectedValues.ToString, listValues.ToString);
  finally
    FreeAndNil(expectedValues);
    FreeAndNil(listValues);
  end;
end;


procedure TMultiselectMoveTest.Move(ASelection: array of Integer; ATargetIndex: Integer);
var
  targetIndex: Integer;
  sourceShift: Integer;
  index: Integer;
  sourceIndex: Integer;

begin
  targetIndex := ATargetIndex;
  sourceShift := 0;

  for index in ASelection do
  begin
    sourceIndex := index;
    if index < ATargetIndex then
      Inc(sourceIndex, sourceShift);

    if targetIndex > sourceIndex then
    begin
      List.Move(sourceIndex, Pred(targetIndex));
      Dec(sourceShift);
    end else if targetIndex < sourceIndex then
    begin
      List.Move(sourceIndex, targetIndex);
      Inc(targetIndex);
    end;
  end;
end;


procedure TMultiselectMoveTest.TestSetUp;
begin
  CheckList([0,1,2,3,4,5,6,7,8,9]);
end;


procedure TMultiselectMoveTest.TestSingleDown;
begin
  Move([1], 5);
  CheckList([0,2,3,4,1,5,6,7,8,9]);
end;


procedure TMultiselectMoveTest.TestSingleMin;
begin
  Move([5], 0);
  CheckList([5,0,1,2,3,4,6,7,8,9]);
end;


procedure TMultiselectMoveTest.TestSingleMax;
begin
  Move([5], 10);
  CheckList([0,1,2,3,4,6,7,8,9,5]);
end;


procedure TMultiselectMoveTest.TestSingleUp;
begin
  Move([8], 5);
  CheckList([0,1,2,3,4,8,5,6,7,9]);
end;


procedure TMultiselectMoveTest.TestMultipleDown;
begin
  Move([1,2], 5);
  CheckList([0,3,4,1,2,5,6,7,8,9]);
end;


procedure TMultiselectMoveTest.TestMultipleUp;
begin
  Move([5,6], 1);
  CheckList([0,5,6,1,2,3,4,7,8,9]);
end;


procedure TMultiselectMoveTest.TestMultipleMin;
begin
  Move([5,6,7], 0);
  CheckList([5,6,7,0,1,2,3,4,8,9]);
end;


procedure TMultiselectMoveTest.TestMultipleMax;
begin
  Move([3,4,5], 10);
  CheckList([0,1,2,6,7,8,9,3,4,5]);
end;


procedure TMultiselectMoveTest.TestGapDown;
begin
  Move([3,5], 6);
  CheckList([0,1,2,4,3,5,6,7,8,9]);
end;


procedure TMultiselectMoveTest.TestGapUp;
begin
  Move([7,9], 3);
  CheckList([0,1,2,7,9,3,4,5,6,8]);
end;


procedure TMultiselectMoveTest.TestGapMiddle;
begin
  Move([3,8], 5);
  CheckList([0,1,2,4,3,8,5,6,7,9]);
end;


procedure TMultiselectMoveTest.TestGapMin;
begin
  Move([3,5,8], 0);
  CheckList([3,5,8,0,1,2,4,6,7,9]);
end;


procedure TMultiselectMoveTest.TestGapMax;
begin
  Move([4,6,9], 10);
  CheckList([0,1,2,3,5,7,8,4,6,9]);
end;


initialization
  RegisterTest(TMultiselectMoveTest.Suite);

end.
