/**
 * The main UI file for this program.
 */
import "./app.css";
import React from "react";
import LoggerStore from "./stores/LoggerStore";
import TopCollar from "./components/TopCollar";
import LogView from "./components/Views/Logs/LogView";
import ConfigViewer from "./components/Views/Config/ConfigViewer";
import _debounce from "lodash/debounce";
import ViewSwitcher from "./components/ViewSwitcher";
import { LauncherClient } from "../../../clients/launcherClient";
import { RouterClient } from "../../../clients/routerClient";

import FloatingFocus from "@q42/floating-focus-a11y";
// The package FloatingFocus injects a focus indicator that moves around the page, but only for
// keyboard users. (Mouse users don't see the indicator.) All that needs to be done is to envoke it.
new FloatingFocus();

interface IProps {}
interface IState {
	activeView: string;
	clientListWidth: number;
	clientListHeight: number;
	logWidth: number;
	fileMode: boolean;
	clientListVisible: boolean;
	windowVisibility: string;
	showSearchBox: boolean;
	showProcessMonitor: boolean;
	showOverlay: boolean;
	overlayMessage: string | null;
}

const CLIENTLIST_HEIGHT_OFFSET = 0;
export default class App extends React.Component<IProps, IState> {
	constructor(props: IProps) {
		super(props);

		const clientListVisible = LoggerStore.getClientListVisible();
		this.state = {
			activeView: LoggerStore.getActiveView(),
			clientListWidth: clientListVisible ? 500 : 0,
			// Initialize with zeros as window is not guaranteed to be defined at initialization
			clientListHeight: window ? window.outerHeight - CLIENTLIST_HEIGHT_OFFSET : 0,
			logWidth: clientListVisible && window ? window.outerWidth - 500 : 0,
			fileMode: LoggerStore.getFileMode(),
			clientListVisible,
			windowVisibility: LoggerStore.getWindowVisibility(),
			showSearchBox: LoggerStore.getShowSearchBox(),
			showProcessMonitor: false,
			showOverlay: false,
			overlayMessage: null,
		};
		this.fileMode = this.fileMode.bind(this);
		this.updateClientListVisible = this.updateClientListVisible.bind(this);
		this.updateWindowVisibility = this.updateWindowVisibility.bind(this);
		this.onkeydown = this.onkeydown.bind(this);
		this.getClientListClasses = this.getClientListClasses.bind(this);
		this.onResizeWindow = this.onResizeWindow.bind(this);
		this.setContainerWidths = _debounce(this.setContainerWidths.bind(this), CLIENTLIST_HEIGHT_OFFSET);
		this.setActiveView = this.setActiveView.bind(this);
		this.setShowSearchBox = this.setShowSearchBox.bind(this);
		this.getShowProcessMonitor = this.getShowProcessMonitor.bind(this);
		this.toggleOverlay = this.toggleOverlay.bind(this);
	}

	componentDidMount() {
		LoggerStore.addListener("FileMode", this.fileMode);
		LoggerStore.addListener("clientListVisibleChange", this.updateClientListVisible);
		LoggerStore.addListener("WindowVisibilityChange", this.updateWindowVisibility);
		LoggerStore.addListener("viewChange", this.setActiveView);
		LoggerStore.addListener("showSearchBoxChange", this.setShowSearchBox);
		LoggerStore.addListener("toggleOverlay", this.toggleOverlay);
		window.addEventListener("keydown", this.onkeydown);
		window.addEventListener("resize", this.onResizeWindow);
		this.getShowProcessMonitor();
	}

	componentWillUnmount() {
		LoggerStore.removeListener("FileMode", this.fileMode);
		LoggerStore.removeListener("clientListVisibleChange", this.updateClientListVisible);
		LoggerStore.removeListener("WindowVisibilityChange", this.updateWindowVisibility);
		LoggerStore.removeListener("viewChange", this.setActiveView);
		LoggerStore.removeListener("showSearchBoxChange", this.setShowSearchBox);
		LoggerStore.removeListener("toggleOverlay", this.toggleOverlay);
		window.removeEventListener("resize", this.onResizeWindow);
		window.removeEventListener("keydown", this.onkeydown);
	}

	toggleOverlay(data: string) {
		this.setState({
			showOverlay: !!data,
			overlayMessage: !!data ? data : null,
		});
	}

	getShowProcessMonitor() {
		// Wait for the launcher to come up, then figure out whether we should show the button to spawn the process monitor.
		const showProcessMonitor = (err, componentList) => {
			this.setState({
				showProcessMonitor: componentList && "Process Monitor" in componentList,
			});
		};
		RouterClient.subscribe("Launcher.update", (err, response) => {
			showProcessMonitor(err, response.data.componentList);
		});
		LauncherClient.getComponentList(showProcessMonitor);
	}

	setShowSearchBox() {
		this.setState({
			showSearchBox: LoggerStore.getShowSearchBox(),
		});
	}

	onkeydown(e: KeyboardEvent) {
		/**
		 * To avoid Hotkeys conflict, make sure that e.altKey
		 * is not there because `ctrl+alt+down` minimizes all
		 */
		if (e.ctrlKey && !e.altKey) {
			if (e.key === "ArrowUp") {
				LoggerStore.setTop();
			} else if (e.key === "ArrowDown") {
				LoggerStore.setBottom();
			} else if (e.key === "ArrowLeft" || e.key === "ArrowRight") {
				LoggerStore.scrollToActiveRow();
			} else if (e.key === "f") {
				LoggerStore.showSearchBox();
			}
			return;
		}

		if (this.state.showSearchBox && e.key === "Escape") {
			LoggerStore.hideSearchBox();
		}
	}

	setActiveView() {
		const activeView = LoggerStore.getActiveView();
		// prevent unnecessary renders...
		if (activeView === this.state.activeView) return;
		this.setState({
			activeView,
		});
	}

	fileMode() {
		this.setState({
			fileMode: LoggerStore.getFileMode(),
		});
	}

	updateClientListVisible() {
		const clientListVisible = LoggerStore.getClientListVisible();
		this.setState(
			{
				clientListVisible,
				logWidth: clientListVisible ? window.outerWidth - 500 : window.outerWidth - 16,
				clientListWidth: clientListVisible ? 500 : 0,
			},
			LoggerStore.triggerLogRerender.bind(LoggerStore)
		);
	}

	updateWindowVisibility() {
		this.setState({
			windowVisibility: LoggerStore.getWindowVisibility(),
		});
	}

	setContainerWidths(e: any) {
		// coming from the resize-grid. If not, coming from window.
		let { logWidth, clientListWidth, clientListHeight } = this.state;
		if (e.resizeChilds) {
			logWidth = e.resizeChilds[1].width;
			clientListWidth = e.resizeChilds[0].width;
		}
		clientListHeight = window.outerHeight - CLIENTLIST_HEIGHT_OFFSET;
		this.setState({ clientListWidth, logWidth, clientListHeight });
	}

	getClientListClasses() {
		if (this.state.clientListWidth < 500) {
			return "left-panel client-list-small";
		}
		return "left-panel";
	}

	onResizeWindow(e: UIEvent) {
		LoggerStore.triggerRowheightRecalculation();
		this.setContainerWidths(e);
	}

	// currently plain-text mode is never used, but hook left in case needed in future
	render() {
		if (this.state.windowVisibility === "hidden") return null;
		return (
			<div>
				{this.state.showOverlay && (
					<div className={`overlay ${this.state.showOverlay ? "active" : ""}`}>
						<div className="overlay-message">{this.state.overlayMessage}</div>
						<div className="loader">
							<img src={"./src/Finsemble-installation.gif"} />
						</div>
					</div>
				)}

				<div className="body-wrapper">
					<TopCollar activeView={this.state.activeView} showProcessMonitor={this.state.showProcessMonitor} />
					<div className="inner-content-wrapper">
						<ViewSwitcher activeView={this.state.activeView} />
						{this.state.activeView === "logger" && <LogView clientListVisible={this.state.clientListVisible} />}
						{this.state.activeView === "config" && <ConfigViewer />}
					</div>
				</div>
			</div>
		);
	}
}
