import { io } from 'socket.io-client';
import { Menu } from './menu';
import { Checklist } from './checklist';
import { Calibration } from './calibration';
import { ce, MorphComponent, MorphFeature } from 'morphux';
import { OutOfServiceMode } from './outOfServiceMode';
import { Timer } from './timer';
import { Lighting } from './lighting';
import { Sound } from './sound';

const socket = io('/');

export class Main {
	Menu = new Menu();
	CheckList = new Checklist(this);
	Calibration = new Calibration(this);
	OutOfServiceMode = new OutOfServiceMode(this);
	Timer = new Timer(this);
	Lighting = new Lighting(this);
	Sound = new Sound(this);

	socket = socket;

	supportButton: HTMLDivElement = document.querySelector('.ntsh-support img');
	supportNumber: string = '';

	constructor() {
		this.registerListeners();
	}

	private registerListeners() {
		this.supportButton.onclick = () => this.showSupport();

		socket.on('status', (data) => {
			this.CheckList.update(data);
		});

		socket.on('supportNumber', (number: string) => {
			this.supportNumber = number;
		});

		socket.on('unityWebSocketState', (state: UnityWebSocketStatus) => {
			this.OutOfServiceMode.input.checked =
				state?.parameters?.outOfService ?? false;
			this.OutOfServiceMode.state =
				state?.parameters?.outOfService ?? false;

			this.Lighting.update(state);
			this.Sound.update(state);
		});

		socket.on('timer', (data) => {
			this.Timer.update(data);
		});
	}

	async executeCommand(
		command: string,
		message: string,
		type: 'unityRunner' | 'unityWebSocket' = 'unityRunner',
	): Promise<boolean> {
		return new Promise<boolean>(async (resolve) => {
			const confirmed = await MorphFeature.Confirm({
				title: 'Are you sure?',
				message,
			});
			if (!confirmed) return resolve(false);

			MorphFeature.Loader({
				active: true,
				message: `Dispatching command...`,
			});
			socket.emit(
				type,
				command,
				(response: { succeed: boolean; message?: string }) => {
					MorphFeature.Loader({ active: false });

					if (!response.succeed)
						return MorphFeature.Alert({
							title: 'Error',
							message: response.message,
						});

					MorphFeature.Notification({
						level: 'success',
						message: `Dispatched command`,
					});
				},
			);
			resolve(true);
		});
	}

	async showSupport() {
		const dialog = new MorphComponent.Dialog({
			title: 'Contact Support',
			width: 'medium',
			height: 'auto',
			okButtonVisible: false,
			cancelButtonVisible: false,
		});

		this.supportNumber.slice();
		const callAnchor = ce(
			'a',
			'ntsh_callanchor',
			{ href: `tel:${this.supportNumber}` },
			`+${this.supportNumber}`,
		);
		dialog.content.appendChild(callAnchor);

		setTimeout(() => callAnchor.click(), 100);
	}
}

const _Main = new Main();

export type ServiceState =
	| 'CONNECTING'
	| 'CONNECTED'
	| 'DISCONNECTED'
	| 'FAILED';

export interface UnityWebSocketStatus {
	state: ServiceState;
	message?: string;
	error?: string;

	parameters: UnityParameters;
}

interface UnityParameters {
	timelineWatching: boolean;
	timelineStanding: boolean;
	timelineProgress: number;
	zedPath: string;
	zedReady: boolean;
	zedFPS: string;
	outOfService: boolean;
	sliders: UnityParameterSlider[];
	advancedSliders: UnityParameterSlider[];
	// dataSliders: UnityParameterSlider[];
	// dataAdvancedSliders: UnityParameterSlider[];
	sensors: UnitySocketMessageHeartbeat['heartbeat']['dataSensors'];
}

type UnityHeartbeatSlider =
	UnitySocketMessageHeartbeat['heartbeat']['dataSliders'][number];
interface UnityParameterSlider extends UnityHeartbeatSlider {
	visualMultiplier: number;
	decimalPlaces: number;
}

interface UnitySocketMessageBase {
	type: string;
	timestamp: number;
}
interface UnitySocketMessageHeartbeat extends UnitySocketMessageBase {
	type: 'heartbeat_data';
	heartbeat: {
		dataSensors: {
			sensorIndex: number;
			deviceName: string;
			outputValue: number;
		}[];
		dataSliders: {
			sliderIndex: number;
			sliderName: string;
			outputValue: number;
			min: number;
			max: number;
			unit: string;
		}[];
		dataTimeline: {
			isStanding: boolean;
			isWatching: boolean;
			timelineProgress: number;
		};
		zedCamera: {
			cameraFPS: string;
			isZedReady: boolean;
			streamInputIP: string;
			streamInputPort: number;
			zedGrabError: number;
		};
		showOutOfService?: boolean;
	};
}
