'use strict';

var _ = require('lodash');
var Async = require('async');

var Util = require('./util.js');

var BaseType = module.exports = function(spec) {
  if (spec) this.spec = spec;
  this.type = "base_type";
  this.converters = {};
}

BaseType.prototype.stringify = function() {
  return JSON.stringify(this.spec, null, 2);
}

BaseType.prototype.parsers = [];
BaseType.prototype.checkFormat = undefined;
BaseType.prototype.fixSpec = function () {
};
BaseType.prototype.resolveSubResources = function (source, sourceType, callback) {
  callback(null);
};

BaseType.prototype.parse = function (data, callback) {
  var self = this;
  if (!_.isString(data))
    callback(null, data);

  var result;
  var errMsg = 'Fail to parse spec:\n';
  Async.detect(self.parsers, function (parser, asyncCb) {
    parser(data, function (err, spec) {
      if (err)
        errMsg += '\t' + err;
      else
        result = spec;
      asyncCb(!err);
    });
  }, function (isParsed) {
    if (isParsed)
      callback(null, result);
    else
      callback(Error(errMsg));
  });
}

BaseType.prototype.readSpec = function (source, callback) {
  var self = this;
  var sourceType = Util.getSourceType(source);

  if (!sourceType)
    callback(Error('Spec source should be object, string, URL or filename.'));

  Util.resourceReaders[sourceType](source, function(err, data) {
    if (err) return callback(err);

    self.parse(data, function (err, spec) {
      if (err) return callback(err);

      callback(err, spec, sourceType);
    });
  });
}

BaseType.prototype.resolveResources = function(source, callback) {
  var self = this;

  self.readSpec(source, function(err, spec, sourceType) {
    if (err) return callback(err);

    if (!self.checkFormat(spec))
      return callback(Error('Incorrect format'));

    self.spec = spec;

    //In make cases URL has some usefull info
    if (sourceType === 'url')
      self.url = source;

    self.resolveSubResources(source, sourceType, function (error) {
      if (err) return callback(err);

      self.fixSpec();

      var version = self.getFormatVersion();
      if (self.supportedVersions.indexOf(version) === -1)
        return callback(Error('Unsupported version'));

      callback();
    });
  });
}

BaseType.prototype.convertTo = function(type, callback) {
  if (type === this.type)
    return callback(null, this);

  var convert = this.converters[type];
  if (!convert) {
    throw new Error("Unable to convert from " + this.type + " to " + type);
  }
  var converted = convert(this, callback);
  return converted;
}

'use strict';

var _ = require('lodash');
var Async = require('async');

var Util = require('./util.js');

var BaseType = module.exports = function(spec) {
  if (spec) this.spec = spec;
  this.type = "base_type";
  this.converters = {};
}

BaseType.prototype.stringify = function() {
  return JSON.stringify(this.spec, null, 2);
}

BaseType.prototype.parsers = [];
BaseType.prototype.checkFormat = undefined;
BaseType.prototype.fixSpec = function () {
};
BaseType.prototype.resolveSubResources = function (source, sourceType, callback) {
  callback(null);
};

BaseType.prototype.parse = function (data, callback) {
  var self = this;
  if (!_.isString(data))
    callback(null, data);

  var result;
  var errMsg = 'Fail to parse spec:\n';
  Async.detect(self.parsers, function (parser, asyncCb) {
    parser(data, function (err, spec) {
      if (err)
        errMsg += '\t' + err;
      else
        result = spec;
      asyncCb(!err);
    });
  }, function (isParsed) {
    if (isParsed)
      callback(null, result);
    else
      callback(Error(errMsg));
  });
}

BaseType.prototype.readSpec = function (source, callback) {
  var self = this;
  var sourceType = Util.getSourceType(source);

  if (!sourceType)
    callback(Error('Spec source should be object, string, URL or filename.'));

  Util.resourceReaders[sourceType](source, function(err, data) {
    if (err) return callback(err);

    self.parse(data, function (err, spec) {
      if (err) return callback(err);

      callback(err, spec, sourceType);
    });
  });
}

BaseType.prototype.resolveResources = function(source, callback) {
  var self = this;

  self.readSpec(source, function(err, spec, sourceType) {
    if (err) return callback(err);

    if (!self.checkFormat(spec))
      return callback(Error('Incorrect format'));

    self.spec = spec;

    //In make cases URL has some usefull info
    if (sourceType === 'url')
      self.url = source;

    self.resolveSubResources(source, sourceType, function (error) {
      if (error) return callback(error);

      self.fixSpec();

      var version = self.getFormatVersion();
      if (self.supportedVersions.indexOf(version) === -1)
        return callback(Error('Unsupported version'));

      callback();
    });
  });
}

BaseType.prototype.convertTo = function(type, callback) {
  if (type === this.type)
    return callback(null, this);

  var convert = this.converters[type];
  if (!convert) {
    throw new Error("Unable to convert from " + this.type + " to " + type);
  }
  var converted = convert(this, callback);
  return converted;
}

Fix error propagation