package movim.widget;

import movim.Session;

class Wrapper {
    private static var instance : Wrapper;

    private var _widgets : Array<String> = [];
    private var _events : Map<String,Array<String>> = new Map();
    private var _eventWidgets : Array<String> = [];

    private var _view : String = ''; // The current page where the widget is displayed

    private var css : Array<String> = []; // All the css loaded by the widgets so far.
    private var js : Array<String> = []; // All the js loaded by the widgets so far.

    public var title : String = null; // If a widget has defined a particular title
    public var image : String = null; // If a widget has defined a particular image
    public var description : String = null; // If a widget has defined a particular description
    public var url : String = null; // If a widget has defined a particular url
    public var links : Array<String> = []; // If a widget have to inject a link in the header

    public function registerAll(?load:Array<String>) : Void {
        //widgets_dir = scandir(APP_PATH +"widgets/");

        for(widget in movim.Bootstrap.getWidgets()) {
            if(load != null && load.indexOf(widget) != -1) {
              this.loadWidget(widget, true);
            }
        }
    }

    static public function getInstance() : Wrapper {
        if(Wrapper.instance == null) {
            Wrapper.instance = new Wrapper();
        }
        return Wrapper.instance;
    }

    public function new() {}

    static public function destroyInstance() : Void {
        if(Wrapper.instance != null) {
            Wrapper.instance = null;
        }
    }

    /**
     * @desc Set the view
     * @param $page the name of the current view
     */
    public function setView(view : String) : Void{
        this._view = view;
    }

    /**
     * @desc Loads a widget and returns it
     * @param $name the name of the widget
     * @param $register know if we are loading in the daemon or displaying
     */
    public function loadWidget(name : String, register:Bool=false) : Base {
        var class_name : Class<Dynamic> = null;
        if(Type.resolveClass('app.widgets.' + name.toLowerCase() + '.' + name) != null) {
            class_name = Type.resolveClass('app.widgets.' + name.toLowerCase() + '.' + name);
        } else {
            throw movim.i18n.Locale.start().translate('error.widget_load_error') + name;
        }

        var widget : Base = null;
        if(register) {
            widget = Type.createInstance(class_name, [true]);
            // We save the registered events of the widget for the filter
            if(widget.events != null) {
                for(key in widget.events.keys()) {
                    if(this._events[key] != null) {
                        if(this._events[key].indexOf(name) == -1) this._events[key].push(name);
                    } else {
                        this._events[key] = [name];
                    }
                }
                this._eventWidgets.push(name);
            }
        } else {
            if(this._view != '') {
                widget = Type.createInstance(class_name, [false, this._view]);
            } else {
                widget = Type.createInstance(class_name, []);
            }

            if(true) { //(php_sapi_name() != 'cli') {
                // Collecting stuff generated by the widgets.
                this.css = this.css.concat(widget.loadcss());
                this.js = this.js.concat(widget.loadjs());

                if(widget.title != null) this.title = widget.title;
                if(widget.image != null) this.image = widget.image;
                if(widget.description != null) this.description = widget.description;
                if(widget.url != null) this.url = widget.url;
                if(widget.links != null) this.links = this.links.concat(widget.links);
            }
        }
        return widget;
    }

    /**
     * @desc Loads a widget and runs a particular function on it.
     * @param $widget_name is the name of the widget.
     * @param $method is the function to be run.
     * @param $params is an array containing the parameters to
     *   be passed along to the method.
     * @return what the widget's method returns.
     */
    public function runWidget(widget_name : String, method : String, params : Array<Dynamic> ) : Dynamic {
        var widget = this.loadWidget(widget_name);

        if(params == null) {
            params = [];
        }

        return Reflect.callMethod(widget, Reflect.field(widget, method), params);
    }

    /**
     * Calls a particular function with the given parameters on
     * all loaded widgets.
     * @param $key is the key of the incoming event
     * @param $data is the Packet that is sent as a parameter
     */
    public function iterate(key : String, data : String) {
        if(this._events.exists(key)) {
            for(widget_name in this._events[key]) {
                var widget : Base = Type.createInstance(Type.resolveClass('app.widgets.' + widget_name.toLowerCase() + '.' + widget_name), [true]);
                if(widget.events.exists(key)) {
                    for(method in widget.events[key]) {
                        /*
                         * We check if the method need to be called if the
                         * session notifs_key is set to a specific value
                         */
                        if(method.filter != null) {
                            var session = Session.start();
                            var notifs_key = session.get('notifs_key');

                            if(notifs_key == 'blurred') {
                              method.func(data);
                            } else {
                                var explode = notifs_key.split('|');
                                var notif_key = explode[0];
                                if(notif_key == method.filter) {
                                  method.func(data);
                                }
                            }
                        } else {
                          method.func(data);
                        }
                    }
                }
            }
        }
    }

    /**
     * @desc Returns the list of loaded CSS.
     */
    public function loadcss() : Array<String> {
        return this.css;
    }

    /**
     * @desc Returns the list of loaded javascripts.
     */
    public function loadjs() : Array<String> {
        return this.js;
    }
}
