React = require 'react'
createClass = require 'create-react-class'
PropTypes = require 'prop-types'
{StyleSheet, css} = require 'aphrodite/no-important'
assign = require 'lodash/assign'

dialogueMixin = require '../mixins/dialogue_mixin'

{ESCAPE, KEY_S} = require '../constants/keyboard'

ConfirmSave = React.createFactory(require('./confirm_save'))
Spinner = React.createFactory(require('./spinner'))

{div, span, button} = require 'react-dom-factories'

###&
  @props.title - OPTIONAL - [String]
  title for the modal header

  @props.buttons - OPTIONAL - [Array]
  Array of button objects with name, handler to be called on click, and disabled boolean, eg...
  ```
  [
    {
      name: 'Create'
      handler: @create
      disabled: no
    }
  ]
  ```

  @props.children - REQUIRED - [Element]
  React element (or array of elements) to inserted as the form body

  @props.styleOverride - OPTIONAL - [Object] 
  aphrodite style object, optionally can contain .form, .title, .bar, .btn, .class defs, eg...
  ```
  {
    formBase: {}
    form: {}
    title: {}
    bar: {}
    btn: {}
  }
  ```
  @props.canUpdate - OPTIONAL - [Boolean]
  Defaults to no, whether or not the user can update and save the form

  @props.save - OPTIONAL - [Function]
  Function that save the form

  @props.close - OPTIONAL - [Function]
  Function that closes the form

  @props.onClose - OPTIONAL - [Function]
  Function that is called right before the modal closes

  @props.showClose - OPTIONAL - [Boolean]
  Defaults to yes, set it to no to not show the close button

  @props.closeAfterSave - OPTIONAL - [Boolean]
  Defaults to yes, set it to no to prevent the modal from calling @props.close after a save is complete
  Note: in this case you MUST pass an onSaveComplete handler that sets the saveState to null after a save

  @props.onSaveComplete - OPTIONAL - [Function]
  Function that is called right after the saveState is set to complete and the success indicator finishes animating

  @props.closeBtnText - OPTIONAL - [String]
  Defaults to 'Cancel', text to display in the close button

  @props.loading - OPTIONAL - [Boolean]
  Defaults to no, whether or not to show the spinner instead of the children

  @props.displayProgressBar - OPTIONAL - [Boolean]
  Defaults to no, whether or not the confirm/save show the progress bar instead of the spinner

  @props.uploadProgress - OPTIONAL
  Progress of a file being uploaded

  @props.unSavedMessage - OPTIONAL
  Message to be displayed in dialog if you have unsaved changes

&###

Form = createClass
  
  displayName: 'Form'

  mixins: [dialogueMixin]

  propTypes:
    styleOverride: PropTypes.shape
      form: PropTypes.object
      bar: PropTypes.object
      title: PropTypes.object
      btn: PropTypes.object
    title: PropTypes.string
    buttons: PropTypes.array
    canUpdate: PropTypes.bool
    save: PropTypes.func.isRequired
    close: PropTypes.func.isRequired
    onClose: PropTypes.func
    onSaveComplete: PropTypes.func
    showClose: PropTypes.bool
    closeAfterSave: PropTypes.bool
    closeBtnText: PropTypes.string
    unSavedMessage: PropTypes.string
    unSavedDialogueHeight: PropTypes.number
    unSavedChanges: PropTypes.bool
    onSaveFail: PropTypes.func
    inLineStyle: PropTypes.object
    loading: PropTypes.bool
    spinnerProps: PropTypes.object
    displayProgressBar: PropTypes.bool
    uploadProgress: PropTypes.oneOfType [
      PropTypes.string
      PropTypes.number
    ]
  
  getDefaultProps: ->
    styleOverride: {}
    inLineStyle: {}
    buttons: []
    canUpdate: true
    showClose: yes
    closeAfterSave: yes
    unSavedDialogueHeight: 100
    saveState: null
    saveMessage: null
    unSavedChanges: no
    loading: no
    spinnerProps:
      length: 7
      radius: 7
      lines: 12
      width: 2
    displayProgressBar: no
    uploadProgress: ''

  componentWillMount: ->
    # This is necessary becasue translation is not available on app load
    # So this cannot live in default props
    @closeBtnText = t 'Cancel'

    document.addEventListener 'keydown', @handleKeyPress

  componentWillUnmount: ->
    document.removeEventListener 'keydown', @handleKeyPress
  
  render: ->
    {styleOverride, title, buttons, closeBtnText, showClose, children, save, saveState, saveMessage, 
    canUpdate, unSavedChanges, onSaveFail, inLineStyle, loading, spinnerProps, displayProgressBar, uploadProgress} = @props

    closeBtnText = closeBtnText or @closeBtnText

    # buttons
    btns = []

    # save button
    btns.push button {
        key: 'save'
        className: if unSavedChanges then css(styles.btn) else css(styles.btn, styles.disabled)
        onClick: save
        disabled: not unSavedChanges
        tabIndex: -1
      }, t 'Save' if canUpdate
    # close/cancel button
    btns.push button {
        key: 'cancel'
        className: css(styles.btn)
        onClick: @closeWithCheck
        tabIndex: -1
      }, closeBtnText if showClose
    # other buttons
    btns.push button {
      key: b.name
      className: css(styles.btn)
      onClick: b.handler
      disabled: b.disabled
    }, b.name for b in buttons by -1

    assign styles, styleOverride

    inLineStyle = assign {}, inLineStyle

    assign spinnerProps,
      key: 'spinner'

    div {
      className: css(styles.formBase, styles.form)
      style: inLineStyle
    }, [
      div {
        key: 'bar'
        className: css(styles.bar)
      }, [
        div {
          key: 'name'
          className: css(styles.title)
        }, title 
        btns       
      ] unless loading
      @dialogueBox()
      ConfirmSave {
        key: 'confirm'
        done: @saveComplete
        fail: -> onSaveFail?()
        saveMessage: saveMessage
        saveState: saveState
        displayProgressBar: displayProgressBar
        uploadProgress: uploadProgress
      } if saveState?
      if loading then Spinner(spinnerProps) else children
    ]

  
  closeWithCheck: ->
    {unSavedMessage, unSavedDialogueHeight, unSavedChanges} = @props

    if unSavedChanges
      @showDialogue
        message: unSavedMessage or t 'There are unsaved changes. How do you want to proceed?'
        confirmText: t 'Discard Changes'
        height: unSavedDialogueHeight
        confirmCallback: @close
    else
      do @close

  
  close: -> 
    {close, onClose} = @props

    onClose?()

    do close
  
  saveComplete: ->
    {close, onSaveComplete, closeAfterSave} = @props

    # if a onSaveComplete method has been passed then call it
    onSaveComplete?()

    do close if closeAfterSave

  handleKeyPress: (e) ->
    {keyCode, metaKey} = e

    if keyCode is ESCAPE then do @closeWithCheck
    if keyCode is KEY_S and metaKey
      do e.preventDefault
      {buttons} = @props
      if buttons[0]?.name is t 'Save' then do buttons[0].handler

styles = StyleSheet.create
  formBase:
    position: 'absolute'
    backgroundColor: 'white'
    overflow: 'hidden'
    width: '100%'
    height: '100%'
  form:
    top: '0px'
    left: '0px'
  bar: 
    position: 'absolute'
    height: '40px'
    backgroundColor: '#fafaf6' 
    borderBottom: '1px solid #e5e4dc'
    overflow: 'hidden'
    zIndex: '8'
    width: '100%'
  title:
    overflow: 'hidden'
    whiteSpace: 'nowrap'
    textOverflow: 'ellipsis'
    fontSize: '18px'
    height: '100%'
    lineHeight: '40px'
    color: '#666666'
    display: 'inline-block'
    marginTop: '0px'
    marginLeft: '20px'
    paddingRight: '20px'
  btn:
    float: 'right'
    marginRight: '15px'
    color: '#007fff'
    height: '28px'
    textAlign: 'center'
    lineHeight: '26px'
    marginTop: '6px'
    fontSize: '13px'
  disabled:
    color: '#cccccc'

  


module.exports = Form


