import { Autocomplete, GoogleMap, GoogleMapProps, InfoBox, InfoWindow, Marker } from '@react-google-maps/api';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from "mobx-react";
import * as React from "react";
import { Doc, WidthSym } from '../../../../fields/Doc';
import { documentSchema } from '../../../../fields/documentSchemas';
import { makeInterface } from '../../../../fields/Schema';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
import { setupMoveUpEvents, emptyFunction } from '../../../../Utils';
import { DragManager } from '../../../util/DragManager';
import { undoBatch } from '../../../util/UndoManager';
import { CollectionViewType } from '../../collections/CollectionView';
import { TabDocView } from '../../collections/TabDocView';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { SidebarAnnos } from '../../SidebarAnnos';
import { StyleProp } from '../../StyleProvider';
import { FieldView, FieldViewProps } from '../FieldView';
import { PresMovement } from '../PresBox';
import "./MapBox.scss"
import { MapMarker } from './MapMarker';

type MapDocument = makeInterface<[typeof documentSchema]>;
const MapDocument = makeInterface(documentSchema);

export type Coordinates = {
    lat: number,
    lng: number,
}

export type LocationData = {
    id: string;
    pos: Coordinates;
};

const mapContainerStyle = {
    height: '100%',
};

const defaultCenter = {
    lat: 38.685,
    lng: -115.234,
};

const mapOptions = {
    fullscreenControl: false,
}

const drawingManager = new google.maps.drawing.DrawingManager({
    drawingControl: true,
    drawingControlOptions: {
        position: google.maps.ControlPosition.TOP_RIGHT,
        drawingModes: [
            google.maps.drawing.OverlayType.MARKER,
            // currently we are not supporting the following drawing mode on map, a thought for future development
            // google.maps.drawing.OverlayType.CIRCLE,
            // google.maps.drawing.OverlayType.POLYLINE,
        ],
    },
});

const options = {
    fields: ["formatted_address", "geometry", "name"], // note: level of details is charged by item per retrieval, not recommended to return all fields
    strictBounds: false,
    types: ["establishment"], // type pf places, subject of change according to user need
} as google.maps.places.AutocompleteOptions;

@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>, MapDocument>(MapDocument) {
    private _dropDisposer?: DragManager.DragDropDisposer;
    private _disposers: { [name: string]: IReactionDisposer } = {};
    public static LayoutString(fieldKey: string) { return FieldView.LayoutString(MapBox, fieldKey); }

    @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
    @observable private selectedPlace: MapMarker | undefined;
    @observable private markerMap: { [id: string]: google.maps.Marker } = {};
    @observable private center = defaultCenter;
    @observable private zoom = 2.5;
    @observable private infoWindowOpen = false;
    @observable private bounds = new window.google.maps.LatLngBounds();
    @observable private inputRef = React.createRef<HTMLInputElement>();
    @observable private searchMarkers: google.maps.Marker[] = [];
    @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
    @observable private childDocs: MapMarker[] = [];

    static _canAnnotate = true;
    static _hadSelection: boolean = false;
    private _sidebarRef = React.createRef<SidebarAnnos>();
    private _ref: React.RefObject<HTMLDivElement> = React.createRef();


    @action
    private setSearchBox = (searchBox: any) => {
        this.searchBox = searchBox;
    }

    // iterate childDocs to size, center, and zoom map to contain all markers
    private fitBounds = (map: google.maps.Map) => {
        console.log('map bound is:' + this.bounds);
        this.childDocs.map(place => {
            this.bounds.extend(place._latlngLocation);
            return place._markerId;
        });
        map.fitBounds(this.bounds)
    }

    // store a reference to google map instance; fit map bounds to contain all markers
    @action
    private loadHandler = (map: google.maps.Map) => {
        this._map = map;
        drawingManager.setMap(map);
        this.fitBounds(map);
    }

    @action
    private markerLoadHandler = (marker: google.maps.Marker, place: MapMarker) => {
        place._markerId ? this.markerMap[place._markerId] = marker : null;
    }

    @action
    private markerClickHandler = (e: MouseEvent, place: any) => {
        // set which place was clicked
        this.selectedPlace = place;

        console.log(this.selectedPlace);

        // used so clicking a second marker works
        if (this.infoWindowOpen) {
            this.infoWindowOpen = false;
            console.log("closeinfowindow")
        }
        this.infoWindowOpen = true;
        console.log("open infowindow")
    }

    /**
     * Called when dragging documents into map sidebar
     * @param doc 
     * @param sidebarKey 
     * @returns 
     */
    sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
        if (!this.layoutDoc._showSidebar) this.toggleSidebar();
        return this.addDocument(doc, sidebarKey);
    }

    /**
     * What does this do exactly? How to operate on sidebar?
     * @param e 
     */
    sidebarBtnDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, (e, down, delta) => {
            const localDelta = this.props.ScreenToLocalTransform().scale(this.props.scaling?.() || 1).transformDirection(delta[0], delta[1]);
            const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
            const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
            const ratio = (curNativeWidth + localDelta[0] / (this.props.scaling?.() || 1)) / nativeWidth;
            if (ratio >= 1) {
                this.layoutDoc.nativeWidth = nativeWidth * ratio;
                this.layoutDoc._width = this.layoutDoc[WidthSym]() + localDelta[0];
                this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
            }
            return false;
        }, emptyFunction, this.toggleSidebar);
    }

    toggleSidebar = action(() => {
        const nativeWidth = NumCast(this.layoutDoc[this.fieldKey + "-nativeWidth"]);
        const ratio = ((!this.layoutDoc.nativeWidth || this.layoutDoc.nativeWidth === nativeWidth ? 250 : 0) + nativeWidth) / nativeWidth;
        const curNativeWidth = NumCast(this.layoutDoc.nativeWidth, nativeWidth);
        this.layoutDoc.nativeWidth = nativeWidth * ratio;
        this.layoutDoc._width = this.layoutDoc[WidthSym]() * nativeWidth * ratio / curNativeWidth;
        this.layoutDoc._showSidebar = nativeWidth !== this.layoutDoc._nativeWidth;
    });

    sidebarWidth = () => {
        !this.layoutDoc._showSidebar ? 0 :
            (NumCast(this.layoutDoc.nativeWidth) - Doc.NativeWidth(this.dataDoc)) * this.props.PanelWidth() / NumCast(this.layoutDoc.nativeWidth);
    }

    @action
    private handlePlaceChanged = () => {
        console.log(this.searchBox);
        const place = this.searchBox.getPlace();

        if (!place.geometry || !place.geometry.location) {
            // user entered the name of a place that wasn't suggested & pressed the enter key, or place details request failed
            window.alert("No details available for input: '" + place.name + "'");
            return;
        }

        // zoom in on the location of the search result
        if (place.geometry.viewport) {
            console.log(this._map);
            this._map.fitBounds(place.geometry.viewport);
        } else {
            console.log(this._map);
            this._map.setCenter(place.geometry.location);
            this._map.setZoom(17);
        }

        // customize icon => customized icon for the nature of the location selected
        const icon = {
            url: place.icon as string,
            size: new google.maps.Size(71, 71),
            origin: new google.maps.Point(0, 0),
            anchor: new google.maps.Point(17, 34),
            scaledSize: new google.maps.Size(25, 25),
        };

        // put temporary cutomized marker on searched location
        this.searchMarkers.forEach((marker) => {
            marker.setMap(null);
        });
        this.searchMarkers = [];
        this.searchMarkers.push(
            new window.google.maps.Marker({
                map: this._map,
                icon,
                title: place.name,
                position: place.geometry.location,
            })
        )
    }

    @action
    private handleInfoWindowClose = () => {
        if (this.infoWindowOpen) {
            this.infoWindowOpen = false;
        }
        this.infoWindowOpen = false;
        this.selectedPlace = undefined;
    }

    @action
    private addMarker = (location: google.maps.LatLng | undefined, map: google.maps.Map) => {
        new window.google.maps.Marker({
            position: location,
            map: map
        });
    }

    render() {
        const { Document, fieldKey, isContentActive: active } = this.props;

        return <div className="MapBox" ref={this._ref}>
            {/* HELLO WORLD! */}
            <div className={"MapBox-contents"}
                style={{ pointerEvents: active() ? undefined : "none", overflow: 'hidden' }}
                onWheel={e => e.stopPropagation()}
                onPointerDown={e => (e.button === 0 && !e.ctrlKey) && e.stopPropagation()} >
                {/* // {/* <LoadScript
                //     googleMapsApiKey={process.env.GOOGLE_MAPS!}
                //     libraries={['places', 'drawing']}
                // >  */}
                <div className="map-wrapper">
                    <GoogleMap
                        mapContainerStyle={mapContainerStyle}
                        zoom={this.zoom}
                        center={this.center}
                        onLoad={map => this.loadHandler(map)}
                        options={mapOptions}
                    >
                        <Autocomplete
                            onLoad={this.setSearchBox}
                            onPlaceChanged={this.handlePlaceChanged}>
                            <input ref={this.inputRef} className="searchbox" type="text" placeholder="Search anywhere:" />
                        </Autocomplete>

                        {this.childDocs.map(place => (
                            <Marker
                                key={place._markerId}
                                position={place._latlngLocation}
                                onLoad={marker => this.markerLoadHandler(marker, place)}
                                onClick={e => this.markerClickHandler(e, place)}
                            />
                        ))}
                        {this.infoWindowOpen && this.selectedPlace && (
                            <InfoWindow
                                anchor={this.markerMap[this.selectedPlace._markerId!]}
                                onCloseClick={this.handleInfoWindowClose}
                            >
                                <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
                                    <div style={{ fontSize: 16 }}>
                                        <div>
                                            <img src="http://placekitten.com/200/300" />
                                            <hr />
                                            <form>
                                                <label>Title: </label><br />
                                                <input type="text" id="fname" name="fname"></input><br />
                                                <label>Desription: </label><br />
                                                <textarea style={{ height: 150 }} id="lname" name="lname" placeholder="Notes, a short description of this location, a brief comment, etc."></textarea>
                                            </form>
                                            <hr />
                                            <div>
                                                <button>New link+</button>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </InfoWindow>
                        )}
                    </GoogleMap>
                </div>
                {/* {/* </LoadScript > */}
                <SidebarAnnos ref={this._sidebarRef}
                    {...this.props}
                    fieldKey={this.annotationKey}
                    rootDoc={this.rootDoc}
                    layoutDoc={this.layoutDoc}
                    dataDoc={this.dataDoc}
                    sidebarAddDocument={this.sidebarAddDocument}
                    moveDocument={this.moveDocument}
                    removeDocument={this.removeDocument}
                    isContentActive={this.isContentActive}
                />
            </div >
        </div >;
    }

}