<?php

namespace Planaday\Utility\Timer;

use Planaday\Utility\DateAndTimeUtility;

class Timer {
	/** @var array{time:float, name:string, subTimer:Timer|null}[] */
	protected array $timedMoments = [];
	protected string $currentDepthPrefix = '';

	public function __construct(
		protected DateAndTimeUtility $dateAndTimeUtility,
		protected string $name,
		?float $overrideStartTime = null
	) {
		$this->timedMoments[] = ['time' => ($overrideStartTime ?? $this->dateAndTimeUtility->getNowMilliseconds()), 'name' => 'start', 'subTimer' => null];
	}

	public function addTimeStamp(?string $nameTimeStamp = null):void {
		$nameTimeStamp = $this->currentDepthPrefix . ($nameTimeStamp ?? '');

		if ( str_starts_with( $nameTimeStamp, '-' ) ){
			$this->sendToSubTimer($nameTimeStamp);
			return;
		}

		$this->timedMoments[] = ['time' => $this->dateAndTimeUtility->getNowMilliseconds(),
		                         'name' => ($nameTimeStamp == '') ? ('Time Slot ' . count($this->timedMoments)) : $nameTimeStamp,
		                         'subTimer' => null
		];
	}

	/**
	 * @return Timestamp[]
	 */
	public function getTimeStamps():array {
		$returnArray = [];

		foreach ($this->timedMoments as $moment){
			if ($moment['subTimer'] != null){
				$subTimes = ($moment['subTimer'])->getTimeStamps();
			}

			$newMoment = new Timestamp($moment['name'], $moment['time'], $subTimes ?? null);
			$returnArray[] = $newMoment;
		}

		return $returnArray;
	}

	public function getTotalTime():float {
		return $this->dateAndTimeUtility->getNowMilliseconds() - $this->timedMoments[0]['time'];
	}

	public function getTimeStampDebugString():string {
		$timerDuration = $this->dateAndTimeUtility->getNowMilliseconds() - $this->timedMoments[0]['time'];
		$output = 'Timer "<i>' . $this->name . '"</i> has been running for <b>' . number_format($timerDuration, 4) . '</b> ms';

		if (count($this->timedMoments) > 1) $output .= '<br>with the following timeslots: <br>';

		$output .= $this->timeStampArrayToString($this->getTimeStamps());

		return $output;
	}

	private function sendToSubTimer(string $nameTimeStamp):void {
		$entryAmount = count($this->timedMoments);
		$subTimer = $this->timedMoments[$entryAmount - 1]['subTimer'];

		if ($subTimer === null){
			$this->timedMoments[$entryAmount - 1]['subTimer'] = $subTimer = new Timer($this->dateAndTimeUtility, $this->timedMoments[$entryAmount - 1]['name'], $this->timedMoments[$entryAmount - 1]['time']);
		}

		$subTimer->addTimeStamp(substr($nameTimeStamp, 1));
	}

	/**
	 * @param Timestamp[] $timestamps
	 *
	 * @return string
	 */
	private function timeStampArrayToString(array $timestamps):string {
		$timeSlotString = '';

		for ( $i = 1; $i < count( $timestamps ); $i ++ ) {
			$timeSlotDuration = $timestamps[$i]->getTime() - $timestamps[$i-1]->getTime();
			$item = '<div class="pad-timer-item"><div class="pad-timer-name">' . $timestamps[$i]->getName() . ' took <b>' . number_format($timeSlotDuration, 4) . '</b> ms</div>';
			if ($timestamps[$i - 1]->getSubTimes() !== null){
				$item .= '<div class="pad-timer-sub-items">' . $this->timeStampArrayToString($timestamps[$i - 1]->getSubTimes()) . '</div>';
			}
			$item .= '</div>';
			$timeSlotString .= $item;
		}

		return $timeSlotString;
	}

	public function setDepth(int $depth):void {
		$this->currentDepthPrefix = str_repeat('-', $depth);
	}

	public function getDepth():int {
		return strlen($this->currentDepthPrefix);
	}
}