'use strict'

var React     = require('react')
var assign    = require('object-assign')
var normalize = require('react-style-normalizer')

function emptyFn(){}

function toUpperFirst(s){
    return s?
            s.charAt(0).toUpperCase() + s.substring(1):
            ''
}

var ALIGN = (function(){
    var MAP = {
        left  : 'flex-start',
        start : 'flex-start',
        center: 'center',
        right : 'flex-end',
        end   : 'flex-end'
    }

    return function(value){
        return MAP[value] || value
    }
})()

var PropTypes    = React.PropTypes
var DISPLAY_NAME = 'ReactButton'

var THEME = {
    'default': {
        //default type
        style: {
            border    : '1px solid rgb(46, 153, 235)',
            color     : 'rgb(84, 84, 84)',
        },
                overStyle: {
                    background: 'linear-gradient(to bottom, rgb(125, 191, 242) 0%, rgb(110, 184, 241) 50%, rgb(117, 188, 242) 100%)',
                    color: 'white'
                },

                activeStyle: {
                    //-6 lightness from overStyle
                    background: ' linear-gradient(to bottom, rgb(106,182,240) 0%,rgb(91,175,239) 50%,rgb(96,178,240) 100%)',
                    color: 'white'
                },

            //disabled
            disabledStyle: {
                //theme properties
                background: 'rgb(221, 221, 221)',
                border: '1px solid rgb(147, 147, 147)',
                color: 'rgb(128, 128, 128)'
            },

            //pressed
            pressedStyle: {
                background: 'linear-gradient(to bottom, rgb(22,135,222) 0%,rgb(20,129,212) 50%,rgb(20,132,218) 100%)',
                color: 'white'
            },

                overPressedStyle: {
                    // +14 lightness from pressed style
                    background: 'linear-gradient(to bottom, rgb(48,153,234) 0%,rgb(36,148,234) 50%,rgb(41,151,235) 100%)',
                },

                activePressedStyle: {
                    background: 'linear-gradient(to bottom, rgb(58,159,236) 0%,rgb(45,153,235) 50%,rgb(50,155,236) 100%)'
                },

            //focused
            focusedStyle: {}
            //---NONE ----
    },
    'primary': {
        style: {
            background: 'linear-gradient(to bottom, #4ea9ee 0%,#41a2ed 50%,#46a5ee 100%)',
            color: 'white'
        },

                overStyle: {
                    // + 10 lightness from primary
                    background: 'linear-gradient(to bottom, rgb(96,178,240) 0%,rgb(83,171,239) 50%,rgb(88,174,240) 100%)'
                },

                activeStyle: {
                    // -5 lightness from primary
                    background: 'linear-gradient(to bottom, rgb(64,162,236) 0%,rgb(50,155,236) 50%,rgb(55,158,237) 100%)'
                },

            //disabled
            disabledStyle: {
                //theme properties
                background: 'rgb(116, 144, 166)',
                color: 'rgb(190, 190, 190)'
            }

            //pressed
            //---NONE---

            //focused
            //---NONE---
    }
}

var ReactButton = React.createClass({

    displayName: DISPLAY_NAME,

    propTypes: {
        fn: PropTypes.func,
        onClick: PropTypes.func,

        primary: PropTypes.bool,
        disabled: PropTypes.bool,
        pressed: PropTypes.bool,
        defaultPressed: PropTypes.bool,

        href: PropTypes.string,
        align: PropTypes.string,

        style: PropTypes.object,

        className       : PropTypes.string,
        activeClassName : PropTypes.string,
        overClassName   : PropTypes.string,
        focusedClassName: PropTypes.string,
        disabledClassName: PropTypes.string
    },

    getDefaultProps: function() {
        return {
            isReactButton: true,
            applyDefaultTheme: true,
            buttonStates: ['focused', 'pressed'],

            'data-display-name': DISPLAY_NAME,

            align: 'center',

            defaultStyle: {
                boxSizing     : 'border-box',

                display       : 'inline-flex',
                alignItems    : 'center',
                justifyContent: 'center',

                userSelect    : 'none',
                textDecoration: 'none',
                cursor        : 'pointer',
                overflow      : 'hidden',

                //theme properties
                //fontFamily: 'Arial',
                // fontSize  : '0.9em',
                whiteSpace: 'nowrap',
                padding   : 5,
                margin    : 2
            },

            defaultDisabledStyle: {
                cursor: 'default',
            },

            defaultLabelStyle: {
                display: 'inline-block'
            },

            ellipsisLabelStyle: {
                textOverflow: 'ellipsis',
                overflow: 'hidden',
                whiteSpace: 'nowrap'
            },

            ellipsis: true,

            href: ''
        }
    },

    getInitialState: function() {
        return {
            mouseOver: false,
            active: false,
            defaultPressed: this.props.defaultPressed
        }
    },

    isFocused: function() {
        return this.state.focused
    },

    isActive: function() {
        return !!this.state.active
    },

    render: function(){
        var props = this.prepareProps(this.props, this.state)

        return (props.factory || React.DOM.a)(props)
    },

    prepareProps: function(thisProps, state) {

        var props = {}

        assign(props, thisProps)

        props.theme = this.prepareTheme(props)

        var pressed = props.pressed != null? props.pressed: state.defaultPressed

        if (pressed != null){
            props.pressed = pressed
        }

        props.active    = props.activeState == null? !!state.active: props.activeState
        props.over      = props.overState == null? !!state.mouseOver: props.overState
        props.focused   = props.focusedState == null? !!state.focused: props.focusedState

        props['data-active']  = props.active
        props['data-over']    = props.over
        props['data-focused'] = props.focused
        props['data-pressed'] = props.pressed
        props['data-disabled'] = props.disabled

        props.style     = this.prepareStyle(props, state)
        props.className = this.prepareClassName(props, state)
        props.children  = this.prepareChildren(props)

        var handleClick = this.handleClick.bind(this, props)

        props.onClick = typeof props.interceptClick == 'function'?
                            props.interceptClick.bind(this, handleClick):
                            handleClick

        props.onFocus      = this.handleFocus.bind(this, props)
        props.onBlur       = this.handleBlur.bind(this, props)
        props.onMouseEnter = this.handleMouseEnter.bind(this, props)
        props.onMouseLeave = this.handleMouseLeave.bind(this, props)
        props.onMouseDown  = this.handleMouseDown.bind(this, props)
        props.onMouseUp    = this.handleMouseUp.bind(this, props)

        return props
    },

    handleFocus: function(props, event) {
        if (props.disabled){
            return
        }

        this.setState({
            focused: true
        })

        ;(this.props.onFocus || emptyFn)(event)
    },

    handleBlur: function(props, event) {
        if (props.disabled){
            return
        }

        this.setState({
            focused: false
        })

        ;(this.props.onBlur || emptyFn)(event)
    },

    handleClick: function(props, event) {
        if (!props.href || props.disabled){
            event.preventDefault()
        }

        if (props.disabled){
            return
        }

        if (props.pressed != null){
            var newPressed = !props.pressed

            if (this.props.pressed == null){
                this.setState({
                    defaultPressed: newPressed
                })
            }

            ;(this.props.onToggle || emptyFn)(newPressed, event)
        }

        ;(this.props.onClick || emptyFn)(event)
        ;(this.props.fn || emptyFn)(props, event)
    },

    handleMouseEnter: function(props, event) {
        if (props.disabled){
            return
        }

        this.setState({
            mouseOver: true
        })

        ;(this.props.onMouseEnter || emptyFn)(event)
    },

    handleMouseLeave: function(props, event) {
        if (props.disabled){
            return
        }

        this.setState({
            mouseOver: false
        })

        ;(this.props.onMouseLeave || emptyFn)(event)
    },

    handleMouseUp: function(props, event) {
        if (props.disabled){
            return
        }

        this.setState({
            active: false
        })

        window.removeEventListener('mouseup', this.handleMouseUp)

        ;(this.props.onMouseUp || emptyFn)(event)
        ;(this.props.onDeactivate || emptyFn)(event)
    },

    handleMouseDown: function(props, event) {

        if (props.disabled){
            return
        }

        this.setState({
            active: true
        })

        window.addEventListener('mouseup', this.handleMouseUp)

        ;(this.props.onMouseDown || emptyFn)(event)
        ;(this.props.onActivate || emptyFn)(event)
    },

    prepareTheme: function(props){
        var theme  = props.theme
        var THEMES = props.themes = props.themes || this.constructor.theme || THEME

        if (typeof theme == 'string'){
            theme = THEMES[theme]?
                        THEMES[theme]:
                        null
        }

        return theme == null?
                THEMES.default:
                theme
    },

    prepareChildren: function(props) {
        var children = props.children

        if (props.label){

            var labelProps = assign({}, props.defaultLabelProps, props.labelProps)
            var defaultLabelStyle = assign({}, props.defaultLabelStyle)

            if (props.ellipsis){
                assign(defaultLabelStyle, props.ellipsisLabelStyle)
            }

            var labelStyle = assign({}, defaultLabelStyle, labelProps.style, props.labelStyle)

            labelProps.style = labelStyle

            children = <span {...labelProps}>
                {props.label}
            </span>
        }

        if (typeof this.props.renderChildren === 'function'){
            return this.props.renderChildren(children)
        }

        return children
    },

    prepareClassName: function(props) {

        var className = props.className || ''

        if (props.disabled){
            if (props.disabledClassName){
                className += ' ' + props.disabledClassName
            }
        } else {
            if (props.active && props.activeClassName){
                className += ' ' + props.activeClassName
            }

            if (props.pressed && props.pressedClassName){
                className += ' ' + props.pressedClassName
            }

            if (props.over && props.overClassName){
                className += ' ' + props.overClassName
            }

            if (props.focused && props.focusedClassName){
                className += ' ' + props.focusedClassName
            }
        }

        return className
    },

    prepareComputedStyleNames: function(props){

        if (typeof props.computeStyleNames == 'function'){
            return props.computeStyleNames(props)
        }

        var names = ['style']

        if (props.disabled){
            names.push('disabledStyle')

            return names
        }

        if (props.focused){
            names.push('focusedStyle')
        }
        if (props.pressed){
            names.push('pressedStyle')
        }

        if (typeof props.addStateStyle == 'function'){
            props.addStateStyle(names)
        }

        if (props.focused && props.pressed){
            names.push('focusedPressedStyle')
        }

        if (typeof props.addCombinedStateStyle == 'function'){
            props.addCombinedStateStyle(names)
        }

        //names is something like ['style','focusedStyle','pressedStyle', 'focusedPressedStyle']
        //
        //now we add over and active styles

        var overNames
        if (props.over){
            overNames = names.map(function(name){
                return 'over' + toUpperFirst(name)
            })
        }

        var activeNames
        if (props.active){
            activeNames = names.map(function(name){
                return 'active' + toUpperFirst(name)
            })
        }

        overNames   && names.push.apply(names, overNames)
        activeNames && names.push.apply(names, activeNames)

        return names
    },

    prepareStyle: function(props) {

        var style = assign({}, this.prepareDefaultStyle(props))

        var styleNames = this.prepareComputedStyleNames(props)
        var theme      = props.theme
        var THEMES     = props.themes

        if (theme){
            //apply default theme first
            if (props.applyDefaultTheme && THEMES.default && theme != THEMES.default){
                styleNames.forEach(function(styleName){
                    assign(style, THEMES.default[styleName])
                })
            }

            //then apply theme
            styleNames.forEach(function(styleName){
                assign(style, theme[styleName])
            })
        }

        ;(props.onThemeStyleReady || emptyFn)(style, props)

        //TODO apply default non-theme first to typed buttons
        //then non-theme
        styleNames.forEach(function(styleName){
            assign(style, props[styleName])
        })

        ;(props.onStyleReady || emptyFn)(style, props)

        return normalize(style)
    },

    prepareDefaultStyle: function(props){
        var defaultStyle = assign({}, props.defaultStyle)

        if (props.block){
            defaultStyle.display = 'flex'
        }

        defaultStyle.justifyContent = ALIGN(props.align)

        if (props.disabled){
            assign(defaultStyle, props.defaultDisabledStyle)
        }

        return defaultStyle
    }
})

ReactButton.themes = THEME

module.exports = ReactButton