import React, { Component, PropTypes } from 'react';

import DatePicker from './IsoDatePicker';
import SelectModal from './SelectModal';
import ToggleButtons from './ToggleButtons';
import ToggleMultiselectButtons from './ToggleMultiselectButtons';
import moment from 'moment';

import form from 'lib/form';
import { fromJS, Iterable } from 'immutable';

import Select, { Async } from 'react-select';
import './react-select.scss';

require('./AbstractForm.scss');
require('./DatePicker.scss');

//TODO
//import { formatNumber, parseNumber } from '~/lib/jh-client-format/format.js';

//TODO
const dd = ",";
const td = " ";
const dn = 2;
const dateFormat = "DD.MM.YYYY";


function formatNumber(n, dd) {
    if (!dd || dd == '.' || !n || isNaN(n)) {
        return n;
    }    
    return ("" + n).replace('.', dd);
}


function parseNumber(s, dd) {
    if (!dd || dd == '.' || !s) {
        return s;
    }    

    s = ("" + s).replace(dd, '.');
    return s;
}


/*
 i18n: {
    dateFormat: '',
    thousandDelimiter: ' ',
    decimalDelimiter: ',',
    decimalNumbers: 2
    currencyFormat: 'left|right'
 }

 var copy = Object.assign({}, obj);
 */

//add cleave.js


const NO_FIELD = "__all__";

const DEFAULT_READ_VALUE = (e) => e.target.value;

//TODO
class AbstractForm extends Component {


    constructor(props) {
        super(props);

        this.renderField = this.renderField.bind(this);
        this.renderCheckbox = this.renderCheckbox.bind(this);
        this.renderDateField = this.renderDateField.bind(this);

        this.wrap = this.wrap.bind(this);

        this.renderErrors = this.renderErrors.bind(this);
        this.hasErrors = this.hasErrors.bind(this);

        this.handleSubmit = this.handleSubmit.bind(this);

        this.formatMsg = this.formatMsg.bind(this);//FIXME???

        this.state = {
            errors: {}
        };
    }


    handleSubmit(onSubmit) {
        return (e) => {
            e.preventDefault();
            e.stopPropagation();
            this.props.submit(onSubmit || this.props.onSubmit);
        }
    }


    renderErrors(fieldId, _errors) {
        if (!this.hasErrors(fieldId) && !(Array.isArray(_errors) && _errors.length)) {
            return null;
        }
        let output = [];
        let n;
        let errors = this.props.errors;
        let notifs = (errors && errors[fieldId]) || _errors;

        for (let i = 0; i < notifs.length; i++) {
            n = notifs[i];
            output.push(<span key={ i }> { n.msg || this.formatMsg(n.code, n.params) } </span>);
        }

        return output;
    }


    hasErrors(id) {
        let errors = this.props.errors;
        return errors && errors[id];
    }


    wrap(id, component, label, disabled, className, description, errors, required) {
        //TODO add label && notifications 
        let hasErrors = this.hasErrors(id) || (Array.isArray(errors) && errors.length);
        let cn = "form-field";
        if (hasErrors) {
            cn += " error";
        }
        if (disabled) {
            cn += " disabled";
        }
        if (required) {
            cn += " required";
        }
        if (className) {
            cn += " " + className;
        }
        return (
            <div className={ cn } key={ id }>
                { label ? <label>{ label }</label> : null }
                { component }
                { this.renderErrors(id, errors) }
                { description } {/* TODO */}
            </div>
        );
    }


    wrapCheckbox(id, component, label, disabled, className, changeHandler, value, onChange, errors, required) {
        //TODO add label && notifications 
        let hasErrors = this.hasErrors(id) || (Array.isArray(errors) && errors.length);
        let cn = "form-field-checkbox";
        if (hasErrors) {
            cn += " error";
        }
        if (disabled) {
            cn += " disabled";
        }
        if (required) {
            cn += " required";
        }
        if (className) {
            cn += " " + className;
        }
        return (
            <div className={ cn } key={ id }>
                { label ? <label htmlFor="id" onClick={ changeHandler(id, () => !value, onChange) }>{ label }</label> : null }
                { component }
                { this.renderErrors(id, errors) }
            </div>
        );
    }

    renderField(id, label, { onChange, disabled, className, type="text", errors, description, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler
        } = this.props;

        return this.wrap(id, <input key={ id } value={ valueOf(id) || '' } onChange={ onChangeHandler(id, (e) => e.target.value, onChange) } disabled={ disabled } type={ type || "text" }/>, label, disabled, className, description, errors, props.required);
    }

    //TODO
    renderMultiField(id, label, { onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler
        } = this.props;

        let val = valueOf(id);
        let value = "";

        if (Iterable.isIterable(val)) {
            val = val.toJS();
        }

        //TODO
        if (Array.isArray(val)) {
            val = val.join(", ");
        }

        let readFn = (e) => {
            let value = e.target.value || "";
            value = value.split(/[;,]/).map(s => s.trim());
            return fromJS(value);
        };

        return this.wrap(id, <input key={ id } value={ val || '' } onChange={ onChangeHandler(id, readFn, onChange) } disabled={ disabled }/>, label, disabled, className, description, errors, props.required);
    }


    //TODO format and parse numbers
    renderDecimalField(id, label, { onChange, disabled, i18n, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        return this.wrap(id, <input 
            { ...props }
            disabled={ disabled }
            value={ formatNumber(valueOf(id) || '', dd) } 
            onChange={ onChangeHandler(id, e => parseNumber(e.target.value, dd), onChange) }
            />, label, disabled, className ? `numeric ${className}` : "numeric", description, errors, props.required);
    }

    renderMoneyField(id, label, currencySymbol, currency, { onChange, disabled, i18n, className, value, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let right = !(i18n && i18n.currencySymbolLeft);

        let field = (
            <div className={ `money ${ right ? "currency-right" : "currency-left" }`}>
                { right ? null : <div className="currency">{ currencySymbol || currency }</div> }
                <div className="value">
                    <input 
                        { ...props }
                        disabled={ disabled }
                        value={ formatNumber(value || valueOf(id) || '', dd) } 
                        onChange={ onChangeHandler(id, e => parseNumber(e.target.value, dd), onChange) }
                        />
                </div>
                { right ? <div className="currency">{ currencySymbol || currency }</div> : null }
            </div>
        );

        return this.wrap(id, field, label, disabled, className ? `numeric ${className}` : "numeric", description, errors, props.required);
    }


    //TODO format and parse numbers
    renderNumericField(id, label, props) {
        return this.renderDecimalField(id, label, props);
    }


    renderCheckbox(id, label, { onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let 
            changeHandler = onChangeHandler,
            value = valueOf(id) || false;

        return this.wrapCheckbox(id, <input type="checkbox" key={ id } name={ id } checked={ value } onChange={ changeHandler(id, (e) => e.target.checked, onChange) } disabled={ disabled }/>, label, disabled, className, changeHandler, value, onChange, description, errors, props.required);
    }


    renderDateField(id, label, { onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
            dateFormat,
            i18n,
        } = this.props;
        return this.wrap(id, <DatePicker value={ valueOf(id) } onChange={ onChangeHandler(id, d => d, onChange) } dateFormat={ i18n ? i18n.dateFormat : null } disabled={ disabled }/>, label, disabled, className, description, errors, props.required);
    }


    renderSelect(id, label, items = [], { simpleValue = true, onChange, labelKey, valueKey, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,

        } = this.props;

        return this.wrap(id, 
            <Select 
                key={ id } 
                value={ valueOf(id, "") } 
                onChange={ onChangeHandler(id, (e) => e, onChange) } 
                disabled={ disabled } 
                options={ items || [] } 
                labelKey={ labelKey || "label" }
                valueKey={ valueKey || "value" }
                simpleValue={ simpleValue } />
        , label, disabled, className, description, errors, props.required);
    }


    renderAsyncSelect(id, label, fetchFn, { onChange, disabled, className, valueKey, labelKey, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        return this.wrap(id, 
            <Async key={ id } value={ valueOf(id, "") } onChange={ onChangeHandler(id, (e) => e.target.value, onChange) } disabled={ disabled } loadOptions={ fetchFn } valueKey={ valueKey || "value" } labelKey={ labelKey || "label" } />
        , label, disabled, className, description, errors, props.required);
    }


    //TODO refactor - redesign
    renderMultiselect(id, label, items = [], { onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let options = valueOf(id, []);

        return this.wrap(id, 
            <select key={ id } value={ options } onChange={ onChangeHandler(id, (e) => [...event.target.options].filter(o => o.selected).map(o => o.value), onChange) } disabled={ disabled } multiple>
                { items.map( (item, id) => <option key={ item.value || id } value={ item.value }> { item.label } </option> ) }
            </select>
        , label, disabled, className, description, errors, props.required);
    }


    renderToggleMultiselect(id, label, items = [], { labelKey, valueKey, options, onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let value = valueOf(id, []);

        return this.wrap(id, <ToggleMultiselectButtons key={ id } options={ items } value={ value } onChange={ onChangeHandler(id, (val) => val, onChange) } disabled={ disabled } valueKey={ valueKey } labelKey={ labelKey } />, label, disabled, className, description, errors, props.required);
    }


    renderTextArea(id, label, { onChange, disabled, className, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        return this.wrap(id, <textarea key={ id } value={ valueOf(id, "") } onChange={ onChangeHandler(id, (e) => e.target.value, onChange) } disabled={ disabled }/>, label, disabled, className, description, errors, props.required);
    }


    //TODO implement properly
    renderToggleButtons(id, label, items = [], { onChange, disabled, nullable, className, labelKey, valueKey, simpleValue = true, description, errors, ...props } = {}) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let buttons = [];
        let value = valueOf(id);

        return this.wrap(id, <ToggleButtons key={ id } value={ value } options={ items } labelKey={ labelKey } valueKey={ valueKey } simpleValue={ simpleValue } onChange={ onChangeHandler(id, (e) => e, onChange) } />, label, disabled, className, description, errors, props.required);
    }


    renderSelectModal(id, label, { onChange, disabled,  renderList, readInputValue, fetchData, className, description, errors, required }) {
        const {
            valueOf,
            onChangeHandler,
        } = this.props;

        let buttons = [];
        let value = valueOf(id);

        return this.wrap(id, <SelectModal 
            label={ label }
            fetchData={ fetchData }
            onChange={ onChangeHandler(id, (val) => val, onChange) }
            renderList={ renderList }
            readInputValue={ readInputValue }
            value={ value }
            disabled={ disabled }
            />, label, disabled, className, description, errors, required);
    }


    formatMsg(id, params) {
        return this.props.intl ? this.props.intl.formatMessage({ id: id, defaultMessage: id, params }) : id;
    }
}

export default AbstractForm;
