const React = require('react');
const ReactDOM = require('react-dom');
const WindowListenable = require('./mixins/window-listenable');
const CssEvent = require('./utils/css-event');
const KeyCode = require('./utils/key-code');
const Transitions = require('./styles/transitions');
const StylePropable = require('./mixins/style-propable');
const FlatButton = require('./flat-button');
const Overlay = require('./overlay');
const Paper = require('./paper');
const DefaultRawTheme = require('./styles/raw-themes/light-raw-theme');
const ThemeManager = require('./styles/theme-manager');

const ReactTransitionGroup = require('react-addons-transition-group');

const TransitionItem = React.createClass({
  mixins: [StylePropable],

  contextTypes: {
    muiTheme: React.PropTypes.object,
  },

  //for passing default theme context to children
  childContextTypes: {
    muiTheme: React.PropTypes.object,
  },

  getChildContext () {
    return {
      muiTheme: this.state.muiTheme,
    };
  },

  getInitialState() {
    return {
      style: {},
      muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme),
    };
  },

  //to update theme inside state whenever a new theme is passed down
  //from the parent / owner using context
  componentWillReceiveProps (nextProps, nextContext) {
    let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
    this.setState({muiTheme: newMuiTheme});
  },

  componentWillEnter(callback) {
    let spacing = this.state.muiTheme.rawTheme.spacing;

    this.setState({
      style: {
        opacity: 1,
        transform: 'translate3d(0, ' + spacing.desktopKeylineIncrement + 'px, 0)',
      },
    });

    setTimeout(callback, 450); // matches transition duration
  },

  componentWillLeave(callback) {
    this.setState({
      style: {
        opacity: 0,
        transform: 'translate3d(0, 0, 0)',
      },
    });

    setTimeout(() => {
      if (this.isMounted()) callback();
    }, 450); // matches transition duration
  },

  render() {
    let {
      style,
      ...other,
    } = this.props;

    return <div {...other} style={this.prepareStyles(this.state.style, style)}>
        {this.props.children}
      </div>;
  },
});

let Dialog = React.createClass({

  mixins: [WindowListenable, StylePropable],

  contextTypes: {
    muiTheme: React.PropTypes.object,
  },

  //for passing default theme context to children
  childContextTypes: {
    muiTheme: React.PropTypes.object,
  },

  getChildContext () {
    return {
      muiTheme: this.state.muiTheme,
    };
  },

  propTypes: {
    actions: React.PropTypes.array,
    autoDetectWindowHeight: React.PropTypes.bool,
    autoScrollBodyContent: React.PropTypes.bool,
    bodyStyle: React.PropTypes.object,
    contentClassName: React.PropTypes.string,
    contentStyle: React.PropTypes.object,
    modal: React.PropTypes.bool,
    openImmediately: React.PropTypes.bool,
    onClickAway: React.PropTypes.func,
    onDismiss: React.PropTypes.func,
    onShow: React.PropTypes.func,
    repositionOnUpdate: React.PropTypes.bool,
    title: React.PropTypes.node,
  },

  windowListeners: {
    keyup: '_handleWindowKeyUp',
    resize: '_positionDialog',
  },

  getDefaultProps() {
    return {
      autoDetectWindowHeight: false,
      autoScrollBodyContent: false,
      actions: [],
      modal: false,
      repositionOnUpdate: true,
    };
  },

  getInitialState() {
    return {
      open: this.props.openImmediately || false,
      muiTheme: this.context.muiTheme ? this.context.muiTheme : ThemeManager.getMuiTheme(DefaultRawTheme),
    };
  },

  //to update theme inside state whenever a new theme is passed down
  //from the parent / owner using context
  componentWillReceiveProps (nextProps, nextContext) {
    let newMuiTheme = nextContext.muiTheme ? nextContext.muiTheme : this.state.muiTheme;
    this.setState({muiTheme: newMuiTheme});
  },

  componentDidMount() {
    this._positionDialog();
    if (this.props.openImmediately) {
      this.refs.dialogOverlay.preventScrolling();
      this._onShow();
    }
  },

  componentDidUpdate() {
    this._positionDialog();
  },

  getStyles() {
    let spacing = this.state.muiTheme.rawTheme.spacing;

    let main = {
      position: 'fixed',
      boxSizing: 'border-box',
      WebkitTapHighlightColor: 'rgba(0,0,0,0)',
      zIndex: 10,
      top: 0,
      left: -10000,
      width: '100%',
      height: '100%',
      transition: Transitions.easeOut('0ms', 'left', '450ms'),
    };

    let content = {
      boxSizing: 'border-box',
      WebkitTapHighlightColor: 'rgba(0,0,0,0)',
      transition: Transitions.easeOut(),
      position: 'relative',
      width: '75%',
      maxWidth: spacing.desktopKeylineIncrement * 12,
      margin: '0 auto',
      zIndex: 10,
    };

    let body = {
      padding: spacing.desktopGutter,
      overflowY: this.props.autoScrollBodyContent ? 'auto' : 'hidden',
      overflowX: 'hidden',
    };

    let gutter = spacing.desktopGutter + 'px ';
    let title = {
        margin: 0,
        padding: gutter + gutter + '0 ' + gutter,
        color: this.state.muiTheme.rawTheme.palette.textColor,
        fontSize: 24,
        lineHeight: '32px',
        fontWeight: '400',
    };


    if (this.state.open) {
      main = this.mergeStyles(main, {
        left: 0,
        transition: Transitions.easeOut('0ms', 'left', '0ms'),
      });
    }

    return {
      main: this.mergeStyles(main, this.props.style),
      content: this.mergeStyles(content, this.props.contentStyle),
      paper: {
        background: this.state.muiTheme.rawTheme.palette.canvasColor,
      },
      body: this.mergeStyles(body, this.props.bodyStyle),
      title: this.mergeStyles(title, this.props.titleStyle),
    };
  },

  render() {
    let styles = this.getStyles();
    let actions = this._getActionsContainer(this.props.actions);
    let title;
    if (this.props.title) {
      // If the title is a string, wrap in an h3 tag.
      // If not, just use it as a node.
      title = Object.prototype.toString.call(this.props.title) === '[object String]' ?
        <h3 style={this.prepareStyles(styles.title)}>{this.props.title}</h3> :
        this.props.title;
    }

    return (
      <div ref="container" style={this.prepareStyles(styles.main)}>
        <ReactTransitionGroup component="div" ref="dialogWindow">
          {this.state.open &&
            <TransitionItem
              className={this.props.contentClassName}
              style={styles.content}>
              <Paper
                style={styles.paper}
                zDepth={4}>
                {title}

                <div ref="dialogContent" style={this.prepareStyles(styles.body)}>
                  {this.props.children}
                </div>

                {actions}
            </Paper>
          </TransitionItem>}
        </ReactTransitionGroup>
        <Overlay
          ref="dialogOverlay"
          show={this.state.open}
          autoLockScrolling={false}
          onTouchTap={this._handleOverlayTouchTap} />
      </div>
    );
  },

  isOpen() {
    return this.state.open;
  },

  dismiss() {
    CssEvent.onTransitionEnd(ReactDOM.findDOMNode(this), () => {
      this.refs.dialogOverlay.allowScrolling();
    });

    this.setState({ open: false });
    this._onDismiss();
  },

  show() {
    this.refs.dialogOverlay.preventScrolling();
    this.setState({ open: true }, this._onShow);
  },

  _getAction(actionJSON, key) {
    let styles = {marginRight: 8};
    let props = {
      key: key,
      secondary: true,
      onClick: actionJSON.onClick,
      onTouchTap: () => {
        if (actionJSON.onTouchTap) {
          actionJSON.onTouchTap.call(undefined);
        }
        if (!(actionJSON.onClick || actionJSON.onTouchTap)) {
          this.dismiss();
        }
      },
      label: actionJSON.text,
      style: styles,
    };
    if (actionJSON.ref) {
      props.ref = actionJSON.ref;
      props.keyboardFocused = actionJSON.ref === this.props.actionFocus;
    }
    if (actionJSON.id) {
      props.id = actionJSON.id;
    }

    return (
      <FlatButton
        {...props} />
    );
  },

  _getActionsContainer(actions) {
    let actionContainer;
    let actionObjects = [];
    let actionStyle = {
      boxSizing: 'border-box',
      WebkitTapHighlightColor: 'rgba(0,0,0,0)',
      padding: 8,
      marginBottom: 8,
      width: '100%',
      textAlign: 'right',
    };

    if (actions.length) {
      for (let i = 0; i < actions.length; i++) {
        let currentAction = actions[i];

        //if the current action isn't a react object, create one
        if (!React.isValidElement(currentAction)) {
          currentAction = this._getAction(currentAction, i);
        }

        actionObjects.push(currentAction);
      }

      actionContainer = (
        <div style={this.prepareStyles(actionStyle)}>
          {actionObjects}
        </div>
      );
    }

    return actionContainer;
  },

  _positionDialog() {
    if (this.state.open) {
      let clientHeight = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
      let container = ReactDOM.findDOMNode(this);
      let dialogWindow = ReactDOM.findDOMNode(this.refs.dialogWindow);
      let dialogContent = ReactDOM.findDOMNode(this.refs.dialogContent);
      let minPaddingTop = 16;

      //Reset the height in case the window was resized.
      dialogWindow.style.height = '';
      dialogContent.style.height = '';

      let dialogWindowHeight = dialogWindow.offsetHeight;
      let paddingTop = ((clientHeight - dialogWindowHeight) / 2) - 64;
      if (paddingTop < minPaddingTop) paddingTop = minPaddingTop;

      //Vertically center the dialog window, but make sure it doesn't
      //transition to that position.
      if (this.props.repositionOnUpdate || !container.style.paddingTop) {
        container.style.paddingTop = paddingTop + 'px';
      }

      // Force a height if the dialog is taller than clientHeight
      if (this.props.autoDetectWindowHeight || this.props.autoScrollBodyContent) {
        let styles = this.getStyles();
        let maxDialogContentHeight = clientHeight - 2 * (styles.body.padding + 64);

        if (this.props.title) maxDialogContentHeight -= dialogContent.previousSibling.offsetHeight;
        if (this.props.actions.length) maxDialogContentHeight -= dialogContent.nextSibling.offsetHeight;

        dialogContent.style.maxHeight = maxDialogContentHeight + 'px';
      }
    }
  },

  _onShow() {
    if (this.props.onShow) this.props.onShow();
  },

  _onDismiss() {
    if (this.props.onDismiss) this.props.onDismiss();
  },

  _handleOverlayTouchTap(e) {
    if (this.props.modal) {
      e.stopPropagation();
    }
    else {
      this.dismiss();
      if (this.props.onClickAway) this.props.onClickAway();
    }
  },

  _handleWindowKeyUp(e) {
    if (e.keyCode === KeyCode.ESC && !this.props.modal) {
      this.dismiss();
    }
  },

});

module.exports = Dialog;
