#include "test.h"

#include <random>

using namespace std;

#include "navigation.h"

TEST_CASE("basic setup") {
	Navigation navi;

	CHECK(!navi.at_end());
	CHECK(navi.at_line_start());
	CHECK(navi.cur() == 0);

	Navigation copy(navi);
	CHECK(!navi.at_end());
	CHECK(navi.at_line_start());
	CHECK(navi.cur() == 0);

}

TEST_CASE("move above 0") {
	Navigation navi;
	CHECK(navi.cur() == 0);
	navi.up();
	CHECK(navi.cur() == 0);
	navi.page_up();
	CHECK(navi.cur() == 0);
}

TEST_CASE("goto tab") {
	Navigation navi;
	navi.goto_pos(0);
	CHECK(navi.tab() == 0);
	CHECK(navi.at_line_start());
	navi.goto_pos(10);
	CHECK(navi.tab() == 0);
	navi.goto_pos(15);
	CHECK(navi.tab() == 0);
	navi.goto_pos(30);
	CHECK(navi.tab() == 0);
	CHECK(navi.at_line_start());
	navi.goto_pos(40);
	CHECK(navi.tab() == 40);
	CHECK(!navi.at_line_start());
	navi.goto_pos(60);
	CHECK(navi.tab() == 40);
	navi.goto_pos(90);
	CHECK(navi.tab() == 80);
}

TEST_CASE("goto and jump") {
	Navigation navi;
	navi.goto_line(10, false);
	CHECK(navi.cur() == 10);
	navi.goto_line(20, true);
	CHECK(navi.cur() == 20);
	navi.jump_back();
	navi.goto_line(30, true);
	CHECK(navi.cur() == 30);
	navi.jump_back();
	CHECK(navi.cur() == 10);
	navi.jump_back();
	CHECK(navi.cur() == 30);

	navi.end();
	CHECK(navi.cur() == G::NO_POS);
	CHECK(navi.at_end());


}

size_t nice(size_t pos) {
	if (pos == G::NO_POS) return 0;
	return pos;
}

void set_view(Navigation* navi, const set<size_t>& full) {
	size_t cur = navi->cur();
	vector<size_t> view;
	view.resize(51, G::NO_POS);

	auto it_down = full.lower_bound(cur);
	auto it_up = it_down;

	size_t p = 24;
	while (it_up != full.begin()) {
		--it_up;
		view[p] = *it_up;
		if (p == 0) break;
		--p;
	}
	for (size_t i = 25; i < 51; ++i) {
		if (it_down != full.end()) view[i] = *it_down;
		else break;
		++it_down;
	}
	/* trace array
	for (size_t i = 0; i < 51 ; ++i) {
		cout << i << ": " << nice(view[i]) << " ";
	}
	cout << endl;*/
	navi->set_view(view);
}

TEST_CASE("move around") {
        mt19937 rng(random_device{}());
	for (int i = 0; i < 1000; ++i) {
		unique_ptr<Navigation> navi(new Navigation());
		set<size_t> fullview;
	        uniform_int_distribution<size_t> move_dist(0, 6);
	        uniform_int_distribution<size_t> len_dist(1, 15);
		uniform_int_distribution<size_t> percent_dist(1, 100);
		size_t len = len_dist(rng);
		len *= len * len;
		size_t percent = percent_dist(rng);
		uniform_int_distribution<size_t> pos_dist(0, len - 1);
		for (size_t j = 0; j < len; ++j) {
			if (percent_dist(rng) < percent) {
				fullview.insert(j);
			}
		}

		for (int j = 0; j < 100; ++j) {
			set_view(navi.get(), fullview);
			size_t move = move_dist(rng);
			size_t cur = navi->cur();
			if (move == 0) {
				auto it = fullview.lower_bound(cur);
				navi->up();
				if (fullview.empty()) {
					CHECK(navi->cur() == G::NO_POS);
				} else if (it == fullview.begin()) {
					CHECK(navi->cur() == *it);
				} else {
					--it;
					CHECK(navi->cur() == *it);
				}
			} else if (move == 1) {
				auto it = fullview.upper_bound(cur);
				navi->down();
				if (it == fullview.end()) {
					if (fullview.empty()) CHECK(navi->cur() == G::NO_POS);
					else {
						--it;
						CHECK(navi->cur() == *it);
					}
				} else {
					CHECK(navi->cur() == *it);
				}
			} else if (move == 2) {
				size_t pos = pos_dist(rng);
				navi->goto_line(pos, false);
				CHECK(navi->cur() == pos);
			} else if (move == 3) {
				navi->start();
				CHECK(navi->cur() == 0);
			} else if (move == 4) {
				navi->end();
				CHECK(navi->cur() == G::NO_POS);
			} else if (move == 5) {
				auto it = fullview.lower_bound(cur);
				navi->page_up();
				for (size_t k = 0; k < 24; ++k) {
					if (it == fullview.begin()) break;
					--it;
				}
				if (fullview.empty()) {
					CHECK(navi->cur() == G::NO_POS);
				} else if (it == fullview.begin()) {
					CHECK(navi->cur() == *it);
				} else {
					--it;
					CHECK(navi->cur() == *it);
				}
			} else if (move == 6) {
				auto it = fullview.lower_bound(cur);
				navi->page_down();
				for (size_t k = 0; k < 25; ++k) {
					if (it == fullview.end()) break;
					++it;
				}
				if (it == fullview.end()) {
					if (fullview.empty()) CHECK(navi->cur() == G::NO_POS);
					else {
						--it;
						CHECK(navi->cur() == *it);
					}
				} else {
					CHECK(navi->cur() == *it);
				}
			}
		}

	}

}

