React = require 'react'
PropTypes = require 'prop-types'
createClass = require 'create-react-class'
assign = require 'lodash/assign'
cloneDeep = require 'lodash/cloneDeep'
isEqual = require 'lodash/isEqual'

animationMixin = require '../mixins/animation_mixin'

Pvr = React.createFactory(require './pvr')
SelectPvrOption = React.createFactory(require './select_pvr_option')

{div} = require 'react-dom-factories'

###&
@general
Select popover menu with sub-option capability

@props.options - [Array] - Required
  array of objects containing at minimum a label and value attribute
  optionally a subLabel property can be passed

@props.defaultSelected - [Object|String] - Optional
  value of the option selected by default

@props.close - [Function] - Required
  func that closes the popover

@props.styleMixin - [Object] - Optional
  object containing any style properties to mixin with and/or overrride the defaults
  note that width height are passed separately so they can have defaults and auto settings
  passing widt/height in this object could cause issues

@props.onChange - [Function] - Required
  method to call when the non selected option is clicked

@props.hideSelected - [Boolean] - Optional
  when on, the defaultSelected option will be removed from the list

@props.headerTitle - [String] - Optional
  optional title String for popover header

@props.headerClass - [String] - Optional
  optional class for popover header

@props.maxHeight - [Number] - Optional
  the maximum height the popover should be. used to set height on the pvr if this is 
  lower than the computed height.

@props.pvrProps - [Object] - Optional
  properties germane to PVR wrapper: width, height, anchor, hAdjust, vAdjust, direction
&###

SelectPvr = createClass
  
  displayName: 'SelectPvr'

  mixins: [animationMixin]

  enterStateStart:
    scale: .9
  enterStateEnd:
    scale: 1
  enterEasing: 'easeOutElastic'
  enterDuration: 600

  propTypes:
    options: PropTypes.array.isRequired
    styleMixin: PropTypes.object
    headerClass: PropTypes.string
    close: PropTypes.func.isRequired   
    optionHeight: PropTypes.number
    onChange: PropTypes.func.isRequired
    maxHeight: PropTypes.number
    pvrProps: PropTypes.object
    canDeselect: PropTypes.bool
    noWrapOptions: PropTypes.bool
    defaultSelected: PropTypes.oneOfType [
      PropTypes.string
      PropTypes.object
    ]

  getInitialState: ->
    {defaultSelected, options, optionHeight} = @props
    selectedOption: defaultSelected or null
    aggregateOptionsHeight: options.length * optionHeight + options.length * 1
    openSubOptions: null

  getDefaultProps: ->
    {
      options: []
      styleMixin: {}
      headerTitle: null
      headerClass: ''
      hideSelected: no
      optionHeight: 36
      noWrapOptions: no
      pvrProps: {}
      canDeselect: no
    }

  render: ->
    {styleMixin, options, hideSelected, optionHeight, headerTitle, headerClass, noWrapOptions, disabled, className, maxHeight, close, canDeselect} = @props
    {selectedOption, scale, aggregateOptionsHeight, openSubOptions} = @state
    style = {}

    pvrProps = cloneDeep @props.pvrProps

    @hasHeader = headerTitle?

    unless pvrProps.height?
      pvrProps.height = aggregateOptionsHeight - (if hideSelected then optionHeight else 0) 
      
    if @hasHeader
      pvrProps.height += 34    

    if maxHeight? and pvrProps.height > maxHeight
      pvrProps.height = maxHeight

    assign style, styleMixin

    if pvrProps.styleMixin?.maxHeight?
      style.maxHeight = pvrProps.styleMixin.maxHeight 
    style.height = pvrProps.height

    if pvrProps.width
      style.width = pvrProps.width

    optionEls = []
    for option in options
      {children, label, id, value, subLabel} = option
      optionsEqual = @compareOptions(option)

      continue if hideSelected and optionsEqual

      childItems = []
      subOptionsHeight = 0

      if children?
        for chld in children when chld?
          opth = chld.optionHeight or optionHeight
          subOptionsHeight += opth
          childItems.push SelectPvrOption {
            key: chld.id or chld.value
            hasSubLabel: chld.subLabel?
            option: chld
            optionHeight: opth
            isSelected: @compareOptions(chld)
            canDeselect: chld.canDeselect
            handleChange: chld.handleChange or @handleChange
            noWrapOptions: chld.noWrapOptions
            customClass: chld.customClass
          }
      
      optionEls.push SelectPvrOption {
        hasSubLabel: subLabel?
        option: option
        optionHeight: optionHeight
        key: id or value
        isSelected: optionsEqual
        canDeselect: canDeselect
        handleChange: @handleChange
        noWrapOptions: noWrapOptions
        subOptionsHeight: subOptionsHeight
        setOpenSubOptions: @setOpenSubOptions
        isOpen: childItems.length and openSubOptions is option
      }, childItems

    pvrProps.scale = scale
    pvrProps.close = close

    pvrProps.element = div {
      key: 'select-pvr'
      className: 'select-pvr'
      style: style
    }, [
        if @hasHeader
          div {
            key: 'header'
            className: "header plain-pvr-content-item #{headerClass}"
          }, headerTitle
        
        div {
          key: 'inner-rows'
        }, optionEls
      ]

    Pvr(pvrProps)

  handleChange: (option) ->
    @setState
      selectedOption: option

    @props.onChange option
    @props.close()

  setOpenSubOptions: (option, adjust) ->
    {options, optionHeight} = @props
    agg = options.length * optionHeight + (options.length * 1) + adjust
    @setState
      openSubOptions: option
      aggregateOptionsHeight: agg

  compareOptions: (option) ->
    {selectedOption} = @state
    {label} = option

    if typeof selectedOption is 'string' then return selectedOption is label
    else if typeof selectedOption is 'object' then return isEqual(selectedOption, option)

    
module.exports = SelectPvr