<?php

/**
 * Calculate CCITT-CRC16 checksum
 */
if (! function_exists('crc16')) {
    function crc16($data)
    {
        $crc = 0x0000;
        for ($i = 0; $i < strlen($data); $i++)
        {
            $x = (($crc >> 8) ^ ord($data[$i])) & 0xFF;
            $x ^= $x >> 4;
            $crc = (($crc << 8) ^ ($x << 12) ^ ($x << 5) ^ $x) & 0xFFFF;
        }
        return $crc;
    }
}

/**
 * Dump out data into a hex dump
 */
if (! function_exists('hex_dump')) {
	function hex_dump($data,$newline="\n",$width=16)
	{
		$result = '';

		$pad = '.'; # padding for non-visible characters
		$to = $from = '';

		for ($i=0; $i<=0xFF; $i++)
		{
			$from .= chr($i);
			$to .= ($i >= 0x20 && $i <= 0x7E) ? chr($i) : $pad;
		}

		$hex = str_split(bin2hex($data),$width*2);
		$chars = str_split(strtr($data,$from,$to),$width);

		$offset = 0;
		foreach ($hex as $i => $line)
		{
			$result .= sprintf('%08X: %-48s  [%s]%s',
				$offset,
				substr_replace(implode(' ',str_split($line,2)),' ',8*3,0),
				$chars[$i],
				$newline);

			$offset += $width;
		}

		return $result;
	}
}

/**
 * Send a value has hex chars
 */
if (! function_exists('hexstr')) {
	function hexstr(int $int)
	{
		if ($int > 0xffff)
			throw new Exception('Int too large for hexstr');

		$hexdigitslower = '0123456789abcdef';
		$x = '';

		if ($int > 0xff) {
			$x .= substr($hexdigitslower,($int&0xf000)>>12,1);
			$x .= substr($hexdigitslower,($int&0x0f00)>>8,1);
		}

		$x .= substr($hexdigitslower,($int&0x00f0)>>4,1);
		$x .= substr($hexdigitslower,($int&0x000f),1);

		return $x;
	}
}

if (! function_exists('timew')) {
	/**
	 * Convert a time into an 32 bit value. This is primarily used to create 8 character hex filenames that
	 * are unique using 1/10th second precision.
	 *
	 * Time is:
	 * + 02 bits unused
	 * + 04 bits Month
	 * + 05 bits Day
	 * + 05 bits Hour
	 * + 06 bits Min
	 * + 06 bits Sec
	 * + 04 bits 10th Sec
	 * = 32 (2 bits free)
	 *
	 * @param \Carbon\Carbon|null $time
	 * @return int
	 */
	function timew(\Carbon\Carbon $time=NULL): int
	{
		static $delay = 0;

		// If we are not passed a time, we'll use the time now
		if (! $time) {
			// In case we are called twice, we'll delay 1/10th second so we have a unique result.
			if (\Carbon\Carbon::now()->getPreciseTimestamp(1) == $delay)
				usleep(100000);

			$time = \Carbon\Carbon::now();
		}

		$delay = $time->getPreciseTimestamp(1);

		$t = ($time->month & 0xf) << 5;
		$t = ($t | ($time->day & 0x1f)) << 5;
		$t = ($t | ($time->hour & 0x1f)) << 6;
		$t = ($t | ($time->minute & 0x3f)) << 6;
		$t = ($t | ($time->second & 0x3f));

		return hexdec(sprintf('%07x%1x',$t,(round($time->milli/100) & 0x7f)));
	}
}

if (! function_exists('dwtime')) {
	/**
	 * Convert a 40 bit integer into a time.
	 * @see timew()
	 *
	 * @param int $time
	 * @param int|null $year
	 * @return \Carbon\Carbon
	 */
	function wtime(int $time,int $year=NULL): \Carbon\Carbon
	{
		if (! $year)
			$year = \Carbon\Carbon::now()->year;

		// Does the time have milli seconds?
		if ($time > pow(2,26)-1) {
			$milli = ($time & 0xf);
			$time = $time >> 4;

		} else {
			$milli = 0;
		}

		$sec = ($time & 0x3f);
		$time = $time >> 6;

		$min = ($time & 0x3f);
		$time = $time >> 6;

		$hr = ($time & 0x1f);
		$time = $time >> 5;

		$day = ($time & 0x1f);
		$time = $time >> 5;

		$month = ($time & 0x1f);

		return \Carbon\Carbon::create($year,$month,$day,$hr,$min,$sec+$milli/10);
	}
}