/**
 * Builder component
 * @class ApBuilder
 */

'use strict'

import React, {PropTypes as types} from 'react'
import classnames from 'classnames'
import ApBuilderEditor from './ap_builder_editor'
import ApBuilderFrame from './ap_builder_frame'
import asleep from 'asleep'
import {ApSpinner} from 'apeman-react-spinner'
import {ApTab, ApTabItem, ApTabContent, ApTabGroup} from 'apeman-react-tab'

let noop = () => undefined

/** @lends ApBuilder */
const ApBuilder = React.createClass({

  // --------------------
  // Specs
  // --------------------

  propTypes: {
    /** Html contents */
    html: types.string.isRequired,
    /** Script text */
    script: types.string,
    /** Global variables */
    globals: types.object,
    /** Compile script before executing */
    compile: types.func,
    /** Handle html edit */
    onHtmlEdit: types.func,
    /** Handle script edit */
    onScriptEdit: types.func,
    /** Handle script error */
    onScriptError: types.func,
    /** Labels */
    labels: types.object,
    /** Errors */
    errors: types.object,
    /** Codemirror mode for html */
    htmlMode: types.string,
    /** Codemirror mode for script */
    scriptMode: types.string,
    /** Custom actions for html editor */
    htmlActions: types.array,
    /** Custom actions for script editor */
    scriptActions: types.array,
    /** Check if supported */
    isSupported: types.func
  },

  mixins: [],

  statics: {},

  getInitialState () {
    const s = this
    return {
      tab: null,
      overlaying: false,
      edittingHtml: '',
      edittingScript: '',
      vr: 0,
      spinning: false,
      htmlActions: [],
      scriptActions: []
    }
  },

  getDefaultProps () {
    return {
      html: '',
      script: '',
      onHtmlEdit: noop,
      onScriptEdit: noop,
      labels: {
        htmlTab: 'HTML',
        scriptTab: 'Script'
      },
      htmlMode: 'htmlmixed',
      scriptMode: 'javascript',
      isSupported () {
        return /Chrome/.test(navigator.userAgent)
      }
    }
  },

  render () {
    const s = this
    let { state, props } = s
    let { labels, html, script, compile } = props
    let { tab, overlaying, edittingScript, edittingHtml } = state
    let overlayClassName = classnames('ap-builder-overlay', {
      'ap-builder-overlay-enabled': overlaying
    })
    return (
      <div className={ classnames('ap-builder', props.className) }
           style={ Object.assign({}, props.style) }>
        <div className='ap-builder-inner'>
          { props.children }
          <ApBuilderFrame src={ html }
                          onLoad={ s.handleHtmlLoad }
                          onScriptError={ props.onScriptError }
                          script={ script }
                          compile={ compile }
                          vr={ state.vr }
                          globals={ props.globals }
          />
        </div>
        <ApTabGroup className={ overlayClassName }>
          <ApTab className='ap-builder-tab'>
            { s._renderTabItem('HTML', labels.htmlTab) }
            { s._renderTabItem('SCRIPT', labels.scriptTab) }
          </ApTab>
          <ApTabContent selected={ tab === 'HTML' }>
            <ApBuilderEditor src={ edittingHtml }
                             mode={ props.htmlMode }
                             onChange={ s.handleEditorChange }
                             onSubmit={ s.handleEditorSubmit }
                             onCancel={ s.handleEditCancel }
                             actions={ props.htmlActions }
            />
          </ApTabContent>
          <ApTabContent selected={ tab === 'SCRIPT' }>
            <ApBuilderEditor src={ edittingScript }
                             mode={ props.scriptMode }
                             onChange={ s.handleEditorChange }
                             onSubmit={ s.handleEditorSubmit }
                             onCancel={ s.handleEditCancel }
                             actions={ props.scriptActions }
            />
          </ApTabContent>
        </ApTabGroup>

        <ApSpinner className='ap-builder-spinner'
                   size='xx-large'
                   enabled={ state.spinning }
        />
      </div>
    )
  },

  // --------------------
  // Lifecycle
  // --------------------

  componentWillMount () {
    const s = this
  },

  componentDidMount () {
    const s = this
    let { props } = s
    let isSupported = props.isSupported()
    if (!isSupported) {
      console.log('isSupported', isSupported)
    }
  },

  componentWillReceiveProps (nextProps) {
    const s = this
  },

  shouldComponentUpdate (nextProps, nextState) {
    const s = this
    return true
  },

  componentWillUpdate (nextProps, nextState) {
    const s = this
  },

  componentDidUpdate (prevProps, prevState) {
    const s = this
  },

  componentWillUnmount () {
    const s = this
  },

  // ------------------
  // Custom
  // ------------------

  handleHtmlLoad (e) {
    const s = this
  },

  handleEditorSubmit (e) {
    const s = this
    let { props, state } = s
    let { edittingScript, edittingHtml } = state
    s.setState({ spinning: true })
    return new Promise((resolve) => resolve(
      ((mode) => {
        switch (mode) {
          case 'javascript':
          case 'jsx':
            return props.onScriptEdit({ value: edittingScript })
          case 'htmlmixed':
            return props.onHtmlEdit({ value: edittingHtml })
          default:
            return null
        }
      })(e.mode)
    )).then(() => {
      s.setState({
        vr: s.state.vr + 1,
        spinning: false
      })
      s.clearOverlay()
    })
  },

  handleEditorChange (e) {
    const s = this
    let { mode, value } = e
    switch (mode) {
      case 'javascript':
      case 'jsx':
        s.setState({ edittingScript: value })
        break
      case 'htmlmixed':
        s.setState({ edittingHtml: value })
        break
      default:
        break
    }
  },

  handleEditCancel (e) {
    const s = this
    s.clearOverlay()
  },

  selectTab (tab) {
    const s = this
    let { state, props } = s
    let deselect = state.tab === tab
    if (deselect) {
      s.clearOverlay()
    } else {
      let overlaying = true
      s.setState({
        tab, overlaying,
        edittingHtml: props.html,
        edittingScript: props.script
      })
    }
  },

  clearOverlay () {
    const s = this
    s.setState({
      overlaying: false
    })
    asleep(200).then(() => {
      s.setState({ tab: null })
    })
  },

  // ------------------
  // Private
  // ------------------

  _renderTabItem (tab, title) {
    const s = this
    let { state, props } = s
    let { errors } = props
    let key = String(tab).toLowerCase()
    let className = classnames('ap-builder-tab-item', `ap-builder-tab-item-${key}`, {
      'ap-builder-tab-item-errored': errors && errors[ key ]
    })
    return (
      <ApTabItem selected={ state.tab === tab }
                 onTap={ () => s.selectTab(tab) }
                 className={ className}
      >
        { title }
      </ApTabItem>
    )
  }
})

export default ApBuilder
