import { EventHandler } from '../../core/event-handler.js';

/**
 * @class
 * @name pc.Component
 * @augments pc.EventHandler
 * @classdesc Components are used to attach functionality on a {@link pc.Entity}. Components
 * can receive update events each frame, and expose properties to the PlayCanvas Editor.
 * @description Base constructor for a Component.
 * @param {pc.ComponentSystem} system - The ComponentSystem used to create this Component.
 * @param {pc.Entity} entity - The Entity that this Component is attached to.
 * @property {pc.ComponentSystem} system The ComponentSystem used to create this Component.
 * @property {pc.Entity} entity The Entity that this Component is attached to.
 * @property {boolean} enabled Enables or disables the component.
 */
function Component(system, entity) {
    EventHandler.call(this);

    this.system = system;
    this.entity = entity;

    if (this.system.schema && !this._accessorsBuilt) {
        this.buildAccessors(this.system.schema);
    }

    this.on("set", function (name, oldValue, newValue) {
        this.fire("set_" + name, name, oldValue, newValue);
    });

    this.on('set_enabled', this.onSetEnabled, this);
}
Component.prototype = Object.create(EventHandler.prototype);
Component.prototype.constructor = Component;

Component._buildAccessors = function (obj, schema) {
    // Create getter/setter pairs for each property defined in the schema
    schema.forEach(function (descriptor) {
        // If the property descriptor is an object, it should have a `name`
        // member. If not, it should just be the plain property name.
        var name = (typeof descriptor === 'object') ? descriptor.name : descriptor;

        Object.defineProperty(obj, name, {
            get: function () {
                return this.data[name];
            },
            set: function (value) {
                var data = this.data;
                var oldValue = data[name];
                data[name] = value;
                this.fire('set', name, oldValue, value);
            },
            configurable: true
        });
    });

    obj._accessorsBuilt = true;
};

Object.assign(Component.prototype, {
    buildAccessors: function (schema) {
        Component._buildAccessors(this, schema);
    },

    onSetEnabled: function (name, oldValue, newValue) {
        if (oldValue !== newValue) {
            if (this.entity.enabled) {
                if (newValue) {
                    this.onEnable();
                } else {
                    this.onDisable();
                }
            }
        }
    },

    onEnable: function () { },

    onDisable: function () { },

    onPostStateChange: function () { }
});

/**
 * @private
 * @property {pc.ComponentData} data Access the {@link pc.ComponentData} directly.
 * Usually you should access the data properties via the individual properties as
 * modifying this data directly will not fire 'set' events.
 */
Object.defineProperty(Component.prototype, "data", {
    get: function () {
        var record = this.system.store[this.entity.getGuid()];
        return record ? record.data : null;
    }
});

export { Component };
