'use strict';

import React from 'react';
import PropTypes from 'prop-types';
import classnames from 'classnames';
import _ from 'lodash';
import {FormErrorAutoFocusContainer} from './FormErrorAutoFocusContainer';

export class Form extends React.Component {
    static childContextTypes = {
        form: PropTypes.object
    };

    static contextTypes = {
        form: PropTypes.object
    };

    static propTypes = {
        onChange: PropTypes.func,
        name: PropTypes.string,
        inputConverter: PropTypes.func,
        outputConverter: PropTypes.func,
        noContainer: PropTypes.bool,
        testAutomationId: PropTypes.string,
        children: PropTypes.node,
        className: PropTypes.string
    };

    static defaultProps = {
        onChange: null,
        name: '',
        inputConverter: null,
        outputConverter: null,
        children: null,
        className: null,
        testAutomationId: 'form',
        noContainer: false
    };

    constructor(props, context) {
        super(props, context);
        this.setValue = this.setOutputValue.bind(this);
    }

    getChildContext() {
        return {
            form: {
                readOnly: this.extractFlatProperty('readOnly'),
                value: this.getInputValue(),
                errors: this.extractProperty('errors'),
                setValue: this.setValue
            }
        };
    }

    getInputValue() {
        return this.inputConverter(this.extractProperty('value'));
    }

    getNeededAutoFocus = () => {
        return this.formErrorAutoFocusContainer.getNeededAutoFocus();
    };

    setOutputValue(value, diff = value) {
        value = this.outputConverter(_.extend({}, this.getInputValue(), value), diff);

        if (this.props.onChange) {
            this.props.onChange(value, diff);
        } else if (this.props.name && this.context.form) {
            this.context.form.setValue({[this.props.name]: value}, {[this.props.name]: diff});
        }
    }

    setNeededAutoFocus = value => {
        this.formErrorAutoFocusContainer.setNeededAutoFocus(value);
    };

    inputConverter(value) {
        return this.props.inputConverter ? this.props.inputConverter.call(this, value) : value;
    }

    outputConverter(value, diff) {
        return this.props.outputConverter
            ? this.props.outputConverter.call(this, value, diff)
            : value;
    }

    extractProperty(propName, props = this.props) {
        let value = props[propName];
        let form = this.context.form;

        if (form && value === undefined) {
            value = form[propName][props.name];
        }

        return value || {};
    }

    extractFlatProperty(propName) {
        let value = this.props[propName];
        let form = this.context.form;

        if (form && value === undefined) {
            value = form[propName];
        }

        return value;
    }

    render() {
        return (
            <FormErrorAutoFocusContainer ref={el => (this.formErrorAutoFocusContainer = el)}>
                {this.props.noContainer ? (
                    this.props.children
                ) : (
                    <div
                        data-test-automation-id={this.props.testAutomationId}
                        className={classnames('rc-form', this.props.className)}>
                        {this.props.children}
                    </div>
                )}
            </FormErrorAutoFocusContainer>
        );
    }
}
