import React, { Component } from "react";
import * as ReactDom from "react-dom/client";
import { DAL } from "../DAL/systemInfo";
import {SystemInfo, CpuInfo, NetworkInfo, DriveInfo} from "../../src/models/systemInfo";
import menu from "./menu";

interface SystemInfoComponentState {
    loading: boolean;
    hidden: boolean;
}

interface SystemInfoProps {
    hostname: string;

    setHidden: any;//((val:boolean)=>void|null);
}

export class SystemInfoComponent extends Component<SystemInfoProps, SystemInfoComponentState> {
    private data: SystemInfo|undefined = undefined;

    public constructor(props: SystemInfoProps) {
        super(props);

        this.state = {
            loading: true,
            hidden: this.isFiltered()
        };
        DAL.SystemInfo.getInfo(this.props.hostname).then(data => {
            this.data = data; // FIXME error handling
            this.setState({...this.state, loading: false});
        });
        menu.addFilterEventListener(() => {
            this.refreshFilter();
        });
    }

    private isFiltered(): boolean {
        const filterShown = menu.getFilteredHostnames();
        return (filterShown.indexOf(this.props.hostname) < 0);
    }

    private refreshFilter() {
        const hidden = this.isFiltered();
        if (hidden === this.state.hidden)
            return;
        this.setState({...this.state, hidden: hidden});
    }

    private layout(content: React.JSX.Element) {
        return <section key={this.props.hostname} className={(this.state.hidden ? "hidden" : "")}><div><h1>{this.props.hostname}</h1>{content}</div></section>;
    }
    private statItem(key: string, value: string|React.JSX.Element, listKey?: number): React.JSX.Element {
        if (!value)
            return <></>;
        key = key.charAt(0).toUpperCase()+String(key).slice(1);
        if (listKey === undefined)
            return <li>{key}: {value}</li>;
        return <li key={listKey}>{key}: {value}</li>;
    }
    private statItemNumber(key: string, value: number): React.JSX.Element {
        if (!value || isNaN(value))
            return <></>;
        return this.statItem(key, `${value}`);
    }
    private statItemMhz(key: string, value: number): React.JSX.Element {
        if (!value || isNaN(value))
            return <></>;
        return this.statItem(key, `${value}`);
    }
    private stringifyByte(value: number): string {
        if (!value || isNaN(value))
            return "";
        let units = ["Byte", "kB", "MB", "GB", "TB", "PB"];
        let unitIndex = 0;
        while (value > 1024 && unitIndex < units.length) {
            value /= 1024;
            unitIndex++;
        }
        value = Math.round(value*100)/100;
        return `${value}${units[unitIndex]}`;
    }
    private statItemBytes(key: string, value: number): React.JSX.Element {
        if (!value || isNaN(value))
            return <></>;
        return this.statItem(key, this.stringifyByte(value));
    }
    private statItemReadOnly(key: string, value: boolean|undefined): React.JSX.Element {
        if (value === undefined)
            return <></>;
        return this.statItem(key, value ? "Read-Write" : "Read Only");
    }
    private statItemBoolean(key: string, value: boolean|undefined): React.JSX.Element {
        if (value === undefined)
            return <></>;
        return this.statItem(key, value ? "true" : "false");
    }
    private statItemArray(key: string, values: string[]): React.JSX.Element {
        return this.statItem(key, <ul>{values.map((x, idx) => <li key={idx}>{x}</li>)}</ul>);
    }
    private statItemFragArray(key: string, values: React.JSX.Element[]): React.JSX.Element {
        return this.statItem(key, <ul>{values}</ul>);
    }
    private uptime(data: SystemInfo): string {
        let totalTime = data.uptime;
        let timeInSec = Math.floor(totalTime % 60);
        totalTime /= 60;
        let timeInMin = Math.floor(totalTime % 60);
        totalTime /= 60;
        let timeInHours = Math.floor(totalTime % 24);
        totalTime /= 24;
        let timeInDays = Math.floor(totalTime);
        let result = "";
        if (timeInDays)
            result += `${timeInDays}d, `;
        if (result.length || timeInHours)
            result += (`${timeInHours}:`).padStart(3, "0");
        result += (`${timeInMin}`).padStart(2, "0")+':'+(`${timeInSec}`).padStart(2, "0");
        return result;
    }
    private cpuInfos(cpu: CpuInfo, cpuIndex: number): React.JSX.Element {
        return this.statItem("model", cpu.model, cpuIndex);
    }
    private networkInfo(network: NetworkInfo, netIndex: number): React.JSX.Element {
        return <li key={netIndex}><ul>
            {this.statItem("Interface", network.iface)}
            {this.statItem("Address", network.address)}
            {this.statItem("Netmask", network.netmask)}
            {this.statItem("Family", network.family)}
            {this.statItem("Mac Address", network.mac)}
            </ul></li>;
    }
    private driveInfo(drive: DriveInfo, driveIdx: number): React.JSX.Element {
        return <li key={driveIdx}><ul>
            {this.statItem("name", drive.name)}
            {this.statItem("type", drive.type)}
            {this.statItem("Mount point", drive.mount)}
            {this.statItem("label", drive.label)}
            {this.statItemBytes("Size", drive.size)}
            {this.statItemBytes("Used Size", drive.usedSize)}
            {this.statItemBytes("Remaining Size", drive.size - drive.usedSize)}
            {this.statItemNumber("Used (%)", Math.round(10000 * drive.usedSize / drive.size) / 100)}
            {this.statItem("Physical", drive.physical)}
            {this.statItem("UUID", drive.uuid)}
            {this.statItem("model", drive.model)}
            {this.statItem("serial", drive.serial)}
            {this.statItemBoolean("removable", drive.removable)}
            {this.statItem("protocol", drive.protocol)}
            {this.statItem("device", drive.device)}
            {this.statItemReadOnly("Access", drive.rw)}
            </ul></li>;
    }
    public render(): React.JSX.Element {
        if (this.state.loading)
            return this.layout(<>Loading..</>);
        else if (this.data === undefined)
            return this.layout(<>Error</>);
        const data = this.data!;
        return this.layout(<>
            <ul>
            {this.statItem("Platform", data.platform)}
            {this.statItem("Distribution", data.distribution)}
            {this.statItem("Architecture", data.arch)}
            {this.statItem("OS Version", data.osVersion)}
            {this.statItem("uptime", this.uptime(data))}
            {this.statItem("Architecture", data.arch)}
            {this.statItem("Node Version", data.nodeVersion)}
            </ul>
            <ul>
            {this.statItem("Manufacturer", data.manufacturer)}
            {this.statItem("Model", data.model)}
            {this.statItemMhz("CPU Max Speed", data.cpuMaxSpeed)}
            {this.statItemFragArray("Cpus", data.cpus.map((x, idx) => this.cpuInfos(x, idx)))}
            {this.statItemBytes("Memory", data.memory)}
            {this.statItem("Memory Layout", <ul>{data.memoryLayout.map((x, idx) => <li key={idx}>{this.stringifyByte(x)}</li>)}</ul>)}
            </ul>
            <ul>
            {this.statItemArray("DNS Servers", data.dnsServers)}
            {this.statItemFragArray("Network Interfaces", data.network.map((x, idx) => this.networkInfo(x, idx)))}
            </ul>
            <ul>
            {this.statItemFragArray("Drives", data.drives.map((x, idx) => this.driveInfo(x, idx)))}
            </ul>
            </>);
    }

    public static async renderMultiple(container: HTMLElement): Promise<React.JSX.Element[]> {
        let domNodes = (await DAL.SystemInfo.listHosts()).map(hostname => <SystemInfoComponent key={hostname} setHidden={null} hostname={hostname}/>);
        ReactDom.createRoot(container).render(<>{domNodes}</>);
        return domNodes;
    }
}

