/**
 * Most of the ForgeJS objects extends FORGE.BaseObject.<br>
 * Its main purpose is to name objects with a class name (a type) and to have a destroy method that handles an "alive" flag.
 *
 * @constructor FORGE.BaseObject
 * @param {string} className - The class name of the object.
 *
 * @todo  See if we can trace inheritance.
 */
FORGE.BaseObject = function(className)
{
    /**
     * The unique identifier of this object.
     * @name  FORGE.BaseObject#_uid
     * @type {string}
     * @private
     */
    this._uid = "";

    /**
     * Array of tags that can be used to identify / classify this object.
     * @name  FORGE.BaseObject#_tags
     * @type {?Array<string>}
     * @private
     */
    this._tags = null;

    /**
     * Custom data associated to this object.
     * @name  FORGE.BaseObject#_data
     * @type {?*}
     * @private
     */
    this._data = null;

    /**
     * Internal refernce to the onDestroy {@link FORGE.EventDispatcher}.
     * @name FORGE.BaseObject#_onDestroy
     * @type {?FORGE.EventDispatcher}
     * @private
     */
    this._onDestroy = null;

    /**
     * Internal reference to the alive flag.
     * @name FORGE.BaseObject#_alive
     * @type {boolean}
     * @private
     */
    this._alive = true;

    /**
     * Internal reference to the debug flag.
     * @name FORGE.BaseObject#_debug
     * @type {boolean}
     * @private
     */
    this._debug = false;

    /**
     * Internal reference to the warning flag.
     * @name  FORGE.BaseObject#_warning
     * @type {boolean}
     * @private
     */
    this._warning = false;

    /**
     * Array to store log if debug is enabled.
     * @name FORGE.BaseObject#_logs
     * @type {?Array}
     * @private
     */
    this._logs = null;

    /**
     * Internal reference to the class name of the object that extends this base object.
     * @name FORGE.BaseObject#_className
     * @type {string}
     * @private
     */
    this._className = className || "BaseObject";

    /**
     * Inheritance chain.
     * @name FORGE.BaseObject#_inheritance
     * @type {Array<String>}
     * @private
     * @todo  Code this mechanism of type chain inheritance
     */
    this._inheritance = ["BaseObject"];
};

FORGE.BaseObject.prototype.constructor = FORGE.BaseObject;


/**
 * Registers the object in the uid index and bind to tag manager if tags set.
 * If no uid set, use a generated one.
 * @method FORGE.BaseObject#_register
 * @private
 */
FORGE.BaseObject.prototype._register = function()
{
    this.log("register");

    //Generate a uid if undefined
    if(typeof this._uid !== "string" || this._uid === "")
    {
        this._uid = FORGE.UID.generate();
    }
    //Register in UID table
    var registered = FORGE.UID.register(this);

    //If this object have tags associated to it.
    if(this._tags !== null)
    {
        //Maybe there is a single string typed tag, convert it to an array.
        if(typeof this._tags === "string")
        {
            this._tags = [this._tags];
        }

        //Register tags if it is an Array
        if(Array.isArray(this._tags) === true)
        {
            FORGE.Tags.register(this);
        }
    }

    return registered;
};

/**
 * Unregisters the object in the uid index.
 * @method FORGE.BaseObject#_unregister
 * @private
 */
FORGE.BaseObject.prototype._unregister = function()
{
    this.log("unregister");

    if(this._uid !== "" && FORGE.UID.exists(this._uid) === true)
    {
        FORGE.UID.unregister(this);
    }
};

/**
 * That method describe how to output the log, can be overwritted by a debug plugin for example.
 * @method FORGE.BaseObject#_stdout
 * @private
 * @param {*} value - The value you want to stdout.
 * @param {string} mode - The console method to use (default is log)
 */
FORGE.BaseObject.prototype._stdout = function(value, mode)
{
    var m = mode || "log";
    var consoleLog = [];
    if (FORGE.Device.chrome === true || FORGE.Device.firefox === true)
    {
        consoleLog = [ "%c[ForgeJS]%c "
            + "FORGE." + this._className + ": " + value + " %c(@"
            + window.performance.now().toFixed(2) + "ms)",
            "background: #e2edff; color: #4286f4; font-weight: 700;",
            "font-weight: 400;",
            "color: #AAA;"
            ];
    }
    else
    {
        consoleLog = [ "[ForgeJS] FORGE." + this._className + ": " + value + " (@"
            + window.performance.now().toFixed(2) + "ms)"
            ];
    }
    console[m].apply(console, consoleLog);

    if(typeof value === "object" && value !== null)
    {
        console[m](value);
    }
};

/**
 * Basic log method, log a string in the console if debug is enabled.
 * @method FORGE.BaseObject#log
 * @param {*} value - The value you want to log in the console.
 */
FORGE.BaseObject.prototype.log = function(value)
{
    if(window["FORGE"]["DEBUG"] === true || window["FORGE"][this._className]["DEBUG"] === true || this._debug === true)
    {
        this._stdout(value, "log");

        if(this._logs === null)
        {
            this._logs = [];
        }

        this._logs.push(value);
    }
};

/**
 * Basic warn method, log a warn string in the console if warning is enabled.
 * @method FORGE.BaseObject#warn
 * @param {?(string|Object)} value - The value you want to warn in the console.
 */
FORGE.BaseObject.prototype.warn = function(value)
{
    if(window["FORGE"]["WARNING"] === true || window["FORGE"][this._className]["WARNING"] === true || this._warning === true)
    {
        this._stdout(value, "warn");
    }
};

/**
 * Basic destroy method, prevent double destroy, change the alive flag.
 * @method FORGE.BaseObject#destroy
 */
FORGE.BaseObject.prototype.destroy = function()
{
    if(this._alive === false)
    {
        return;
    }

    this.log("destroy");

    this._unregister();

    if(this._onDestroy !== null)
    {
        this._onDestroy.dispatch();
        this._onDestroy.destroy();
        this._onDestroy = null;
    }

    this._data = null;

    this._alive = false;
};

/**
 * Get the class name of the object.
 * @name FORGE.BaseObject#className
 * @readonly
 * @type {string}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "className",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._className;
    }

});

/**
 * Get the uid of the object.
 * @name FORGE.BaseObject#uid
 * @readonly
 * @type {string}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "uid",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._uid;
    }
});

/**
 * Get the tags associated to this object.
 * @name FORGE.BaseObject#tags
 * @readonly
 * @type {Array}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "tags",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._tags;
    }
});

/**
 * Get the alive flag value of the object.
 * @name FORGE.BaseObject#alive
 * @readonly
 * @type {boolean}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "alive",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._alive;
    }
});

/**
* Get and set any custom data you want to associate to this object.
* @name FORGE.BaseObject#data
* @type {*}
*/
Object.defineProperty(FORGE.BaseObject.prototype, "data",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._data;
    },

    /** @this {FORGE.BaseObject} */
    set: function(value)
    {
        this._data = value;
    }
});

/**
 * Get and set the debug flag.
 * @name FORGE.BaseObject#debug
 * @type {boolean}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "debug",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._debug;
    },

    /** @this {FORGE.BaseObject} */
    set: function(value)
    {
        this._debug = Boolean(value);

        if(this._debug === true)
        {
            console.log("Enabling debug for a FORGE."+this._className+" instance :");
            console.log(this);
        }
    }
});

/**
 * Get and set the warning flag.
 * @name FORGE.BaseObject#warning
 * @type {boolean}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "warning",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        return this._warning;
    },

    /** @this {FORGE.BaseObject} */
    set: function(value)
    {
        this._warning = Boolean(value);

        if(this._warning === true)
        {
            console.log("Enabling warning for a FORGE."+this._className+" instance :");
            console.log(this);
        }
    }
});

/**
 * Get the onDestroy {@link FORGE.EventDispatcher}, this event is emitted at the end of the destroy sequence.
 * @name FORGE.BaseObject#onDestroy
 * @readonly
 * @type {FORGE.EventDispatcher}
 */
Object.defineProperty(FORGE.BaseObject.prototype, "onDestroy",
{
    /** @this {FORGE.BaseObject} */
    get: function()
    {
        if(this._onDestroy === null)
        {
            this._onDestroy = new FORGE.EventDispatcher(this);
        }

        return this._onDestroy;
    }
});