import React, {Component} from 'react';
import Dropzone from 'react-dropzone';
import ImageSize from 'browser-image-size';
import PropTypes from 'prop-types';
import Error from '../ErrorLabel';
import CropperStore from '../Cropper/index.store';
import Cropper from '../Cropper';
import Icon from '../Icon';
import Translate from '../Translate';
import classNames from 'classnames';
import './style.scss';

class ImageUpload extends Component {
    constructor(props) {
        super(props);

        this.state = {
            preview: this.props.preview,
            isUploaded: Boolean(this.props.preview),
            errors: this.props.errors,
        };

        this.cropperStore = null;
        this.dropzoneRef = null;
        this.onDrop = this.onDrop.bind(this);
        this.onEdit = this.onEdit.bind(this);
        this.onRemove = this.onRemove.bind(this);
        this.onSuccessLoaded = this.onSuccessLoaded.bind(this);
        this.setCropperData = this.setCropperData.bind(this);
        this.setUploaderData = this.setUploaderData.bind(this);

        if (this.props.isCropperAvailable) {
            const cropperData = {
                onChange: this.onSuccessLoaded,
                aspectRatio: this.props.cropperAspectRatio,
                width: this.props.width,
                height: this.props.height,
                maxWidth: this.props.maxWidth,
                maxHeight: this.props.maxHeight,
            };

            this.cropperStore = new CropperStore(cropperData);
        }
    }

    componentWillReceiveProps(nextProps) {
        this.setState({
            errors: nextProps.errors.length ? nextProps.errors : [],
        });
    }

    onEdit() {
        this.setCropperData(this.state.preview);
    }

    onDrop(acceptedFiles, rejectedFiles) {
        if (rejectedFiles.length) {
            const acceptFormats = this.props.acceptFormats.split(',');
            if (!acceptFormats.some((format) => format === rejectedFiles[0].type)) {
                this.setState({
                    errors: [`Incorrect image data. Please upload ${this.props.availableFormats}.`],
                });

                return;
            }

            if (rejectedFiles[0].size > this.props.maxFileSize) {
                this.setState({
                    errors: [`Incorrect image's size. Please upload ${this.props.maxFileSize / 1024}kb`],
                });

                return;
            }
        }

        if (this.props.isCropperAvailable) {
            this.setCropperData(acceptedFiles[0].preview, acceptedFiles[0].type);
            return;
        }

        this.setUploaderData(acceptedFiles[0]);
    }

    onSuccessLoaded(base64Result) {
        const state = {
            errors: [],
            preview: base64Result,
            isUploaded: true,
        };

        const callback = () => {
            this.props.onChange(this.state.preview);
        };

        this.setState(state, callback);
    }

    onRemove() {
        const state = {
            preview: '',
            isUploaded: false,
        };

        const callback = () => {
            this.props.onChange(this.state.preview);
        };

        this.setState(state, callback);
    }

    setCropperData(preview, originalType) {
        const availableWidth = this.props.width;
        const availableHeight = this.props.height;

        ImageSize(preview)
            .then((size) => {
                const cropperData = {
                    preview,
                    width: size.width > availableWidth ? availableWidth : size.width,
                    expectedMinWidth: availableWidth,
                    expectedMinHeight: availableHeight,
                    isVisible: true,
                    originalType,
                };

                this.cropperStore
                    .setCropperData(cropperData)
                    .showCropper();
            });

    }

    setUploaderData(file) {
        const availableWidth = this.props.width;
        const availableHeight = this.props.height;

        ImageSize(file.preview)
            .then((size) => {
                if ((size.width === availableWidth) && (size.height === availableHeight)) {
                    const reader = new FileReader();

                    reader.onloadend = () => {
                        this.onSuccessLoaded(reader.result);
                    };
                    reader.readAsDataURL(file);
                } else {
                    this.setState({
                        errors: [`Incorrect image's size. Please upload ${availableWidth}x${availableHeight}px`],
                    });
                }
            });
    }

    render() {
        const removeButton = !this.props.isDisabled ? (
            <button
                className="ImageUpload__preview-button"
                onClick={this.onRemove}
            >
                <Icon
                    name="close"
                    size="small"
                    color="white"
                />
            </button>
        ) : null;

        const cropButton = this.props.isCropperAvailable && !this.props.isDisabled ? (
            <button
                className="ImageUpload__preview-button"
                onClick={this.onEdit}
            >
                <Icon
                    name="crop"
                    size="small"
                    color="white"
                />
            </button>
        ) : null;

        const preview = this.state.preview && (
            <div className="ImageUpload__preview">
                <img
                    src={this.state.preview}
                    alt={this.props.title}
                    width={this.props.width}
                    height={this.props.height}
                />
                <div className="ImageUpload__preview-buttons">
                    {cropButton}
                    {removeButton}
                </div>
            </div>
        );

        const errors = (
            <Error errors={this.state.errors} />
        );

        const button = (
            <button
                disabled={this.props.isDisabled}
                onClick={() => this.dropzoneRef.open()}
                className="button button_secondary ImageUpload__button"
            >
                <Translate tag={this.props.buttonLabel} />
            </button>
        );

        const title = (
            <span className="ImageUpload__title"><Translate tag={this.props.title} /></span>
        );

        const description = (
            this.props.description && <span className="ImageUpload__description"><Translate tag={this.props.description} /></span>
        );

        const maxSize = !this.props.isCropperAvailable
            ? this.props.maxFileSize
            : Infinity;

        const cropper = this.props.isCropperAvailable && (
            <Cropper store={this.cropperStore} />
        );

        const uploader = (
            <div className="ImageUpload__uploader">
                {title}
                {description}
                {button}
                <Dropzone
                    disabled={this.props.isDisabled}
                    className="ImageUpload__dropzone-area"
                    ref={(node) => { this.dropzoneRef = node; }}
                    onDrop={this.onDrop}
                    accept={this.props.acceptFormats}
                    multiple={false}
                    maxSize={maxSize}
                />
            </div>
        );

        const style = {
            width: this.props.width,
            height: this.props.height,
            ...this.props.styles,
        };

        const className = classNames('ImageUpload', {
            ImageUpload_isUploaded: this.state.isUploaded,
        });

        return (
            <div
                className={className}
                id={this.props.elementId}
                style={style}
            >
                {errors}
                {preview || uploader}
                {cropper}
            </div>
        );
    }
}

ImageUpload.propTypes = {
    errors: PropTypes.array,
    acceptFormats: PropTypes.string.isRequired,
    availableFormats: PropTypes.string,
    title: PropTypes.string,
    preview: PropTypes.string,
    description: PropTypes.string,
    buttonLabel: PropTypes.string.isRequired,
    maxFileSize: PropTypes.number,
    width: PropTypes.number.isRequired,
    height: PropTypes.number.isRequired,
    maxWidth: PropTypes.number,
    maxHeight: PropTypes.number,
    styles: PropTypes.object,
    onChange: PropTypes.func.isRequired,
    isDisabled: PropTypes.bool,
    elementId: PropTypes.string,
    isCropperAvailable: PropTypes.bool,
    cropperAspectRatio: PropTypes.oneOfType([(props, propName, componentName) => {
        // if the value is string it's necessary to check the matching with strict ratio for Native format
        if (!/^\d+[:\/]\d+$/.test(props[propName])) {
            return new Error(`Invalid prop ${propName} supplied to ${componentName}. Validation failed.`);
        }
    }, PropTypes.number]),
};

ImageUpload.defaultProps = {
    errors: [],
    title: '',
    preview: '',
    description: '',
    maxFileSize: 204800,
    availableFormats: '.png .gif .jpg',
    isDisabled: false,
    maxWidth: Infinity,
    maxHeight: Infinity,
    elementId: '',
    isCropperAvailable: false,
    cropperAspectRatio: 1.5,
    styles: {},
};

export default ImageUpload;
