import { GoogleMap, Marker, InfoWindow, InfoBox, useJsApiLoader, LoadScript, GoogleMapProps, StandaloneSearchBox, Autocomplete } from '@react-google-maps/api';
import { observable, action, computed, Lambda, runInAction, IReactionDisposer } from "mobx";
import { observer } from "mobx-react";
import { Doc, DocListCast, Field, FieldResult, Opt } from "../../../../fields/Doc";
import { documentSchema } from "../../../../fields/documentSchemas";
import { Id } from "../../../../fields/FieldSymbols";
import { makeInterface } from "../../../../fields/Schema";
import { Cast, NumCast, ScriptCast, StrCast } from "../../../../fields/Types";
import { LinkManager } from "../../../util/LinkManager";
import { undoBatch, UndoManager } from "../../../util/UndoManager";
import "./CollectionMapView.scss";
import { CollectionSubView } from "../CollectionSubView";
import React = require("react");
import requestPromise = require("request-promise");
import ReactDOM from 'react-dom';
import { DragManager } from '../../../util/DragManager';
import { MapMarker } from '../../nodes/MapMarker/MapMarker';


/**
 * Idea behind storing a marker:
 * 1. on the map api, have a button "add marker" that adds the marker on the map & store the marker as a node in the collection
 * (but don't render the marker in the collection itself)
 * 2. each marker, as a node, has the same feature as all other nodes for linking (the same way one could form a link between a node inside a child collection 
 * and a node outside the child collection)
 * 
 * /util/LinkManager.ts -- link relations
 * 
 */

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

export type LocationData = {
    id?: number;
    pos?: { lat: number, lng: number };
};

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

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

@observer
export default class CollectionMapView extends CollectionSubView<MapSchema, Partial<GoogleMapProps>>(MapSchema) {
    private _dropDisposer?: DragManager.DragDropDisposer;
    private _disposers: { [name: string]: IReactionDisposer } = {};


    @observable private _map = null as unknown as google.maps.Map;
    @observable private selectedPlace: LocationData = null as any;
    @observable private markerMap = {};
    @observable private center = defaultCenter;
    @observable private zoom = 2.5;
    @observable private clickedLatLng = null;
    @observable private infoWindowOpen = false;
    @observable private bounds = new window.google.maps.LatLngBounds();
    @observable private inputRef = React.createRef<HTMLInputElement>();
    @observable private buttonRef = React.createRef<HTMLDivElement>();
    @observable private searchMarkers: google.maps.Marker[] = [];

    private 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;

    @observable private searchBox = new window.google.maps.places.Autocomplete(this.inputRef.current!, this.options);

    @observable private myPlaces = [
        { id: 1, pos: { lat: 39.09366509575983, lng: -94.58751660204751 } },
        { id: 2, pos: { lat: 41.82399, lng: -71.41283 } },
        { id: 3, pos: { lat: 47.606214, lng: -122.33207 } },
    ];

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

    @action
    private setButton = (button: any) => {
        this.buttonRef = button;
        this._map.controls[google.maps.ControlPosition.TOP_RIGHT].push(this.buttonRef.current!)
    }

    // iterate myPlaces to size, center, and zoom map to contain all markers
    private fitBounds = (map: google.maps.Map) => {
        console.log('map bound is:' + this.bounds);
        this.myPlaces.map(place => {
            this.bounds.extend(place.pos);
            return place.id;
        });
        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;
        this.fitBounds(map);

        // //add a custom control for button add marker 
        // //TODO: why this doesn't work
        // const google = window.google;
        // console.log("google window: " + google)
        // const controlButtonDiv = document.createElement('div');
        // ReactDOM.render(<button onClick={() => console.log('click me to add marker!')}>Add Marker</button>, controlButtonDiv);
        // map.controls[google.maps.ControlPosition.TOP_RIGHT].push(controlButtonDiv);
    }

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

        console.log(this.selectedPlace.id);
        console.log(this.selectedPlace.pos);

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

    // @action
    // private handleInfoWindowClose = () => {
    //     if (this.infoWindowOpen) {
    //         this.infoWindowOpen = false;
    //     }
    //     this.infoWindowOpen = false;
    //     this.selectedPlace = { id: undefined, pos: undefined };
    // }

    // @action 
    // private markerLoadHandler = (marker: any, place: LocationData) => {
    //     if (marker != null) {
    //         this.markerMap[place.id] = marker;
    //     }
    // }

    @action
    @undoBatch
    private handleDragMarker = (marker: any, place: LocationData) => {
        // if (marker != null) {
        //     place = {
        //         id: place.id,
        //         position: {
        //             lat: marker.latLng.lat().toFixed(3),
        //             lng: marker.latLng.lng().toFixed(3)
        //         }
        //     }

        //     console.log(place);
        //     console.log(this.myPlaces);
        // }
    }

    @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 (hard to tell from other pre-existing markers, probably won't implement
         */
        // 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),
        // };

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

    // @computed get markerContent() {
    //     const allMarkers = this.childLayoutPairs
    //     const markerId = NumCast(this.layoutDoc._itemIndex);
    //     const selectedMarker = this.childLayoutPairs?.[markerId];
    //     const position = {
    //         lat: NumCast(this.layoutDoc.lat),
    //         lng: NumCast(this.layoutDoc.lng)
    //     }
    //     return <>
    //         {
    //             allMarkers?.map(place => (
    //                 <Marker
    //                     key={markerId}
    //                     position={position}
    //                     onClick={e => this.markerClickHandler(e, place.layout)} //??
    //                     draggable={true}
    //                     onDragEnd={marker => this.handleDragMarker(marker, place.layout)}
    //                 />
    //             ))
    //         }
    //         {this.infoWindowOpen && selectedMarker && (
    //             <InfoBox
    //                 //anchor={selectedMarker}
    //                 // onCloseClick={this.handleInfoWindowClose}
    //                 position={position}
    //             // options={{ enableEventPropagation: true }}
    //             >
    //                 <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
    //                     <div style={{ fontSize: 16 }}>
    //                         {/* the linkmenu as the ones in other nodes */}
    //                         <div>
    //                             <a>a link to another node</a>
    //                             <hr />
    //                         </div>
    //                         <div>
    //                             <a>a link to another node</a>
    //                             <hr />
    //                         </div>
    //                         <div>
    //                             <a>a link to another node</a>
    //                             <hr />
    //                         </div>
    //                         <div>
    //                             <button>New +</button>
    //                         </div>
    //                     </div>
    //                 </div>
    //             </InfoBox>
    //         )}

    //     </>
    // }

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

    // @action
    // private renderMarkerToMap = (marker: Doc) => {
    //     const id = StrCast(marker.id);
    //     const lat = NumCast(marker.lat);
    //     const lng = NumCast(marker.lng);

    //     return <Marker
    //         key={id}
    //         position={{ lat: lat, lng: lng }}
    //         onClick={e => this.markerClickHandler(e, marker)}
    //     />
    // }

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

        return <div className="collectionMapView" ref={this.createDashEventsTarget}>

            <div className={"collectionMapView-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)}
                    >
                        <Autocomplete
                            onLoad={this.setSearchBox}
                            onPlaceChanged={this.handlePlaceChanged}>
                            <input ref={this.inputRef} className="searchbox" type="text" placeholder="Search anywhere:" />
                        </Autocomplete>

                        <div onLoad={this.setButton}>
                            <div ref={this.buttonRef} className="add-button-UI" >
                                <div className="add-button-Text">Add Marker</div>
                            </div>
                        </div>

                        {this.myPlaces.map(place => (
                            <Marker
                                key={place.id}
                                position={place.pos}
                                // onLoad={marker => this.markerLoadHandler(marker, place)}
                                onClick={e => this.markerClickHandler(e, place)}
                                draggable={true}
                                onDragEnd={marker => this.handleDragMarker(marker, place)}
                            />
                        ))}
                        {this.infoWindowOpen && this.selectedPlace && (
                            <InfoBox
                                // anchor={this.markerMap[this.selectedPlace.id]}
                                // onCloseClick={this.handleInfoWindowClose}
                                position={this.selectedPlace.pos}
                            // options={{ enableEventPropagation: true }}
                            >
                                <div style={{ backgroundColor: 'white', opacity: 0.75, padding: 12 }}>
                                    <div style={{ fontSize: 16 }}>
                                        <div>
                                            <a>a link to another node</a>
                                            <hr />
                                        </div>
                                        <div>
                                            <a>a link to another node</a>
                                            <hr />
                                        </div>
                                        <div>
                                            <a>a link to another node</a>
                                            <hr />
                                        </div>
                                        <div>
                                            <button>New +</button>
                                        </div>
                                    </div>
                                </div>
                            </InfoBox>
                        )}
                    </GoogleMap>
                </div>
                {/* </LoadScript > */}
            </div >
        </div >;
    }

}