'use strict';

/*eslint-disable max-len*/

var common = require('./common'); var YAMLException = require('./exception'); var Type = require('./type');

function compileList(schema, name, result) {

var exclude = [];

schema.include.forEach(function (includedSchema) {
  result = compileList(includedSchema, name, result);
});

schema[name].forEach(function (currentType) {
  result.forEach(function (previousType, previousIndex) {
    if (previousType.tag === currentType.tag && previousType.kind === currentType.kind) {
      exclude.push(previousIndex);
    }
  });

  result.push(currentType);
});

return result.filter(function (type, index) {
  return exclude.indexOf(index) === -1;
});

}

function compileMap(/* lists… */) {

var result = {
      scalar: {},
      sequence: {},
      mapping: {},
      fallback: {}
    }, index, length;

function collectType(type) {
  result[type.kind][type.tag] = result['fallback'][type.tag] = type;
}

for (index = 0, length = arguments.length; index < length; index += 1) {
  arguments[index].forEach(collectType);
}
return result;

}

function Schema(definition) {

this.include  = definition.include  || [];
this.implicit = definition.implicit || [];
this.explicit = definition.explicit || [];

this.implicit.forEach(function (type) {
  if (type.loadKind && type.loadKind !== 'scalar') {
    throw new YAMLException('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.');
  }
});

this.compiledImplicit = compileList(this, 'implicit', []);
this.compiledExplicit = compileList(this, 'explicit', []);
this.compiledTypeMap  = compileMap(this.compiledImplicit, this.compiledExplicit);

}

Schema.DEFAULT = null;

Schema.create = function createSchema() {

var schemas, types;

switch (arguments.length) {
  case 1:
    schemas = Schema.DEFAULT;
    types = arguments[0];
    break;

  case 2:
    schemas = arguments[0];
    types = arguments[1];
    break;

  default:
    throw new YAMLException('Wrong number of arguments for Schema.create function');
}

schemas = common.toArray(schemas);
types = common.toArray(types);

if (!schemas.every(function (schema) { return schema instanceof Schema; })) {
  throw new YAMLException('Specified list of super schemas (or a single Schema object) contains a non-Schema object.');
}

if (!types.every(function (type) { return type instanceof Type; })) {
  throw new YAMLException('Specified list of YAML types (or a single Type object) contains a non-Type object.');
}

return new Schema({
  include: schemas,
  explicit: types
});

};

module.exports = Schema;