import {AbstractIconManager, kIPCIconChannel, RemoteIcon, RemoteIconState, setIconManager} from "tc-shared/file/Icons";
import * as loader from "tc-loader";
import {Stage} from "tc-loader";
import {ChannelMessage, IPCChannel} from "tc-shared/ipc/BrowserIPC";
import * as ipc from "tc-shared/ipc/BrowserIPC";
import {Settings} from "tc-shared/settings";
import {LogCategory, logWarn} from "tc-shared/log";

class RemoteRemoteIcon extends RemoteIcon {
    constructor(serverUniqueId: string, iconId: number) {
        super(serverUniqueId, iconId);
    }

    public setState(state: RemoteIconState) {
        super.setState(state);
    }

    public setErrorMessage(message: string) {
        super.setErrorMessage(message);
    }

    public setImageUrl(url: string) {
        super.setImageUrl(url);
    }
}

class RemoteIconManager extends AbstractIconManager {
    private readonly ipcChannel: IPCChannel;
    private callbackInitialized: () => void;

    private cachedIcons: {[key: string]: RemoteRemoteIcon} = {};

    constructor() {
        super();

        this.ipcChannel = ipc.getInstance().createChannel(Settings.instance.static(Settings.KEY_IPC_REMOTE_ADDRESS, "invalid"), kIPCIconChannel);
        this.ipcChannel.messageHandler = this.handleIpcMessage.bind(this);
    }

    async initialize() {
        this.ipcChannel.sendMessage("initialize", {});

        await new Promise((resolve, reject) => {
            const timeout = setTimeout(() => {
                this.callbackInitialized = undefined;
                reject(tr("initialize timeout"));
            }, 5000);

            this.callbackInitialized = () => {
                clearTimeout(timeout);
                resolve();
            }
        });
    }

    resolveIcon(iconId: number, serverUniqueId: string, handlerId?: string): RemoteIcon {
        iconId = iconId >>> 0;

        const uniqueId = RemoteIconManager.iconUniqueKey(iconId, serverUniqueId);
        if(this.cachedIcons[uniqueId]) { return this.cachedIcons[uniqueId]; }

        const icon = new RemoteRemoteIcon(serverUniqueId, iconId);
        this.cachedIcons[uniqueId] = icon;

        this.ipcChannel.sendMessage("icon-resolve", {
            iconId: iconId,
            serverUniqueId: serverUniqueId,
            handlerId: handlerId
        });
        return icon;
    }


    private handleIpcMessage(_remoteId: string, broadcast: boolean, message: ChannelMessage) {
        if(!broadcast) {
            if(message.type === "initialized") {
                if(this.callbackInitialized)
                    this.callbackInitialized();
            }
        }

        if(message.type === "notify-icon-status") {
            const icon = this.cachedIcons[message.data.iconUniqueId];
            if(!icon) { return; }

            switch (message.data.status as RemoteIconState) {
                case "destroyed":
                    delete this.cachedIcons[message.data.iconUniqueId];
                    icon.destroy();
                    break;

                case "empty":
                    icon.setState("empty");
                    break;

                case "error":
                    icon.setErrorMessage(message.data.errorMessage);
                    icon.setState("error");
                    break;

                case "loaded":
                    icon.setImageUrl(message.data.url);
                    icon.setState("loaded");
                    break;

                case "loading":
                    icon.setState("loading");
                    break;

                default:
                    logWarn(LogCategory.FILE_TRANSFER, tr("Received remote icon state change with an unknown state %s"), message.data.state);
                    break;
            }
        }
    }
}

loader.register_task(Stage.JAVASCRIPT_INITIALIZING, {
    priority: 10,
    name: "IPC icon init",
    function: async () => {
        let instance = new RemoteIconManager();
        await instance.initialize();
        setIconManager(instance);
    }
});