React = require 'react'
ProgressBar = React.createFactory(require './progress_bar')
AlertModal = React.createFactory(require './alert_modal')
utils = require '../utils'

{IMAGE_TYPES} = require '../constants/file_types'

{div, input, button, ul, li} = React.DOM


FileInput = React.createClass

  displayName: 'FileInput'

  propTypes:
    openAlertModal: React.PropTypes.func.isRequired
    wrapperClass: React.PropTypes.string
    className: React.PropTypes.string
    multiple: React.PropTypes.bool
    chooseFileText: React.PropTypes.string
    removeFileText: React.PropTypes.string
    name: React.PropTypes.string.isRequired
    maxSize: React.PropTypes.number
    resolution: React.PropTypes.object
    disabled: React.PropTypes.bool

  getDefaultProps: ->
    wrapperClass: 'file-input-wrapper'
    className: 'file-input'
    multiple: false
    chooseFileText: 'Choose file...'
    removeFileText: 'Remove file'
    maxSize: 1048576 # Default max size of 1MB
    disabled: no

    ## Keep this commented out. This is how to form the resolution prop:
    # resolution:
    #   max:
    #     h: null
    #     w: null
    #   min:
    #     h: null
    #     w: null

  getInitialState: ->
    fileInputKey: 0
    valid: true
    inputHasFile: false

  render: ->
    {name, className, wrapperClass, multiple, uploadProgress, chooseFileText, removeFileText, showFileRemove, disabled} = @props
    {fileInputKey, inputHasFile} = @state
    multiple = if multiple then 'multiple' else ''
    uploadProgress = uploadProgress[name]

    wrapperClass += ' is-uploading' if uploadProgress?

    div {
      className: wrapperClass
    }, [
      input {
        key: fileInputKey
        className: className
        type: 'file'
        ref: 'file'
        name: name
        multiple: multiple
        onChange: @handleChange
        style:
          display: if inputHasFile then 'none' else 'block'
        disabled: disabled
      }
      div {
        key: 'overlay'
        className: "file-overlay #{if uploadProgress? then 'is-uploading' else ''}"
      }, [
        div {
          key: 'tools'
          className: 'overlay-tools'
        }, [
          button {
            key: 'add'
            className: 'add-file'
            onClick: @handleFileClick
          }, t(chooseFileText)
          button {
            key: 'remove'
            className: 'remove-file'
            onClick: @handleFileRemove
          }, t(removeFileText) if inputHasFile or showFileRemove
        ]
        ProgressBar {
          key: 'progress'
          progress: uploadProgress
        } if uploadProgress?
      ] if not disabled
    ]

  clear: ->
    @setState
      fileInputKey: @state.fileInputKey + 1
      inputHasFile: false

  validate: (file, img) ->
    {fileTypes, maxSize, resolution} = @props
    {files} = @refs.file
    status =
      errors: []
    URL = window.URL or window.webkitURL
    extension = file.name.split('.').pop()

    # Check file size
    if maxSize? and file.size > maxSize then status.errors.push t "Size must be less than __maxSize__", maxSize: utils.bytesToSize(maxSize)

    # Check file extensions
    if fileTypes?
      # Use an array of qualified image types
      if typeof fileTypes is 'object'
        types = fileTypes.join(', ')

      # If set to imagesOnly, only image formats will be accepted
      else if typeof fileTypes is 'string' and fileTypes is 'imagesOnly'
        types = IMAGE_TYPES.join(', ')

      # Check the file type to see if it's allowed
      if types.length and types.search(extension) is -1 then status.errors.push t "File type must be __fileType__", fileType: types.toUpperCase()

    # For images, enforce resolution restrictions
    if resolution? and img? and URL?
      {min, max} = resolution
      resErrors = []

      if max?
        if max.h and img.height > max.h then resErrors.push t "Less than __value__ in __measure__", value: "#{max.h}px", measure: 'height'
        if max.w and img.width > max.w then resErrors.push t "Less than __value__ in __measure__", value: "#{max.w}px", measure: 'width'
      if min?
        if min.h and img.height < min.h then resErrors.push t "Greater than __value__ in __measure__", value: "#{min.h}px", measure: 'height'
        if min.w and img.width < min.w then resErrors.push t "Greater than __value__ in __measure__", value: "#{min.w}px", measure: 'width'

      if resErrors.length
        status.errors.push(t 'Resolution requirements')
        status.errors.push(resErrors)

    return status

  showErrorMessage: (errors) ->
    @stopFilesLoop = true
    messages = []

    # Parse the validation errors
    for error, index in errors
      if typeof error is 'object' and error.length
        for sErr in error
          messages.push li {
            key: index
            className: 'sub-item'
          }, sErr
      else
        messages.push li {
          key: index
          className: 'item'
        }, error

    if messages.length
      msg = ul {
        key: 'list'
        className: 'error-message-list'
      }, messages
    else 
      msg = t "Selected files must match requirements"

    @props.openAlertModal msg

    @clear()

  handleFileRemove: ->
    # Report back the name of the element
    @props.onFileRemove?(@getValue(), @clear)

  handleFileClick: (e) ->
    e.stopPropagation()
    utils.synthesizeMouseEvent(@refs.file, 'click')

  handleChange: (e) ->
    {uploadProgress, onChange, resolution, name} = @props
    {files} = @refs.file
    @stopFilesLoop = false

    # Check for the URL class so we can use createObjectURL to assign data to images created dynamically
    # This is used to enforce resolution rules, and is not supported by IE9
    URL = window.URL or window.webkitURL

    # If an upload is in progress, do nothing
    if uploadProgress[name]? then return e.preventDefault()

    for file, index in files
      extension = file.name.split('.').pop()
      isImage = extension in IMAGE_TYPES
      isLastFile = index is files.length - 1

      # If an invalid file is detected, stop scanning
      if @stopFilesLoop
        @stopFilesLoop = false
        return

      # If an image and resolution constraints defined, wait for img onload
      if resolution? and isImage and URL?
        img = new Image()
        img.onload = =>
          v = @validate(file, img)
          if v.errors.length then @showErrorMessage(v.errors)
          else if isLastFile and v
            onChange?(@getValue())
            @setState
              inputHasFile: true
        img.src = URL.createObjectURL(file)

      # For all other files, run validation normally
      else 
        v = @validate(file)
        if v.errors.length then @showErrorMessage(v.errors)
        else if isLastFile and v
          onChange?(@getValue())
          @setState
            inputHasFile: true

  getValue: ->
    value =
      files: @refs.file.files
      name: @refs.file.name
      maxSize: @props.maxSize
      ref: @

    return value

module.exports = FileInput