var ref = require('./env/default');
var DEFAULT_MODULES = ref.DEFAULT_MODULES;
var DEFAULT_COMPONENTS = ref.DEFAULT_COMPONENTS;
var DEFAULT_ENV = ref.DEFAULT_ENV;
var defaultModules = require('./modules/index')
var ref$1 = require('./util');
var clonePlainObject = ref$1.clonePlainObject;

var LOG_LEVELS = [
  '__DEBUG',
  '__LOG',
  '__INFO',
  '__WARN',
  '__ERROR'
]

var Runtime = function Runtime (jsFramework, options) {
  var this$1 = this;

  options = options || {}

  if (!jsFramework) {
    console.error("[runtime] no JS framework given, init failed.")
    return
  }

  // Init instance management.
  this.instanceMap = {}
  this._nextInstanceId = 1

  // Init JS Framework
  this.target = jsFramework
  this.target.WXEnvironment = clonePlainObject(options.env || DEFAULT_ENV)

  // Bind global methods for JS framework.
  this.loggers = []
  this.target.nativeLog = function () {
    var args = [], len = arguments.length;
    while ( len-- ) args[ len ] = arguments[ len ];

    var level = args[args.length - 1]
    var levelIndex = LOG_LEVELS.indexOf(level)
    if (levelIndex === -1) {
      levelIndex = 1
      level = LOG_LEVELS[1]
    }
    else {
      args.pop()
    }
    this$1.loggers.forEach(function (logger) {
      if (!logger.level) {
          logger.handler.apply(null, [level.substr(2).toLowerCase()].concat(args))
      }
      else if (LOG_LEVELS.indexOf(logger.level) === levelIndex) {
        logger.handler.apply(null, args)
      }
    })
  }
  this.target.callNative = function (id, tasks) {
    if (this$1.instanceMap[id]) {
      this$1.instanceMap[id].callNative(id, tasks)
    }
  }

  // Register modules and components.
  this.modules = {}
  this.registerModules(options.modules || DEFAULT_MODULES)
  this.registerComponents(options.components || DEFAULT_COMPONENTS)
};
Runtime.prototype.onlog = function onlog (type, handler) {
  if (!this.target) {
    console.error("[runtime] no JS framework given, this runtime is not working.")
    return
  }
  if (typeof type === 'function') {
    handler = type
    type = ''
  }
  var level = '__' + type.toUpperCase()
  if (LOG_LEVELS.indexOf(level) < 0) {
    level = ''
  }
  this.loggers.push({ level: level, handler: handler })
};
Runtime.prototype.offlog = function offlog (handler) {
    var this$1 = this;

  if (!this.target) {
    console.error("[runtime] no JS framework given, this runtime is not working.")
    return
  }
  this.loggers.some(function (logger, index) {
    if (logger.handler === handler) {
      this$1.loggers.splice(index, 1)
      return true
    }
  })
};
Runtime.prototype.registerModules = function registerModules (modules) {
    var this$1 = this;

  if (!this.target) {
    console.error("[runtime] no JS framework given, this runtime is not working.")
    return
  }
  var target = this.target
  modules.forEach(function (module) {
    var loop = function ( name ) {
      var registration = []
      var functions = {}
      var methods = module[name]
      if (Array.isArray(methods)) {
        // Handle default modules
        registration = methods
        methods.forEach(function (methodName) {
          functions[methodName] = ((defaultModules[name] || {})[methodName] || function () {})
        })
      }
      else {
        // Handle custom modules
        registration = Object.keys(methods)
        functions = methods
      }
      this$1.modules[name] = functions
      var obj;
        target.registerModules(( obj = {}, obj[name] = registration, obj ))
    };

      for (var name in module) loop( name );
  })
};
Runtime.prototype.registerComponents = function registerComponents (components) {
  if (!this.target) {
    console.error("[runtime] no JS framework given, this runtime is not working.")
    return
  }
  var target = this.target
  components.forEach(function (component) { return target.registerComponents([component]); })
};
Runtime.prototype._genInstanceId = function _genInstanceId () {
  if (!this.target) {
    console.error("[runtime] no JS framework given, this runtime is not working.")
    return
  }
  return this._nextInstanceId++
};

exports.Runtime = Runtime
