import { serializable } from "../../engine/engine_serialization_decorator.js";
import { getParam } from "../../engine/engine_utils.js";
import { Behaviour } from "../Component.js";
import { EventList } from "../EventList.js";
import type { PlayableDirector } from "./PlayableDirector.js";

const debug = getParam("debugsignals")


/**
 * Used to reference a signal asset in a SignalReceiver. This is internally used by the {@link SignalReceiverEvent}.  
 */
export class SignalAsset {
    @serializable()
    guid!: string;
}

/**
 * An event that links a signal to a reaction.
 * Used internally by {@link SignalReceiver}.
 */
export class SignalReceiverEvent {
    @serializable(SignalAsset)
    signal!: SignalAsset;
    @serializable(EventList)
    reaction!: EventList<void>;
}

/** SignalReceiver is a component that listens for signals and invokes a reaction when a signal is received. 
 * Signals can be added to a signal track on a {@link PlayableDirector}
 * 
 * @summary Receives signals and invokes reactions
 * @category Animation and Sequencing
 * @group Components
 */
export class SignalReceiver extends Behaviour {

    private static receivers: { [key: string]: SignalReceiver[] } = {};

    static invoke(guid: string) {
        if (SignalReceiver.receivers[guid]) {
            const receivers = SignalReceiver.receivers[guid];
            if (!receivers) return;
            for (const rec of receivers)
                rec.invoke(guid);
        }
    }



    @serializable(SignalReceiverEvent)
    events?: SignalReceiverEvent[];

    /** @internal */
    awake(): void {
        if (debug) console.log("SignalReceiver awake", this);
    }

    /** @internal */
    onEnable(): void {
        if (this.events) {
            for (const evt of this.events) {
                if (!SignalReceiver.receivers[evt.signal.guid])
                    SignalReceiver.receivers[evt.signal.guid] = [];
                SignalReceiver.receivers[evt.signal.guid].push(this);
            }
        }
    }

    /** @internal */
    onDisable(): void {
        if (this.events) {
            for (const evt of this.events) {
                if (SignalReceiver.receivers[evt.signal.guid]) {
                    const idx = SignalReceiver.receivers[evt.signal.guid].indexOf(this);
                    if (idx >= 0)
                        SignalReceiver.receivers[evt.signal.guid].splice(idx, 1);
                }
            }
        }
    }

    invoke(sig: SignalAsset | string) {
        if (!this.events || !Array.isArray(this.events)) return;
        const id = typeof sig === "object" ? sig.guid : sig;
        for (const evt of this.events) {
            if (evt.signal.guid === id) {
                try {
                    if (!evt.reaction) {
                        console.warn("Missing reaction for signal", evt, this);
                        continue;
                    }
                    else if (!evt.reaction.invoke) {
                        console.warn("Missing invoke - possibly a serialization error", evt, this);
                        continue;
                    }
                    evt.reaction.invoke();
                }
                catch (err) {
                    console.error(err);
                }
            }
        }
    }

    // onDeserialize(key: string, value: any): any | void
    // {
    //     switch(key){
    //         case "events":
    //             console.log(value);
    //             const evt = eventListSerializer;
    //             for(const e in value){

    //             }
    //             break;
    //     }
    // }
}



// class SignalAssetSerializer extends TypeSerializer {
//     constructor() {
//         super("SignalReceiverEvent");
//     }

//     onSerialize(_data: EventList, _context: SerializationContext) {
//         console.log("TODO: SERIALIZE EVENT");
//         return undefined;
//     }

//     onDeserialize(data: SignalReceiverEvent, context: SerializationContext): EventList | undefined | null {
//         console.log("DESERIALIZE", data);
//         // if (data && data.type === "EventList") {
//         //     console.log("DESERIALIZE EVENT", data);
//         //     const fns = new Array<Function>();
//         //     for (const call of data.calls) {
//         //         const target = componentSerializer.findObjectForGuid(call.target, context.root);
//         //         let fn;
//         //         if (call.argument) {
//         //             let arg = call.argument;
//         //             if (typeof arg === "object") {
//         //                 arg = objectSerializer.onDeserialize(call.argument, context);
//         //                 if (!arg) arg = componentSerializer.onDeserialize(call.argument, context);
//         //             }
//         //             fn = () => target[call.method](arg);
//         //         }
//         //         else fn = () => target[call.method]();
//         //         fns.push(fn);
//         //     }
//         //     const evt: EventList = new EventList(fns);
//         //     return evt;
//         // }
//         return undefined;
//     }
// }
// new SignalAssetSerializer();