<?php

namespace App\Classes;

use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class Font
{
	private string $text = '';
	private int $width = 0;
	private int $height = 0;

	public function __get($key)
	{
		switch ($key) {
			case 'height':
				return $this->height;

			case 'width':
				return $this->width;

			default:
				throw new \Exception(sprintf('Unknown key %s',$key));
		}
	}

	/**
	 * Message text, goes after header, and if a logo, to the right of it
	 * @note Text can have color codes added: eg: ANSI::ansi_code([1,37])."text"
	 *
	 * @param string $text
	 */
	public function addText(string $text)
	{
		$this->text = $text;
		$this->dimensions();
	}

	/**
	 * Characters used in the font
	 *
	 * @return Collection
	 */
	private function chars(): Collection
	{
		static $chars = [];

		if ($this->text && empty($chars[$this->text])) {
			// Trim any leading/trailing spaces
			$text = trim(strtolower($this->text));
			$chars[$this->text] = collect();

			// Work out the characters we need
			foreach (array_unique(str_split($text)) as $c) {
				if (! $x=Arr::get(static::FONT,$c))
					continue;

				$chars[$this->text]->put($c,$x);
			}
		}

		return $chars[$this->text] ?: collect();
	}

	/**
	 * Full width of the rendered text
	 *
	 * @return void
	 */
	private function dimensions(): void
	{
		$chars = $this->chars();
		$escape = FALSE;

		foreach (str_split(strtolower($this->text)) as $c) {
			if (ord($c) === 0x1b) {
				$escape = TRUE;
				continue;

			} elseif ($escape) {
				$escape = FALSE;
				continue;
			}

			$this->width += ($x=Arr::get($chars->get($c),0)) ? count($x) : 1;

			if ($x)
				$this->height = (($y=count($chars->get($c))) > $this->height) ? $y : $this->height;
		}

		// If the last character is a space, we'll reduce the width
		$space = TRUE;
		foreach ($chars->get($c) as $x)
			if (array_pop($x) != 32) {
				$space = FALSE;
				break;
			}

		if ($space)
			$this->width--;
	}

	public function render_line(int $line): string
	{
		$chars = $this->chars();
		$result = '';
		$escape = FALSE;
		$ansi = FALSE;

		foreach (str_split(strtolower($this->text)) as $c) {
			if (ord($c) === 0x1b) {
				$escape = TRUE;

			} elseif ($escape && $c) {
				$result .= ANSI::ansi_color(ord($c));
				$escape = FALSE;
				$ansi = TRUE;

			} elseif (($c === ' ') || (! $font_chars=$chars->get($c))) {
				$result .= $c;

			} else {
				foreach (Arr::get($font_chars,$line) as $char)
					$result .= chr($char);
			}
		}

		return $result.($ansi ? ANSI::ansi_color(0x0e) : '');
	}

	/**
	 * The height of this font (based on the 1st char)
	 *
	 * @return int
	 */
	public static function height(): int
	{
		return count(Arr::get(static::FONT,'a'));
	}
}