###&

@general
  This component is intended to be used inside of a modal or popover, but could be used other places to confirm a save event was successful.
  Tips for using this..
  - It is absolutely positioned w/ all zeroes, to add to the top level of the render tree
  - Create a local 'saveState' state on your component, and default it to null
  - render like this:

  ```coffee
  ConfirmSave {
    key: 'confirm'
    done: @close
    saveState: @state.saveState
  } if @state.saveState?
  ```

  This way, when you trigger the save, also call @setState({saveState: 'pending'})
  Then pass a call back to the save action, which just calls @setState({saveState: 'complete'})

@props.saveState - REQUIRED - [String] 
  This can be either 'pending' or 'complete' or 'failed'
    - When 'pending', a spinner shows
    - When 'complete', a green check mark flashes before calling the done callback
    - When 'warning', a yellow exclamation mark flashes before calling the done callback
    - When 'failed', a red x flashes before calling the fail callback

@props.saveMessage - REQUIRED - [String]
  A message that will be displayed alongside the saveState imagery

@props.longSaveMessage - OPTIONAL - [Boolean] - default false
  Will improve formatting for long error messages

@props.dismissBtnText - OPTIONAL - [String]
  Text that will be displayed as the dismiss button

@props.done - REQUIRED - [Function]
  This method will be called animationDuration ms after the saveState hits complete
  It will usually be a method that sets the parent's saveState back to null, which will remove this widget from the DOM, and will usually be a method that also performs some other post save action

@props.fail - OPTIONAL - [Function]
  This method will be called animationDuration ms after the saveState hits failed
  It is REQUIRED if there is any change saveState will ever hit 'failed', otherwise the component will never hide after a failed save
  It will usually be a method that sets the parent's saveState back to null, which will remove this widget from the DOM

@props.scaleCheckmark - OPTIONAL - [Number] - default 1
  percent to scale the confirm check and spinner

@props.vTranslateCheckmark - OPTIONAL - default 0
  number of pixels to move the checkmark up above the middle of the container

@props.animationDuration - OPTIONAL - default 800
  ms over which the check scalling takes place

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

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

&###

React = require 'react'
createClass = require 'create-react-class'
Animation = require 'ainojs-animation'
easing = require 'ainojs-easing'
Spinner = React.createFactory(require './spinner')
ProgressBar = React.createFactory(require './progress_bar')

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

ConfirmSave = createClass
  
  displayName: 'ConfirmSave'

  getDefaultProps: ->
    dismissBtnText: 'Dismiss'
    scaleCheckmark: 1
    animationDuration: 800
    vTranslateCheckmark: 0
    saveMessage: ''
    displayProgressBar: no
    uploadProgress: ''
    longSaveMessage: no

  
  getInitialState: ->
    scale: .25

  render: ->
    {dismissBtnText, saveState, scaleCheckmark, vTranslateCheckmark, saveMessage, displayProgressBar, uploadProgress, longSaveMessage} = @props
    {scale} = @state

    confirmSaveClass = 'confirm-save-wrap'
    confirmSaveClass += ' has-message' if saveMessage
    

    if saveState is 'pending' and displayProgressBar and uploadProgress isnt ''
      confirmSaveClass += ' progress-background'

      content = ProgressBar {
          key: 'progress'
          progress: uploadProgress
          className: "modal-bar"
          labelPosition: "top"

      } 
    else if saveState is 'pending'
      content = Spinner {
        lines: 12
        length: 10
        width: 3
        radius: 8
        color: 'white'
      } 
    # Use a separate component for messages
    else if saveMessage
      checkClassName = 'confirm-check-message'
      checkClassName += ' failed' if saveState is 'failed'
      checkClassName += ' warning' if saveState is 'warning'
      checkClassName += ' long' if longSaveMessage

      # Don't do a scale animation when there's a message
      scaleCheckmark = 1

      content = [
        div {
          key: 'icon'
          className: checkClassName
        }, 
          span {
            className: 'confirm-message'
          }, saveMessage
        div {
          key: 'dismiss'
          className: 'message-dismiss'
        },
          button {
            onClick: @end
          }, dismissBtnText
      ]
    else 
      checkClassName = 'confirm-check'
      checkClassName += ' failed' if saveState is 'failed'
      checkClassName += ' warning' if saveState is 'warning'

      content = div {
        className: checkClassName
        style:
          transform: "scale(#{scale})"
          msTransform: "scale(#{scale})"
          WebkitTransform: "scale(#{scale})"
      }

    if saveState is 'pending' and displayProgressBar and uploadProgress isnt ''
      displayContent = div {
        className: 'progress-bar-frame'
      }, content
    else
      displayContent = div {
        className: 'confirm-frame'
        style:
          transform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
          msTransform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
          WebkitTransform: "scale(#{scaleCheckmark}) translateY(#{vTranslateCheckmark}px)"
      }, content
    

    div {
      className: confirmSaveClass
    }, displayContent

  componentWillMount: ->
    @endHasBeenCalled = no
    # Handles the case when the saveState is never pending
    if @props.saveState is 'complete' or @props.saveState is 'failed'
      @animateImmediately = yes

  componentDidMount: ->
    @animateCheck() if @animateImmediately

  componentDidUpdate: (prevProps) ->
    return unless prevProps.saveState is 'pending' and (@props.saveState is 'complete' or @props.saveState is 'warning' or @props.saveState is 'failed')
    @animateCheck()

  componentWillUnmount: ->
    if @animation?.isAnimating() then @animation.end()

  animateCheck: ->
    if @animation?.isAnimating() then @animation.end()
    
    {scale} = @state 
    {animationDuration, saveMessage} = @props

    @animation = new Animation 
      duration: animationDuration
      easing: easing('easeOutElastic')
    .init {scale}
    .on 'frame', @onFrame
    .on 'complete', if saveMessage then -> else @end
    .animateTo {scale: 1}

  onFrame: (e) ->
    @setState e.values

  end: ->
    {saveState, done, fail} = @props

    if not @endHasBeenCalled
      @endHasBeenCalled = yes
      if saveState is 'complete' then @props.done?()
      else @props.fail?()
        
module.exports = ConfirmSave