module.exports = {
    bind: function (device, actor) {
        utils.inheritMethods(actor, new Actor());
        utils.inheritMethods(actor, require(device.findActorType(actor.type).modulePath).create());

        // TODO Put in bind() method

        actor.device = device;
        actor.eventProcessors = [];
        device[actor.id] = actor;

        return actor;
    }
};

var q = require('q');
var utils = require("./utils");
var logger = require("./logger");

/**
 *
 */
function Actor() {
    this.class = "Actor";

    utils.inheritMethods(this, logger.create());

    /**
     * Required for logging.
     */
    Actor.prototype.publishMessage = function (content) {
        this.device.node.publishMessage(content);
    };

    /**
     *
     * @returns {{id: *, label: *, type: *, configuration: *}}
     */
    Actor.prototype.clientCopy = function () {
        return {
            id: this.id,
            label: this.label,
            type: this.type,
            configuration: this.configuration // TODO Full copy?
        };
    };

    /**
     *
     */
    Actor.prototype.startActor = function () {
   
        // For logging

        this.class = "Actor";
        this.operationalState = {status: 'NA', message: 'NA'};

        this.logInfo("Starting Actor [" + this.label + "]");

        utils.restoreState(this);   
        this.publishState();
        this.start()
            .then(function () {
                this.publishOperationalStateChange();
                this.publishStateChange();

                this.logInfo("Actor [" + this.label + "] started.");
            }.bind(this))
            .fail(function (error) {
                this.logError(error);
                this.operationalState = {
                    status: 'ERROR', 
                    message: "Failed to start Actor [" + this.label
                    + "]: " + error.message
                };
                this.publishOperationalStateChange();

            }.bind(this));

    };

    /**
     *
     */
    Actor.prototype.stopActor = function () {
        var deferred = q.defer();

        if (this.stop) {
            this.stop();
        }

        this.logInfo("Actor [" + this.name + "] stopped.");
        deferred.resolve();

        return deferred.promise;
    };

    /**
     * There are Actors that can also interact as Sensors and therefore would need to publish events.
     * This is one more indication to homogenizing Actors and Sensors and just work with one concept.
     */
    Actor.prototype.publishEvent = function (event, data) {
        for (var n = 0; n < this.eventProcessors.length; ++n) {
            this.eventProcessors[n].notifyOnSensorEvent(this, {
                type: event,
                data: data
            });
        }

        this.device.node.publishActorEvent(this.device, this, event, data);
    };

    /**
     *
     */
    Actor.prototype.publishOperationalStateChange = function () {
        for (var n in this.eventProcessors) {
            this.eventProcessors[n].notifyOnActorOperationalStateChange(this, this.operationalState);
        }

        this.device.node.publishActorOperationalStateChange(this.device, this, this
            .operationalState);
    };

    /**
     *
     */
    Actor.prototype.publishStateChange = function (change) {
        if (change) {
            for (var n in this.eventProcessors) {
                this.eventProcessors[n].notifyOnActorStateChange(this, this.state);
            }
            this.device.node.publishActorStateChange(this.device, this, change);
        } else {

            this.publishState();
        }

    };

    /**
     *
     */
    Actor.prototype.publishState = function () {
        for (var n in this.eventProcessors) {
            this.eventProcessors[n].notifyOnActorStateChange(this, this.state);
        }

        this.device.node.publishActorStateChange(this.device, this, this.getState());
    };

    /**
     *
     */
    Actor.prototype.publishStateChangeHistory = function (history) {
        this.device.node.publishActorStateChangeHistory(this.device, this, history);
    };

    /**
     *
     */
    Actor.prototype.isSimulated = function () {
        return this.configuration.simulated || this.device.isSimulated();
    };
}