<?php

namespace App\Classes\Parser;

use Illuminate\Support\Facades\Log;

use App\Classes\FrameFields;
use App\Classes\Parser as AbstractParser;
use App\Classes\Frame\Ansi as AnsiFrame;

class Ansi extends AbstractParser {
	private $content = '';
	private $startline = 0;
	public $fields = NULL;

	public function __construct(string $content,int $startline=1)
	{
		$this->content = $content;
		$this->startline = $startline;
		$this->fields = collect();
	}

	public function __toString(): string
	{
		return $this->parse($this->startline);
	}

	/**
	 * Parse a string and look for the next character that is not $char
	 *
	 * @param string $char
	 * @param int $start
	 * @return bool|int
	 */
	private function findEOF(string $char,int $start)
	{
		for ($c=$start;$c <= strlen($this->content);$c++)
		{
			if ($this->content{$c} != $char)
				return $c-$start;
		}

		return FALSE;
	}

	/**
	 * @param $startline
	 * @param int $offset
	 * @return string
	 */
	private function parse($startline): string
	{
		// Our starting coordinates
		$x = 1;
		$y = $startline;
		$output = '';

		// Scan the frame for a field start
		for ($c=0; $c<=strlen($this->content); $c++)
		{
			// If the frame is not big enough, fill it with spaces.
			$byte = isset($this->content{$c}) ? $this->content{$c} : ' ';
			$advance = 0;

			switch ($byte) {
				case CR:
					$x = 1;
					break;

				case LF:
					$y++;
					break;

				case ESC:
					$advance = 1;
					// Is the next byte something we know about
					$nextbyte = isset($this->content{$c+$advance}) ? $this->content{$c+$advance} : ' ';

					switch ($nextbyte) {
						case '[':
							$advance++;
							$chars = $nextbyte;

							// Find our end CSI param
							$matches = [];

							$a = preg_match('/([0-9]+[;]?)+([a-zA-Z])/',$this->content,$matches,NULL,$c+$advance);

							if (! $a)
								break;

							$advance += strlen($matches[0])-1;
							$chars .= $matches[0];

							switch ($matches[2]) {
								// We ignore 'm' they are color CSIs
								case 'm': break;

								case 'C':
									$x += $matches[1];  // Advance our position
									break;

								default:
									dump('Unhandled CSI: '.$matches[2]);
							}

							break;

						case ' ':
							dump(['l'=>__LINE__,'LOOSE ESC?']);

							break;

						default:
							$c--;                   // Allow for the original ESC
							$advance++;
							$fieldtype = ord($nextbyte)%128; // @todo Do we need the %128 for ANSI?
							$fieldlength = $this->findEOF(chr($fieldtype),$c+1)+1;

							$byte = '';

							$this->fields->push(new FrameFields([
								'type'=>chr($fieldtype),
								'length'=>$fieldlength,
								'x'=>$x,             // Adjust for the ESC char
								'y'=>$y,
							]));

							Log::debug(sprintf('Field found at [%s,%s], Type: %s, Length: %s',$x-1,$y,$fieldtype,$fieldlength));
							$advance += $fieldlength-2;
							$x += $fieldlength;
							$chars = $this->fields->last()->output(AnsiFrame::$if_filler);
					}

					break;

				default:
					$x++;
			}

			$output .= $byte;

			if ($advance) {
				$output .= $chars;
				$c += $advance;
			}

			if ($x > 80) {
				$x = 1;
				$y++;
			}
		}

		return $output;
	}
}