<?php declare(strict_types=1);

namespace Aviat\Kilo\Tests;

use Aviat\Kilo\Enum\Color;
use Aviat\Kilo\Enum\Highlight;
use PHPUnit\Framework\TestCase;

use function Aviat\Kilo\array_replace_range;
use function Aviat\Kilo\ctrl_key;
use function Aviat\Kilo\get_file_syntax_map;
use function Aviat\Kilo\is_ascii;
use function Aviat\Kilo\is_ctrl;
use function Aviat\Kilo\is_digit;
use function Aviat\Kilo\is_separator;
use function Aviat\Kilo\is_space;
use function Aviat\Kilo\str_contains;
use function Aviat\Kilo\syntax_to_color;
use function Aviat\Kilo\tabs_to_spaces;

class FunctionTest extends TestCase {


	public function test_is_ascii(): void
	{
		$this->assertFalse(is_ascii('©'));
		$this->assertFalse(is_ascii("\x80"));
		$this->assertTrue(is_ascii('a'));
		$this->assertTrue(is_ascii("\x7b"));
	}

	public function test_ctrl_key(): void
	{
		$this->assertEquals(-1, ctrl_key("\x80"));

		$this->assertEquals(0x01, ctrl_key('a'));
	}

	public function test_is_ctrl(): void
	{
		for ($i = 0x0; $i < 0x20; $i++)
		{
			$char = chr($i);
			$this->assertTrue(is_ctrl($char), 'Should be a control character');
		}

		for ($n = 0x20; $n < 0x7f; $n++)
		{
			$char = chr($n);
			$this->assertFalse(is_ctrl($char), 'Should not be a control character');
		}

		// Escape, code 7f, is an outlier
		$this->assertTrue(is_ctrl(chr(0x7f)));
	}

	public function test_is_space(): void
	{
		$this->assertFalse(is_space("\x80"), 'Non-ascii character is not considered a space');

		foreach ([' ', "\t", "\n", "\r", "\xa", "\xb", "\xc"] as $char)
		{
			$this->assertTrue(is_space($char), 'Should be considered a space character');
		}
	}

	public function test_is_digit(): void
	{
		$this->assertFalse(is_separator("\x80"), 'Non-ascii character is not a digit');

		$digits = str_split('01234567890');
		foreach ($digits as $digit)
		{
			$this->assertTrue(is_digit($digit));
		}
	}

	public function test_is_separator(): void
	{
		$this->assertFalse(is_separator("\x80"), 'Non-ascii character is not a separator');

		$chars = str_split(',.()+-/*=~%<>[];');
		foreach ($chars as $char)
		{
			$this->assertTrue(is_separator($char), 'The character should be considered a separator');
		}

		// Null byte is considered a separator
		$this->assertTrue(is_separator("\0"));
	}

	public function test_syntax_to_color(): void
	{
		// Nonsense input returns FG::White
		$this->assertEquals(syntax_to_color(999), Color::FG_WHITE);

		$this->assertNotEquals(syntax_to_color(Highlight::OPERATOR), Color::FG_WHITE);
	}

	public function test_get_file_syntax_map(): void
	{
		$this->assertNotEmpty(get_file_syntax_map());
	}

	public function test_str_contains(): void
	{
		// Search from string offset
		$this->assertTrue(str_contains('  vcd', 'vcd', 2));

		$this->assertFalse(str_contains('', "\0"));

		// An empty search string returns false
		$this->assertFalse(str_contains('', ''));

		$this->assertTrue(str_contains('alphabet', 'phab'));
	}

	public function test_tabs_to_spaces(): void
	{
		$original = "\t\t\t{";
		$this->assertFalse(str_contains(tabs_to_spaces($original), "\t"));
	}

	public function test_array_replace_range_length_1(): void
	{
		$original = [
			'a', 'b', 'c', 'd', 'e', 'f'
		];

		$expected = ['a', 'b', 'c', 'foo', 'e', 'f'];

		array_replace_range($original, 3, 1, 'foo');

		$this->assertEquals($expected, $original);
	}

	public function test_array_replace_range_length_gt_1(): void
	{
		$original = [
			'a', 'b', 'c', 'd', 'e', 'f'
		];

		$expected = [
			'a', 'foo', 'foo', 'foo', 'e', 'f'
		];

		array_replace_range($original, 1, 3, 'foo');
		$this->assertEquals($expected, $original);
	}
}