import Utilities from './Utilities.js'

export default class MessageHandler {

constructor(serverInteractor, objectManager, logger) {
    this.serverInteractor = serverInteractor;
    this.objectManager = objectManager;
    this.logger = logger;
    this.eventHandlers = {};
}
handle(message) {
    switch (message.action) {
        case "create": this.handleCreateMessage(message); break;
        case "update": this.handleUpdateMessage(message); break;
        case "destroy": this.handleDestroyMessage(message); break;
        case "invoke": this.handleInvokeMessage(message); break;
        default:
            this.logger.log(`[MessageHandler] Unknown action ${message.action}`);
    }
}
handleCreateMessage(message) {
    const object = document.createElement(message.tag);
    this.objectManager.insert(message.id, object);
    this.objectManager.get("root").appendChild(object);
    this.handleUpdateMessage(message);
}
handleUpdateMessage(message) {
    Object.keys(message.changes).forEach(change => {
        if (change === "children") {
            this.handleUpdateChildrenMessage(message);
        } else if (change === "events") {
            this.handleUpdateEventsMessage(message);
        } else if (!(change in this.objectManager.get(message.id))) {
            this.logger.log(`[MessageHandler] Unknown attribute '${change}' for ${message.id}`);
        } else if (change === "style") {
            this.handleUpdateStyleMessage(message);
        } else {
            this.handleUpdateAttributeMessage(change, message);
        }
    });
}
handleUpdateAttributeMessage(attr, message) {
    const object = this.objectManager.get(message.id);
    object[attr] = message.changes[attr];
    object.setAttribute(attr, message.changes[attr]);
}
handleUpdateChildrenMessage(message) {
    const object = this.objectManager.get(message.id);
    for (let i = 0; i < message.changes.children.length; i++) {
        const childId = message.changes.children[i];
        const child = this.objectManager.get(childId);
        Utilities.insertAt(object, child, i);
    }
}
handleUpdateEventsMessage(message) {
    const me = this;
    const object = this.objectManager.get(message.id);
    // remove previous event handlers
    if (me.eventHandlers[message.id]) {
        Object.keys(me.eventHandlers[message.id]).forEach(eventName => {
            object.removeEventListener(eventName, me.eventHandlers[message.id][eventName]);
        });
    }
    me.eventHandlers[message.id] = {};
    // add new handlers
    message.changes.events.forEach(eventName => {
        me.eventHandlers[message.id][eventName] = function (e) {
            const data = JSON.parse(Utilities.stringifyEvent(e));
            if (object.tagName === "FORM") {
                const formData = new FormData(object);
                formData.forEach(function (value, key) {
                    data[key] = value;
                });
            }
            if (eventName === "dragstart") {
                e.dataTransfer.setData("text/plain", object.id);
            } else if (e.dataTransfer) {
                data.target = e.dataTransfer.getData("text/plain");
            }
            me.serverInteractor.send({
                action: "event",
                id: message.id,
                event: eventName,
                data: data
            });
            return false;
        };
        object.addEventListener(eventName, me.eventHandlers[message.id][eventName], false);
    });
}
handleUpdateStyleMessage(message) {
    const object = this.objectManager.get(message.id);
    Object.keys(message.changes.style).forEach(style => {
        object.style[style] = message.changes.style[style];
    });
}
handleDestroyMessage(message) {
    this.objectManager.get(message.id).remove();
    this.objectManager.remove(message.id);
}
handleInvokeMessage(message) {
    const object = this.objectManager.get(message.id);
    if (!(functor in object)) {
        this.logger.log(`[ObjectManager] Trying to invoke a non-existing functor ${message.functor}!`);
        return;
    }
    for (let i = 0; i < args.length; i++) {
        if (this.objectManager.contains(args[i]))
            args[i] = this.objectManager.get(args[i]);
    }
    this.send({
        action: "response",
        token: message.token,
        returnValue: object[functor].apply(object, args)
    });
}

}