/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule NativeModules
 * @flow
 */
'use strict';

const BatchedBridge = require('BatchedBridge');
const RemoteModules = BatchedBridge.RemoteModules;

function normalizePrefix(moduleName: string): string {
  return moduleName.replace(/^(RCT|RK)/, '');
}

/**
 * Dirty hack to support old (RK) and new (RCT) native module name conventions.
 */
Object.keys(RemoteModules).forEach((moduleName) => {
  const strippedName = normalizePrefix(moduleName);
  if (RemoteModules['RCT' + strippedName] && RemoteModules['RK' + strippedName]) {
    throw new Error(
      'Module cannot be registered as both RCT and RK: ' + moduleName
    );
  }
  if (strippedName !== moduleName) {
    RemoteModules[strippedName] = RemoteModules[moduleName];
    delete RemoteModules[moduleName];
  }
});

/**
 * Define lazy getters for each module.
 * These will return the module if already loaded, or load it if not.
 */
const NativeModules = {};
Object.keys(RemoteModules).forEach((moduleName) => {
  Object.defineProperty(NativeModules, moduleName, {
    enumerable: true,
    get: () => {
      let module = RemoteModules[moduleName];
      if (module && typeof module.moduleID === 'number' && global.nativeRequireModuleConfig) {
        const json = global.nativeRequireModuleConfig(moduleName);
        const config = json && JSON.parse(json);
        module = config && BatchedBridge.processModuleConfig(config, module.moduleID);
        RemoteModules[moduleName] = module;
      }
      return module;
    },
  });
});

/**
 * Copies the ViewManager constants and commands into UIManager. This is
 * only needed for iOS, which puts the constants in the ViewManager
 * namespace instead of UIManager, unlike Android.
 *
 * We'll eventually move this logic to UIManager.js, once all
 * the call sites accessing NativeModules.UIManager directly have
 * been removed #9344445
 */
const UIManager = NativeModules.UIManager;
UIManager && Object.keys(UIManager).forEach(viewName => {
  const viewConfig = UIManager[viewName];
  if (viewConfig.Manager) {
    let constants;
    /* $FlowFixMe - nice try. Flow doesn't like getters */
    Object.defineProperty(viewConfig, 'Constants', {
      enumerable: true,
      get: () => {
        if (constants) {
          return constants;
        }
        constants = {};
        const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
        viewManager && Object.keys(viewManager).forEach(key => {
          const value = viewManager[key];
          if (typeof value !== 'function') {
            constants[key] = value;
          }
        });
        return constants;
      },
    });
    let commands;
    /* $FlowFixMe - nice try. Flow doesn't like getters */
    Object.defineProperty(viewConfig, 'Commands', {
      enumerable: true,
      get: () => {
        if (commands) {
          return commands;
        }
        commands = {};
        const viewManager = NativeModules[normalizePrefix(viewConfig.Manager)];
        viewManager && Object.keys(viewManager).forEach((key, index) => {
          const value = viewManager[key];
          if (typeof value === 'function') {
            commands[key] = index;
          }
        });
        return commands;
      },
    });
  }
});

module.exports = NativeModules;
