<?php

namespace App\Models;

use Exception;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;

/**
 * Class Setup
 *
 * @package App\Models
 * @property Collection nodes
 * @property array binkp_options
 */
class Setup extends Model
{
	public const S_DOMAIN		= 1<<1;		// Users can create Domains
	public const S_SYSTEM		= 1<<2;		// Users can create Systems

	public const BINKP_OPT_CHT			= 1<<1;	/* CHAT mode - not implemented */
	public const BINKP_OPT_CR			= 1<<2;	/* Crypt mode - not implemented */
	public const BINKP_OPT_MB			= 1<<3;	/* Multi-Batch mode */
	public const BINKP_OPT_MD			= 1<<4;	/* CRAM-MD5 mode */
	public const BINKP_OPT_ND			= 1<<5;	/* http://ftsc.org/docs/fsp-1027.001: No-dupes mode */
	public const BINKP_OPT_NDA			= 1<<6;	/* http://ftsc.org/docs/fsp-1027.001: Asymmetric ND mode */
	public const BINKP_OPT_NR			= 1<<7;	/* http://ftsc.org/docs/fsp-1027.001: Non-Reliable mode */
	public const BINKP_OPT_MPWD			= 1<<8;	/* Multi-Password mode - not implemented */

	public const BINKP_PORT				= 24554;
	public const BINKP_BIND				= '0.0.0.0';
	public const EMSI_PORT				= 60179;
	public const EMSI_BIND				= self::BINKP_BIND;

	public const O_BINKP				= 1<<1; /* Listen for BINKD connections */
	public const O_EMSI					= 1<<2; /* Listen for EMSI connections */
	public const O_HIDEAKA				= 1<<3; /* Hide AKAs to different Zones */

	public const PRODUCT_NAME			= 'Clearing Houz';
	public const PRODUCT_ID				= 0xAB8D;
	public const PRODUCT_VERSION_MAJ	= 0;
	public const PRODUCT_VERSION_MIN	= 0;

	public const hexdigitslower			= '0123456789abcdef';

	// Our non model attributes and values
	private array $internal = [];

	public static function product_id(int $c=self::PRODUCT_ID): string
	{
		return hexstr($c);
	}

	/* RELATIONS */

	public function system()
	{
		return $this->belongsTo(System::class);
	}

	/* ATTRIBUTES */

	public function getLocationAttribute()
	{
		return $this->system->location;
	}

	public function getSysopAttribute()
	{
		return $this->system->sysop;
	}

	public function getSystemNameAttribute()
	{
		return $this->system->name;
	}

	/* METHODS */

	public function __construct(array $attributes = [])
	{
		parent::__construct($attributes);

		// @todo These option should be in setup?
		$this->binkp_options = ['m','d','r','b'];

		/* EMSI SETTINGS */
		$this->do_prevent = 1;				/* EMSI - send an immediate EMSI_INQ on connect */
		$this->ignore_nrq = 0;
		$this->options = 0;					/* EMSI - our capabilities */
	}

	/**
	 * @throws Exception
	 */
	public function __get($key)
	{
		switch ($key) {
			case 'binkp_options':
			case 'ignore_nrq':
			case 'opt_nr':	// @todo - this keys are now in #binkp as bits
			case 'opt_nd':
			case 'opt_nda':
			case 'opt_md':
			case 'opt_cr':
			case 'opt_mb':
			case 'opt_cht':
			case 'opt_mpwd':
			case 'do_prevent':
				return $this->internal[$key] ?? FALSE;

			case 'version':
				return File::exists('VERSION') ? chop(File::get('VERSION')) : 'dev';

			default:
				return parent::__get($key);
		}
	}

	/**
	 * @throws Exception
	 */
	public function __set($key,$value)
	{
		switch ($key) {
			case 'binkp_options':
			case 'ignore_nrq':
			case 'opt_nr':
			case 'opt_nd':
			case 'opt_nda':
			case 'opt_md':
			case 'opt_cr':
			case 'opt_mb':
			case 'opt_cht':
			case 'opt_mpwd':
			case 'do_prevent':
				$this->internal[$key] = $value;
				break;

			default:
				parent::__set($key,$value);
		}
	}

	/* BINKP OPTIONS: BINKP_OPT_* */

	public function binkpOptionClear(int $key): void
	{
		$this->binkp &= ~$key;
	}

	public function binkpOptionGet(int $key): int
	{
		return ($this->binkp & $key);
	}

	public function binkpOptionSet(int $key): void
	{
		$this->binkp |= $key;
	}

	/* GENERAL OPTIONS: O_* */

	public function optionClear(int $key): void
	{
		$this->options &= ~$key;
	}

	public function optionGet(int $key): int
	{
		return ($this->options & $key);
	}

	public function optionSet(int $key): void
	{
		$this->options |= $key;
	}
}