<?php

namespace App\Models;

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

use App\Classes\Protocol\{Binkp,DNS,EMSI};

/**
 * This class represents our configuration.
 *
 * Our 'System' is defined by system_id, and from it we can find out our BBS name and addresses.
 *
 * @package App\Models
 * @property Collection nodes
 */
class Setup extends Model
{
	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 BIND					= '::';
	public const EMSI_PORT				= 60179;
	public const EMSI_BIND				= self::BIND;
	public const DNS_PORT				= 53;
	public const DNS_BIND				= '::';

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

	public const MAX_MSGS_PKT			= 50;

	protected $casts = [
		'options' => 'array',
		'servers' => 'array',
	];

	/**
	 * @throws \Exception
	 */
	public function __get($key)
	{
		switch ($key) {
			case 'binkp_bind':
			case 'dns_bind':
			case 'emsi_bind':
				return Arr::get($this->servers,str_replace('_','.',$key),self::BIND);

			case 'binkp_port':
				return Arr::get($this->servers,str_replace('_','.',$key),Binkp::PORT);
			case 'dns_port':
				return Arr::get($this->servers,str_replace('_','.',$key),EMSI::PORT);
			case 'emsi_port':
				return Arr::get($this->servers,str_replace('_','.',$key),DNS::PORT);

			case 'options_options':
				return Arr::get($this->options,'options');

			case 'binkp_active':
			case 'dns_active':
			case 'emsi_active':
			case 'binkp_options':
			case 'dns_options':
			case 'emsi_options':
				return Arr::get($this->servers,str_replace('_','.',$key));

			case 'msgs_pkt':
				return Arr::get($this->options,$key,self::MAX_MSGS_PKT);

			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_bind':
			case 'binkp_port':
			case 'binkp_options':
			case 'dns_bind':
			case 'dns_port':
			case 'dns_options':
			case 'emsi_bind':
			case 'emsi_port':
			case 'emsi_options':
				return Arr::set($this->servers,str_replace('_','.',$key),$value);

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

	/**
	 * The Mailer Product ID in hex.
	 *
	 * @param int $c
	 * @return string
	 * @throws \Exception
	 */
	public static function product_id(int $c=self::PRODUCT_ID): string
	{
		return hexstr($c);
	}

	/* RELATIONS */

	/**
	 * The defined system that this setup is valid for
	 *
	 * @return \Illuminate\Database\Eloquent\Relations\BelongsTo
	 */
	public function system()
	{
		return $this->belongsTo(System::class);
	}

	/* METHODS */

	public function optionClear(int $key,$index='option_options'): void
	{
		$this->{$index} &= ~$key;
	}

	public function optionGet(int $key,$index='option_options'): int
	{
		return ($this->{$index} & $key);
	}

	public function optionSet(int $key,$index='option_options'): void
	{
		$this->{$index} |= $key;
	}
}