/**
 * Copyright 2013-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.
 *
 * 
 */

'use strict';

var _prodInvariant = require('./reactProdInvariant'),
    _assign = require('object-assign');

function _objectWithoutProperties(obj, keys) { var target = {}; for (var i in obj) { if (keys.indexOf(i) >= 0) continue; if (!Object.prototype.hasOwnProperty.call(obj, i)) continue; target[i] = obj[i]; } return target; }

function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

var ReactComponentEnvironment = require('./ReactComponentEnvironment');
var ReactDefaultBatchingStrategy = require('./ReactDefaultBatchingStrategy');
var ReactEmptyComponent = require('./ReactEmptyComponent');
var ReactMultiChild = require('./ReactMultiChild');
var ReactHostComponent = require('./ReactHostComponent');
var ReactTestMount = require('./ReactTestMount');
var ReactTestReconcileTransaction = require('./ReactTestReconcileTransaction');
var ReactUpdates = require('./ReactUpdates');
var ReactTestTextComponent = require('./ReactTestTextComponent');
var ReactTestEmptyComponent = require('./ReactTestEmptyComponent');
var invariant = require('fbjs/lib/invariant');

/**
 * Drill down (through composites and empty components) until we get a native or
 * native text component.
 *
 * This is pretty polymorphic but unavoidable with the current structure we have
 * for `_renderedChildren`.
 */
function getRenderedHostOrTextFromComponent(component) {
  var rendered;
  while (rendered = component._renderedComponent) {
    component = rendered;
  }
  return component;
}

var ReactTestComponent = function () {
  function ReactTestComponent(element) {
    _classCallCheck(this, ReactTestComponent);

    this._currentElement = element;
    this._renderedChildren = null;
    this._topLevelWrapper = null;
    this._hostContainerInfo = null;
  }

  ReactTestComponent.prototype.mountComponent = function mountComponent(transaction, nativeParent, hostContainerInfo, context) {
    var element = this._currentElement;
    this._hostContainerInfo = hostContainerInfo;
    // $FlowFixMe https://github.com/facebook/flow/issues/1805
    this.mountChildren(element.props.children, transaction, context);
  };

  ReactTestComponent.prototype.receiveComponent = function receiveComponent(nextElement, transaction, context) {
    this._currentElement = nextElement;
    // $FlowFixMe https://github.com/facebook/flow/issues/1805
    this.updateChildren(nextElement.props.children, transaction, context);
  };

  ReactTestComponent.prototype.getPublicInstance = function getPublicInstance() {
    var element = this._currentElement;
    var hostContainerInfo = this._hostContainerInfo;
    !hostContainerInfo ? process.env.NODE_ENV !== 'production' ? invariant(false, 'hostContainerInfo should be populated before getPublicInstance is called.') : _prodInvariant('145') : void 0;
    return hostContainerInfo.createNodeMock(element);
  };

  ReactTestComponent.prototype.toJSON = function toJSON() {
    // not using `children`, but I don't want to rewrite without destructuring
    // eslint-disable-next-line no-unused-vars
    var _currentElement$props = this._currentElement.props,
        children = _currentElement$props.children,
        props = _objectWithoutProperties(_currentElement$props, ['children']);

    var childrenJSON = [];
    for (var key in this._renderedChildren) {
      var inst = this._renderedChildren[key];
      inst = getRenderedHostOrTextFromComponent(inst);
      var json = inst.toJSON();
      if (json !== undefined) {
        childrenJSON.push(json);
      }
    }
    var object = {
      type: this._currentElement.type,
      props: props,
      children: childrenJSON.length ? childrenJSON : null
    };
    Object.defineProperty(object, '$$typeof', {
      value: Symbol['for']('react.test.json')
    });
    return object;
  };

  ReactTestComponent.prototype.getHostNode = function getHostNode() {};

  ReactTestComponent.prototype.unmountComponent = function unmountComponent(safely, skipLifecycle) {
    // $FlowFixMe https://github.com/facebook/flow/issues/1805
    this.unmountChildren(safely, skipLifecycle);
  };

  return ReactTestComponent;
}();

_assign(ReactTestComponent.prototype, ReactMultiChild.Mixin);

// =============================================================================

ReactUpdates.injection.injectReconcileTransaction(ReactTestReconcileTransaction);
ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);

ReactHostComponent.injection.injectGenericComponentClass(ReactTestComponent);
ReactHostComponent.injection.injectTextComponentClass(ReactTestTextComponent);
ReactEmptyComponent.injection.injectEmptyComponentFactory(function () {
  return new ReactTestEmptyComponent();
});

ReactComponentEnvironment.injection.injectEnvironment({
  processChildrenUpdates: function () {},
  replaceNodeWithMarkup: function () {}
});

var ReactTestRenderer = {
  create: ReactTestMount.render,
  /* eslint-disable camelcase */
  unstable_batchedUpdates: ReactUpdates.batchedUpdates
};

module.exports = ReactTestRenderer;