/*
 * Copyright 2011 eBay Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

require('raptor-polyfill/string/endsWith');

var repeatedId = require('./repeated-id');

/**
 * A WidgetDef is used to hold the metadata collected at runtime for
 * a single widget and this information is used to instantiate the widget
 * later (after the rendered HTML has been added to the DOM)
 */
function WidgetDef(config, endFunc, out) {
    this.type = config.type; // The widget module type name that is passed to the factory
    this.id = config.id; // The unique ID of the widget
    this.config = config.config; // Widget config object (may be null)
    this.state = config.state; // Widget state object (may be null)
    this.scope = config.scope; // The ID of the widget that this widget is scoped within
    this.domEvents = null; // An array of DOM events that need to be added (in sets of three)
    this.customEvents = config.customEvents; // An array containing information about custom events
    this.bodyElId = config.bodyElId; // The ID for the default body element (if any any)
    this.children = []; // An array of nested WidgetDef instances
    this.end = endFunc; // A function that when called will pop this widget def off the stack
    this.extend = config.extend; // Information about other widgets that extend this widget.
    this.out = out; // The AsyncWriter that this widget is associated with
    this.hasDomEvents = config.hasDomEvents; // A flag to indicate if this widget has any
                                             // listeners for non-bubbling DOM events
    this._nextId = 0; // The unique integer to use for the next scoped ID
}

WidgetDef.prototype = {
    /**
     * Register a nested widget for this widget. We maintain a tree of widgets
     * so that we can instantiate nested widgets before their parents.
     */
    addChild: function (widgetDef) {
        this.children.push(widgetDef);
    },
    /**
     * This helper method generates a unique and fully qualified DOM element ID
     * that is unique within the scope of the current widget. This method prefixes
     * the the nestedId with the ID of the current widget. If nestedId ends
     * with `[]` then it is treated as a repeated ID and we will generate
     * an ID with the current index for the current nestedId.
     * (e.g. "myParentId-foo[0]", "myParentId-foo[1]", etc.)
     */
    elId: function (nestedId) {
        if (nestedId == null) {
            return this.id;
        } else {
            if (typeof nestedId === 'string' && nestedId.endsWith('[]')) {
                return repeatedId.nextId(this.out, this.id, nestedId);
            } else {
                return this.id + '-' + nestedId;
            }
        }
    },
    /**
     * Registers a DOM event for a nested HTML element associated with the
     * widget. This is only done for non-bubbling events that require
     * direct event listeners to be added.
     * @param  {String} type The DOM event type ("mouseover", "mousemove", etc.)
     * @param  {String} targetMethod The name of the method to invoke on the scoped widget
     * @param  {String} elId The DOM element ID of the DOM element that the event listener needs to be added too
     */
    addDomEvent: function(type, targetMethod, elId) {

        if (!targetMethod) {
            // The event handler method is allowed to be conditional. At render time if the target
            // method is null then we do not attach any direct event listeners.
            return;
        }

        if (!this.domEvents) {
            this.domEvents = [];
        }
        this.domEvents.push(type);
        this.domEvents.push(targetMethod);
        this.domEvents.push(elId);
    },
    /**
     * Returns a string representation of the DOM events data.
     */
    getDomEventsAttr: function() {
        if (this.domEvents) {
            return this.domEvents.join(',');
        }
    },
    /**
     * Returns the next auto generated unique ID for a nested DOM element or nested DOM widget
     */
    nextId: function() {
        return this.id + '-w' + (this._nextId++);
    }
};

module.exports = WidgetDef;