'use strict';
import React from 'react';
import createReactClass from 'create-react-class';
import PropTypes from 'prop-types';
import L from 'leaflet';
import Cartesian2 from 'terriajs-cesium/Source/Core/Cartesian2';
import defined from 'terriajs-cesium/Source/Core/defined';
import EllipsoidGeodesic from 'terriajs-cesium/Source/Core/EllipsoidGeodesic';
import getTimestamp from 'terriajs-cesium/Source/Core/getTimestamp';
import ObserveModelMixin from '../../ObserveModelMixin';
import Styles from './legend.scss';

const geodesic = new EllipsoidGeodesic();

const distances = [
    1, 2, 3, 5,
    10, 20, 30, 50,
    100, 200, 300, 500,
    1000, 2000, 3000, 5000,
    10000, 20000, 30000, 50000,
    100000, 200000, 300000, 500000,
    1000000, 2000000, 3000000, 5000000,
    10000000, 20000000, 30000000, 50000000];

const DistanceLegend = createReactClass({
    displayName: 'DistanceLegend',
    mixins: [ObserveModelMixin],

    propTypes: {
        terria: PropTypes.object,
    },

    getInitialState() {
        return {
            distanceLabel: undefined,
            barWidth: 0
        };
    },

    /* eslint-disable-next-line camelcase */
    UNSAFE_componentWillMount() {
        this.viewerSubscriptions = [];
        this.removeUpdateSubscription = undefined;

        this._lastLegendUpdate = undefined;

        this.viewerSubscriptions.push(this.props.terria.beforeViewerChanged.addEventListener(()=>{
            if (defined(this.removeUpdateSubscription)) {
                this.removeUpdateSubscription();
                this.removeUpdateSubscription = undefined;
            }
        }));

        this.addUpdateSubscription();

        this.viewerSubscriptions.push(this.props.terria.afterViewerChanged.addEventListener(()=>{
            this.addUpdateSubscription();
        }));
    },

    componentWillUnmount() {
        this.removeUpdateSubscription && this.removeUpdateSubscription();
        this.viewerSubscriptions.forEach(remove => remove());
    },

    addUpdateSubscription() {
        const that = this;
        if (defined(this.props.terria.cesium)) {
            const scene = this.props.terria.cesium.scene;
            this.removeUpdateSubscription = scene.postRender.addEventListener(()=>{
                this.updateDistanceLegendCesium(scene);
            });
        } else if (defined(this.props.terria.leaflet)) {
            const map = this.props.terria.leaflet.map;

            const potentialChangeCallback = function potentialChangeCallback() {
                that.updateDistanceLegendLeaflet(map);
            };

            that.removeUpdateSubscription = function() {
                map.off('zoomend', potentialChangeCallback);
                map.off('moveend', potentialChangeCallback);
            };

            map.on('zoomend', potentialChangeCallback);
            map.on('moveend', potentialChangeCallback);

            that.updateDistanceLegendLeaflet(map);
        }
    },

    updateDistanceLegendCesium(scene) {
        const now = getTimestamp();
        if (now < this._lastLegendUpdate + 250) {
            return;
        }

        this._lastLegendUpdate = now;

        // Find the distance between two pixels at the bottom center of the screen.
        const width = scene.canvas.clientWidth;
        const height = scene.canvas.clientHeight;

        const left = scene.camera.getPickRay(new Cartesian2((width / 2) | 0, height - 1));
        const right = scene.camera.getPickRay(new Cartesian2(1 + (width / 2) | 0, height - 1));

        const globe = scene.globe;
        const leftPosition = globe.pick(left, scene);
        const rightPosition = globe.pick(right, scene);

        if (!defined(leftPosition) || !defined(rightPosition)) {
            this.setState({
                barWidth: undefined,
                distanceLabel: undefined
            });
            return;
        }

        const leftCartographic = globe.ellipsoid.cartesianToCartographic(leftPosition);
        const rightCartographic = globe.ellipsoid.cartesianToCartographic(rightPosition);

        geodesic.setEndPoints(leftCartographic, rightCartographic);
        const pixelDistance = geodesic.surfaceDistance;

        // Find the first distance that makes the scale bar less than 100 pixels.
        const maxBarWidth = 100;
        let distance;
        for (let i = distances.length - 1; !defined(distance) && i >= 0; --i) {
            if (distances[i] / pixelDistance < maxBarWidth) {
                distance = distances[i];
            }
        }

        if (defined(distance)) {
            let label;
            if (distance >= 1000) {
                label = (distance / 1000).toString() + ' km';
            } else {
                label = distance.toString() + ' m';
            }

            this.setState({
                barWidth: (distance / pixelDistance) | 0,
                distanceLabel: label
            });

        } else {
            this.setState({
                barWidth: undefined,
                distanceLabel: undefined
            });
        }
    },

    updateDistanceLegendLeaflet(map) {
        const halfHeight = map.getSize().y / 2;
        const maxPixelWidth = 100;
        const maxMeters = map.containerPointToLatLng([0, halfHeight]).distanceTo(
            map.containerPointToLatLng([maxPixelWidth, halfHeight]));

        const meters = L.control.scale()._getRoundNum(maxMeters);
        const label = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km';

        this.setState({
            barWidth: (meters / maxMeters) * maxPixelWidth,
            distanceLabel: label
        });
    },

    render() {
        const barStyle = {
            width: this.state.barWidth + 'px',
            left: (5 + (125 - this.state.barWidth) / 2) + 'px',
            height: '2px'
        };

        const distanceLabel = this.state.distanceLabel ?
                (<div className={Styles.distanceLegend}>
                  <label>{this.state.distanceLabel}</label>
                  <div className={Styles.bar} style={barStyle}></div>
                 </div>) : null;

        return distanceLabel;
    },
});
module.exports = DistanceLegend;
