var _ = require('underscore')
    , util = require('util');

function AWSResource(type, propdefinitions, name, properties, attributes) {
    if (!(this instanceof AWSResource)) {
        return new AWSResource(type, propdefinitions, name, properties, attributes);
    }

    this.type = type;
    this.name = name;
    this.properties = properties;
    this.propdefinitions = propdefinitions;
    this.attributes = attributes;
}

function ValidationError() {
    var tmp = Error.apply(this, arguments);
    tmp.name = this.name = 'ValidationError';

    this.stack = tmp.stack;
    this.message = tmp.message;
}
var IntermediateInheritor = function() {}
IntermediateInheritor.prototype = Error.prototype;
ValidationError.prototype = new IntermediateInheritor();

AWSResource.ValidationError = ValidationError;

AWSResource.prototype.validate = function() {
    var propdefinitions = this.propdefinitions;
    var mandatoryPropertyNames = _.reduce(propdefinitions, function(memo, value, key) {
        if (value.required === true) {
            memo.push(key);
        }
        return memo;
    }, []);

    var self = this;

    // check if mandatory properties are set
    mandatoryPropertyNames.forEach(function(propname) {
        var propvalue = self.properties[propname];
        if (typeof propvalue === 'undefined') {
            throw new AWSResource.ValidationError(util.format('Mandatory property "%s" is not set', propname));
        }
    });

    var propertyNames = Object.keys(this.properties);
    propertyNames.forEach(function(propname) {
        var propvalue = self.properties[propname];
        var def = propdefinitions[propname] || {};
        var type = def.type;
        if (typeof type === 'undefined') {
            throw new AWSResource.ValidationError(util.format('Internal error, missing type for property "%s"', propname));
        }
        if (typeof propvalue === 'undefined' && !def.required) {
            return;
        }
        if (!type.validate(propvalue)) {
            var value = propvalue;
            if (typeof propvalue === 'object') {
                value = JSON.stringify(propvalue);
            }
            throw new AWSResource.ValidationError(util.format('Resource "%s": Invalid value for "%s" should be of type "%s" got "%s"', self.name, propname, type.name, value));
        }
        if (def.validators instanceof Array) {
            _.forEach(def.validators, function(validator) {
                var result = validator(self);
                if (typeof result !== 'undefined') {
                    throw new AWSResource.ValidationError(util.format('Resource "%s": Value for "%s" failed validation: "%s"', self.name, propname, result));
                }
            });
        }
    });
};

// TODO: rename to "toCloudFormationTemplate"
AWSResource.prototype.toObject = function() {
    this.validate();

    var propdefinitions = this.propdefinitions;
    var properties = _.mapObject(this.properties, function(value, key) {
        var def = propdefinitions[key] || {};
        if (def.type && def.type.toCloudFormationValue) {
            return def.type.toCloudFormationValue(value);
        }
        return value;
    });

    var object = {
        Type: this.type,
        Properties: properties,
    };

    object = _.extend(object, this.attributes);

    return object;
};

AWSResource.define = function(type, propdefinitions) {
    function Resource(name, properties, attributes) {
        if (!(this instanceof Resource)) {
            return new Resource(name, properties, attributes);
        }

        AWSResource.call(this, type, propdefinitions, name, properties, attributes);
    }
    Resource.prototype = Object.create(AWSResource.prototype);
    Resource.prototype.constructor = Resource;

    return Resource;
};

module.exports = AWSResource;

@@ -1373,67 +1373,184 @@
 ror(
-util.format('Mandatory property %22%25s%22 is not set', propname)
+%0A                util.format(%0A                    'Resource %22%25s%22: Mandatory property %22%25s%22 is not set',%0A                    self.name,%0A                    propname)%0A            
 );%0A 
@@ -1896,79 +1896,196 @@
 ror(
-util.format('Internal error, missing type for property %22%25s%22', propname)
+%0A                util.format(%0A                    'Resource %22%25s%22: Internal error, missing type for property %22%25s%22',%0A                    self.name,%0A                    propname)%0A            
 );%0A 
@@ -2424,36 +2424,74 @@
 dationError(
-util.format(
+%0A                util.format(%0A                    
 'Resource %22%25
@@ -2554,47 +2554,140 @@
 s%22',
- self.name, propname, type.name, value)
+%0A                    self.name,%0A                    propname,%0A                    type.name,%0A                    value)%0A            
 );%0A 
@@ -2963,105 +2963,264 @@
 ror(
-util.format('Resource %22%25s%22: Value for %22%25s%22 failed validation: %22%25s%22', self.name, propname, result)
+%0A                        util.format(%0A                            'Resource %22%25s%22: Value for %22%25s%22 failed validation: %22%25s%22',%0A                            self.name,%0A                            propname,%0A                            result)%0A                    
 );%0A 

Add 'Resource "%s":' to some error messages for easier debugging