/** @file testPartitioning.cxx
 ** Unit Tests for Scintilla internal data structures
 **/

#include <cstddef>
#include <cstring>

#include <stdexcept>
#include <string_view>
#include <vector>
#include <optional>
#include <algorithm>
#include <memory>

#include "Debugging.h"

#include "Position.h"
#include "SplitVector.h"
#include "Partitioning.h"

#include "catch.hpp"

using namespace Scintilla::Internal;

constexpr int growSize = 4;

constexpr int lengthTestArray = 8;
static const int testArray[lengthTestArray] = {3, 4, 5, 6, 7, 8, 9, 10};

// Test SplitVectorWithRangeAdd.

TEST_CASE("SplitVectorWithRangeAdd") {

	SplitVectorWithRangeAdd<int> svwra(growSize);

	SECTION("IsEmptyInitially") {
		REQUIRE(0 == svwra.Length());
	}

	SECTION("IncrementExceptEnds") {
		svwra.InsertFromArray(0, testArray, 0, lengthTestArray);
		svwra.RangeAddDelta(1, lengthTestArray-1, 1);
		for (int i=0; i<svwra.Length(); i++) {
			if ((i == 0) || (i == lengthTestArray-1))
				REQUIRE((i+3) == svwra.ValueAt(i));
			else
				REQUIRE((i+4) == svwra.ValueAt(i));
		}
	}

}

// Test Partitioning.

TEST_CASE("Partitioning") {

	Partitioning<Sci::Position> part(growSize);

	SECTION("IsEmptyInitially") {
		REQUIRE(1 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(part.Partitions()));
		REQUIRE(0 == part.PartitionFromPosition(0));
	}

	SECTION("SimpleInsert") {
		part.InsertText(0, 1);
		REQUIRE(1 == part.Partitions());
		REQUIRE(1 == part.PositionFromPartition(part.Partitions()));
	}

	SECTION("TwoPartitions") {
		part.InsertText(0, 2);
		part.InsertPartition(1, 1);
		REQUIRE(2 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(1 == part.PositionFromPartition(1));
		REQUIRE(2 == part.PositionFromPartition(2));
	}

	SECTION("MoveStart") {
		part.InsertText(0, 3);
		part.InsertPartition(1, 2);
		part.SetPartitionStartPosition(1,1);
		REQUIRE(2 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(1 == part.PositionFromPartition(1));
		REQUIRE(3 == part.PositionFromPartition(2));
	}

	SECTION("InsertAgain") {
		part.InsertText(0, 3);
		part.InsertPartition(1, 2);
		part.InsertText(0,3);
		part.InsertText(1,2);
		REQUIRE(2 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(5 == part.PositionFromPartition(1));
		REQUIRE(8 == part.PositionFromPartition(2));
	}

	SECTION("InsertMultiple") {
		part.InsertText(0, 10);
		const Sci::Position positions[] { 2, 5, 7 };
		part.InsertPartitions(1, positions, std::size(positions));
		REQUIRE(4 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(2 == part.PositionFromPartition(1));
		REQUIRE(5 == part.PositionFromPartition(2));
		REQUIRE(7 == part.PositionFromPartition(3));
		REQUIRE(10 == part.PositionFromPartition(4));
	}

	SECTION("InsertMultipleWithCast") {
		part.InsertText(0, 9);
		REQUIRE(1 == part.Partitions());
		const ptrdiff_t positionsp[]{ 2, 4, 6, 8 };
		part.InsertPartitionsWithCast(1, positionsp, std::size(positionsp));
		REQUIRE(5 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(2 == part.PositionFromPartition(1));
		REQUIRE(4 == part.PositionFromPartition(2));
		REQUIRE(6 == part.PositionFromPartition(3));
		REQUIRE(8 == part.PositionFromPartition(4));
		REQUIRE(9 == part.PositionFromPartition(5));
	}

	SECTION("InsertReversed") {
		part.InsertText(0, 3);
		part.InsertPartition(1, 2);
		part.InsertText(1,2);
		part.InsertText(0,3);
		REQUIRE(2 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(5 == part.PositionFromPartition(1));
		REQUIRE(8 == part.PositionFromPartition(2));
	}

	SECTION("InverseSearch") {
		part.InsertText(0, 3);
		part.InsertPartition(1, 2);
		part.SetPartitionStartPosition(1,1);

		REQUIRE(2 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(1 == part.PositionFromPartition(1));
		REQUIRE(3 == part.PositionFromPartition(2));

		REQUIRE(0 == part.PartitionFromPosition(0));
		REQUIRE(1 == part.PartitionFromPosition(1));
		REQUIRE(1 == part.PartitionFromPosition(2));

		REQUIRE(1 == part.PartitionFromPosition(3));
	}

	SECTION("DeletePartition") {
		part.InsertText(0, 2);
		part.InsertPartition(1, 1);
		part.RemovePartition(1);
		REQUIRE(1 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(2 == part.PositionFromPartition(1));
	}

	SECTION("DeleteAll") {
		part.InsertText(0, 3);
		part.InsertPartition(1, 2);
		part.SetPartitionStartPosition(1,1);
		part.DeleteAll();
		// Back to initial state
		REQUIRE(1 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(part.Partitions()));
	}

	SECTION("TestBackwards") {
		part.InsertText(0, 10);
		part.InsertPartition(1, 3);
		part.InsertPartition(2, 6);
		part.InsertPartition(3, 9);
		part.InsertText(2,4);
		part.InsertText(1,2);
		part.InsertText(0,3);
		REQUIRE(4 == part.Partitions());
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(6 == part.PositionFromPartition(1));
		REQUIRE(11 == part.PositionFromPartition(2));
		REQUIRE(18 == part.PositionFromPartition(3));
		REQUIRE(19 == part.PositionFromPartition(4));
	}

	SECTION("TestMany") {
		// Provoke backstep call
		part.InsertText(0, 42);
		for (int i=0; i<20; i++) {
			part.InsertPartition(i+1, (i+1) * 2);
		}
		for (int i=20; i>0; i--) {
			part.InsertText(i,2);
		}
		REQUIRE(21 == part.Partitions());
		for (int i=1; i<20; i++) {
			REQUIRE((i*4 - 2) == part.PositionFromPartition(i));
			REQUIRE(i == part.PartitionFromPosition(i*4 - 2));
		}
		part.InsertText(19,2);
		REQUIRE(3 == part.PartitionFromPosition(10));
		part.InsertText(0,2);
		part.InsertText(0,-2);
		part.RemovePartition(1);
		REQUIRE(0 == part.PositionFromPartition(0));
		REQUIRE(6 == part.PositionFromPartition(1));
		REQUIRE(10 == part.PositionFromPartition(2));
		part.RemovePartition(10);
		REQUIRE(46 == part.PositionFromPartition(10));
		REQUIRE(10 == part.PartitionFromPosition(46));
		REQUIRE(50 == part.PositionFromPartition(11));
		REQUIRE(11 == part.PartitionFromPosition(50));
	}

}
