var BenchmarkQueue = React.createClass({
  propTypes: {
    debug: React.PropTypes.bool,
    onChange: React.PropTypes.func.isRequired,
    initialQueue: React.PropTypes.array.isRequired,
    maxTime: React.PropTypes.number,
    onCompleteEach: React.PropTypes.func,
    onError: React.PropTypes.func
  },

  getDefaultProps: function() {
    return {
      maxTime: 5
    };
  },

  getInitialState: function() {
    return {
      queue: this.props.initialQueue.slice()
    };
  },

  setItemState: function(state) {
    state.test = this.state.queue[0].test;
    state.react = this.state.queue[0].react;
    this.props.onChange(state);
  },

  handleContextReady: function(window) {
    var benchmark = window.Benchmark(window.exports);
    benchmark.options.maxTime = this.props.maxTime; //DEBUG

    var itemState = {
      testRunnerURL: window.location.href,

      name: window.exports.name,
      platform: window.Benchmark.platform.description,
      reactVersion: window.React.version,

      isMinified: (function() {
        var code = window.React.render.toString();
        return code.indexOf(',') - code.indexOf('(') <= 2;
      }())
    };

    this.setItemState(itemState);

    var self = this;
    benchmark.on('start error cycle complete', function() {
      var stats = JSON.parse(JSON.stringify(benchmark.stats));
      itemState.stats = stats;
      itemState.isRunning = benchmark.running;
      itemState.error = benchmark.error;
      self.setItemState(itemState);
    });
    if (this.props.onError) {
      benchmark.on('error', this.props.onError);
    }
    benchmark.on('complete', function() {
      var queue = self.state.queue.slice();
      var queueItem = queue.shift();
      if (self.props.onCompleteEach) {
        self.props.onCompleteEach(queueItem);
      }
      self.setState({queue:queue});
    });
    benchmark.run({async:true});
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.queue.length < this.state.queue.length;
  },

  render: function() {
    if (!(this.state.queue && this.state.queue.length > 0)) {
      return React.DOM.div({style:{display:'none'}});
    }
    return BrowserPerfRunnerContext({
      debug: this.props.debug,
      test: this.state.queue[0].test,
      react: this.state.queue[0].react,
      onReady: this.handleContextReady
    });
  }
});

var BrowserPerfRunnerContext = React.createClass({

  propTypes: {
    debug: React.PropTypes.bool,
    test: function(object, key) {
      React.PropTypes.string.isRequired(object, key);
      if (/\.jsx?$/i.test(object[key])) {
        return;
      }
      throw Error('Expected `' + key + '` to be a test file name with extension `.js` or `.jsx`');
    },
    react: function(object, key) {
      React.PropTypes.string.isRequired(object, key);
      if (/^(?:builds\/.+|edge|previous|(?:\d+\.){2}\d+)$/.test(object[key])) {
        return;
      }
      throw Error('Expected `' + key + '` prop to be a valid react version string, build string or \'edge\' or \'previous\'');
    },
    onReady: React.PropTypes.func.isRequired
  },

  getInitialState: function() {
    return {
      testRunnerURL:'about:blank'
    };
  },

  // _handleFrameError: function(error) {
  //   console.error('BrowserPerfRunnerContext', error);
  // },
  //
  // _handleFrameLoad: function(event) {
  //   console.log('BrowserPerfRunnerContext', event);
  // },
  //
  _handleMessage: function(event) {
    if (location.href.indexOf(event.origin) !== 0) {
      return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message from ' + event.origin);
    }
    if (event.source.location.href.indexOf(this.state.testRunnerURL) === -1) {
      return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message from ' + event.source.location.href);
    }
    if (event.data !== 'Ready!') {
      return console.debug('BrowserPerfRunnerContext#_handleMessage ignored message ' + JSON.stringify(event.data));
    }
    this.props.onReady(event.source);
  },

  _getTestRunnerURL: function(props) {
    return 'runner.html' +
      '?' +
      'debug=' + (props.debug ? 1 : 0) +
      '&' +
      'react=' + encodeURIComponent(props.react) +
      '&' +
      'test=' + encodeURIComponent(props.test);
  },

  _renderState: function(props) {
    return {
      testRunnerURL: this._getTestRunnerURL(props)
    };
  },

  componentDidMount: function() {
    var node = this.refs.iframe.getDOMNode();
    // node.onload = this._handleFrameLoad;
    // node.onerror = this._handleFrameError;
    if (window.addEventListener) {
      window.addEventListener('message', this._handleMessage, false);
    } else if (window.attachEvent) {
      window.attachEvent('onmessage', this._handleMessage);
    } else {
      throw Error('cannot attach onmessage listener');
    }
    this.setState(this._renderState(this.props));
  },

  componentWillUnmount: function() {
    if (window.removeEventListener) {
      window.removeEventListener('message', this._handleMessage);
    } else if (window.detachEvent) {
      window.detachEvent('onmessage', this._handleMessage);
    } else {
      throw Error('cannot detach onmessage listener');
    }
    this.refs.iframe.getDOMNode().src = '';
  },

  componentWillReceiveProps: function(nextProps) {
    this.setState(this._renderState(nextProps));
  },

  shouldComponentUpdate: function(nextProps, nextState) {
    return nextState.testRunnerURL !== this.state.testRunnerURL;
  },

  render: function() {
    return (
      React.DOM.iframe({
        ref: 'iframe',
        name: 'BrowserPerfRunnerContextFrame',
        style: this.style,
        src: this.state.testRunnerURL
      })
    );
  },

  style: {
    position: 'absolute',
    right: '100%',
    bottom: '100%'
  }

});
