import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { GoogleMapProps, Marker } from '@react-google-maps/api';
import BingMapsReact from 'bingmaps-react';
import { EditableText, IconButton, Type } from 'browndash-components';
import { action, computed, IReactionDisposer, observable, ObservableMap, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, Opt } from '../../../../fields/Doc';
import { Width } from '../../../../fields/DocSymbols';
import { Id } from '../../../../fields/FieldSymbols';
import { InkTool } from '../../../../fields/InkField';
import { ScriptField } from '../../../../fields/ScriptField';
import { NumCast, StrCast } from '../../../../fields/Types';
import { emptyFunction, returnEmptyDoclist, returnEmptyFilter, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents, Utils } from '../../../../Utils';
import { Docs } from '../../../documents/Documents';
import { DragManager } from '../../../util/DragManager';
import { SnappingManager } from '../../../util/SnappingManager';
import { Transform } from '../../../util/Transform';
import { undoable, UndoManager } from '../../../util/UndoManager';
import { MarqueeOptionsMenu } from '../../collections/collectionFreeForm';
import { ViewBoxAnnotatableComponent, ViewBoxAnnotatableProps } from '../../DocComponent';
import { Colors } from '../../global/globalEnums';
import { MarqueeAnnotator } from '../../MarqueeAnnotator';
import { Annotation } from '../../pdf/Annotation';
import { SidebarAnnos } from '../../SidebarAnnos';
import { DocumentView } from '../DocumentView';
import { FieldView, FieldViewProps } from '../FieldView';
import { PinProps, PresBox } from '../trails';
import './MapBox.scss';
// amongus
/**
 * MapBox architecture:
 * Main component: MapBox.tsx
 * Supporting Components: SidebarAnnos, CollectionStackingView
 *
 * MapBox is a node that extends the ViewBoxAnnotatableComponent. Similar to PDFBox and WebBox, it supports interaction between sidebar content and document content.
 * The main body of MapBox uses Google Maps API to allow location retrieval, adding map markers, pan and zoom, and open street view.
 * Dash Document architecture is integrated with Maps API: When drag and dropping documents with ExifData (gps Latitude and Longitude information) available,
 * sidebarAddDocument function checks if the document contains lat & lng information, if it does, then the document is added to both the sidebar and the infowindow (a pop up corresponding to a map marker--pin on map).
 * The lat and lng field of the document is filled when importing (spec see ConvertDMSToDD method and processFileUpload method in Documents.ts).
 * A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
 */

// const _global = (window /* browser */ || global /* node */) as any;

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

const defaultCenter = {
    lat: 42.360081,
    lng: -71.058884,
};

const mapOptions = {
    fullscreenControl: false,
};

const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/  and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>

// const script = document.createElement('script');
// script.defer = true;
// script.async = true;
// script.src = `https://maps.googleapis.com/maps/api/js?key=${bingApiKey}&libraries=places,drawing`;
// document.head.appendChild(script);

/**
 * Consider integrating later: allows for drawing, circling, making shapes on map
 */
// const drawingManager = new window.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,
//         ],
//     },
// });

@observer
export class MapBox extends ViewBoxAnnotatableComponent<ViewBoxAnnotatableProps & FieldViewProps & Partial<GoogleMapProps>>() {
    private _dropDisposer?: DragManager.DragDropDisposer;
    private _disposers: { [name: string]: IReactionDisposer } = {};
    private _annotationLayer: React.RefObject<HTMLDivElement> = React.createRef();
    @observable private _overlayAnnoInfo: Opt<Doc>;
    showInfo = action((anno: Opt<Doc>) => (this._overlayAnnoInfo = anno));
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(MapBox, fieldKey);
    }
    public get SidebarKey() {
        return this.fieldKey + '_sidebar';
    }
    private _setPreviewCursor: undefined | ((x: number, y: number, drag: boolean, hide: boolean) => void);
    @computed get inlineTextAnnotations() {
        return this.allMapMarkers.filter(a => a.text_inlineAnnotations);
    }

    @observable private _map: google.maps.Map = null as unknown as google.maps.Map;
    @observable private selectedPlace: Doc | undefined;
    @observable private markerMap: { [id: string]: google.maps.Marker } = {};
    @observable private center = navigator.geolocation ? navigator.geolocation.getCurrentPosition : defaultCenter;
    @observable private _marqueeing: number[] | undefined;
    @observable private inputRef = React.createRef<HTMLInputElement>();
    @observable private searchMarkers: google.maps.Marker[] = [];
    @observable private searchBox = undefined as any; //  new window.google.maps.places.Autocomplete(this.inputRef.current!, options);
    @observable private _savedAnnotations = new ObservableMap<number, HTMLDivElement[]>();
    @computed get allSidebarDocs() {
        return DocListCast(this.dataDoc[this.SidebarKey]);
    }
    @computed get allMapMarkers() {
        return DocListCast(this.dataDoc[this.annotationKey]);
    }
    @observable private toggleAddMarker = false;
    private _mainCont: React.RefObject<HTMLDivElement> = React.createRef();

    @observable _showSidebar = false;
    @computed get SidebarShown() {
        return this._showSidebar || this.layoutDoc._layout_showSidebar ? true : false;
    }

    static _canAnnotate = true;
    static _hadSelection: boolean = false;
    private _sidebarRef = React.createRef<SidebarAnnos>();
    private _ref: React.RefObject<HTMLDivElement> = React.createRef();
    private _disposer: { [key: string]: IReactionDisposer } = {};
    componentDidMount() {
        this.props.setContentView?.(this);
    }

    componentWillUnmount(): void {
        Object.keys(this._disposer).forEach(key => this._disposer[key]?.());
    }
    // iterate allMarkers to size, center, and zoom map to contain all markers
    private fitBounds = (map: google.maps.Map) => {
        const curBounds = map.getBounds() ?? new window.google.maps.LatLngBounds();
        const isFitting = this.allMapMarkers.reduce((fits, place) => fits && curBounds?.contains({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), true as boolean);
        !isFitting && map.fitBounds(this.allMapMarkers.reduce((bounds, place) => bounds.extend({ lat: NumCast(place.lat), lng: NumCast(place.lng) }), new window.google.maps.LatLngBounds()));
    };

    /**
     * Load and render all map markers
     * @param marker
     * @param place
     */
    @action
    private markerLoadHandler = (marker: google.maps.Marker, place: Doc) => {
        place[Id] ? (this.markerMap[place[Id]] = marker) : null;
    };

    /**
     * on clicking the map marker, set the selected place to the marker document & set infowindowopen to be true
     * @param e
     * @param place
     */
    @action
    private markerClickHandler = (e: google.maps.MapMouseEvent, place: Doc) => {
        // set which place was clicked
        this.selectedPlace = place;
        // place.infoWindowOpen = true;
    };

    /**
     * Place the marker on google maps & store the empty marker as a MapMarker Document in allMarkers list
     * @param position - the LatLng position where the marker is placed
     * @param map
     */
    @action
    private placeMarker = (position: google.maps.LatLng, map: google.maps.Map) => {
        const marker = new google.maps.Marker({
            position: position,
            map: map,
        });
        map.panTo(position);
        const mapMarker = Docs.Create.PushpinDocument(NumCast(position.lat()), NumCast(position.lng()), false, [], {});
        this.addDocument(mapMarker, this.annotationKey);
    };

    _loadPending = true;

    @action
    centered = () => {
        if (this._loadPending && this._map.getBounds()) {
            this._loadPending = false;
            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
        }
        this.dataDoc.mapLat = this._map.getCenter()?.lat();
        this.dataDoc.mapLng = this._map.getCenter()?.lng();
    };

    @action
    zoomChanged = () => {
        if (this._loadPending && this._map.getBounds()) {
            this._loadPending = false;
            this.layoutDoc.freeform_fitContentsToBox && this.fitBounds(this._map);
        }
        this.dataDoc.mapZoom = this._map.getZoom();
    };

    /**
     * function that reads the place inputed from searchbox, then zoom in on the location that's been autocompleted;
     * add a customized temporary marker on the map
     */
    @action
    private handlePlaceChanged = () => {
        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) {
            this._map.fitBounds(place.geometry.viewport);
        } else {
            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,
            })
        );
    };

    /**
     * Called when dragging documents into map sidebar or directly into infowindow; to create a map marker, ref to MapMarkerDocument in Documents.ts
     * @param doc
     * @param sidebarKey
     * @returns
     */
    sidebarAddDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
        console.log('print all sidebar Docs');
        if (!this.layoutDoc._layout_showSidebar) this.toggleSidebar();
        const docs = doc instanceof Doc ? [doc] : doc;
        docs.forEach(doc => {
            if (doc.lat !== undefined && doc.lng !== undefined) {
                const existingMarker = this.allMapMarkers.find(marker => marker.lat === doc.lat && marker.lng === doc.lng);
                if (existingMarker) {
                    Doc.AddDocToList(existingMarker, 'data', doc);
                } else {
                    const marker = Docs.Create.PushpinDocument(NumCast(doc.lat), NumCast(doc.lng), false, [doc], {});
                    this.addDocument(marker, this.annotationKey);
                }
            }
        }); //add to annotation list

        return this.addDocument(doc, sidebarKey); // add to sidebar list
    };

    /**
     * Removing documents from the sidebar
     * @param doc
     * @param sidebarKey
     * @returns
     */
    sidebarRemoveDocument = (doc: Doc | Doc[], sidebarKey?: string) => {
        if (this.layoutDoc._layout_showSidebar) this.toggleSidebar();
        const docs = doc instanceof Doc ? [doc] : doc;
        return this.removeDocument(doc, sidebarKey);
    };

    /**
     * Toggle sidebar onclick the tiny comment button on the top right corner
     * @param e
     */
    sidebarBtnDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (e, down, delta) =>
                runInAction(() => {
                    const localDelta = this.props
                        .ScreenToLocalTransform()
                        .scale(this.props.NativeDimScaling?.() || 1)
                        .transformDirection(delta[0], delta[1]);
                    const fullWidth = this.layoutDoc[Width]();
                    const mapWidth = fullWidth - this.sidebarWidth();
                    if (this.sidebarWidth() + localDelta[0] > 0) {
                        this._showSidebar = true;
                        this.layoutDoc._width = fullWidth + localDelta[0];
                        this.layoutDoc._layout_sidebarWidthPercent = ((100 * (this.sidebarWidth() + localDelta[0])) / (fullWidth + localDelta[0])).toString() + '%';
                    } else {
                        this._showSidebar = false;
                        this.layoutDoc._width = mapWidth;
                        this.layoutDoc._layout_sidebarWidthPercent = '0%';
                    }
                    return false;
                }),
            emptyFunction,
            () => UndoManager.RunInBatch(this.toggleSidebar, 'toggle sidebar map')
        );
    };

    sidebarWidth = () => (Number(this.layout_sidebarWidthPercent.substring(0, this.layout_sidebarWidthPercent.length - 1)) / 100) * this.props.PanelWidth();
    @computed get layout_sidebarWidthPercent() {
        return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
    }
    @computed get sidebarColor() {
        return StrCast(this.layoutDoc.sidebar_color, StrCast(this.layoutDoc[this.props.fieldKey + '_backgroundColor'], '#e4e4e4'));
    }

    /**
     * Handles toggle of sidebar on click the little comment button
     */
    @computed get sidebarHandle() {
        return (
            <div
                className="mapBox-overlayButton-sidebar"
                key="sidebar"
                title="Toggle Sidebar"
                style={{
                    display: !this.props.isContentActive() ? 'none' : undefined,
                    top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
                    backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
                }}
                onPointerDown={this.sidebarBtnDown}>
                <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
            </div>
        );
    }

    // TODO: Adding highlight box layer to Maps
    @action
    toggleSidebar = () => {
        //1.2 * w * ? = .2 * w      .2/1.2
        const prevWidth = this.sidebarWidth();
        this.layoutDoc._layout_showSidebar = (this.layoutDoc._layout_sidebarWidthPercent = StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%') === '0%' ? `${(100 * 0.2) / 1.2}%` : '0%') !== '0%';
        this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
    };

    sidebarDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, this.sidebarMove, emptyFunction, () => setTimeout(this.toggleSidebar), true);
    };
    sidebarMove = (e: PointerEvent, down: number[], delta: number[]) => {
        const bounds = this._ref.current!.getBoundingClientRect();
        this.layoutDoc._layout_sidebarWidthPercent = '' + 100 * Math.max(0, 1 - (e.clientX - bounds.left) / bounds.width) + '%';
        this.layoutDoc._layout_showSidebar = this.layoutDoc._layout_sidebarWidthPercent !== '0%';
        e.preventDefault();
        return false;
    };

    setPreviewCursor = (func?: (x: number, y: number, drag: boolean, hide: boolean) => void) => (this._setPreviewCursor = func);

    @action
    onMarqueeDown = (e: React.PointerEvent) => {
        if (!e.altKey && e.button === 0 && this.props.isContentActive(true) && ![InkTool.Highlighter, InkTool.Pen, InkTool.Write].includes(Doc.ActiveTool)) {
            setupMoveUpEvents(
                this,
                e,
                action(e => {
                    MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
                    this._marqueeing = [e.clientX, e.clientY];
                    return true;
                }),
                returnFalse,
                () => MarqueeAnnotator.clearAnnotations(this._savedAnnotations),
                false
            );
        }
    };
    @action finishMarquee = (x?: number, y?: number) => {
        this._marqueeing = undefined;
        x !== undefined && y !== undefined && this._setPreviewCursor?.(x, y, false, false);
    };

    addDocumentWrapper = (doc: Doc | Doc[], annotationKey?: string) => {
        return this.addDocument(doc, annotationKey);
    };

    pointerEvents = () => (this.props.isContentActive() && !MarqueeOptionsMenu.Instance.isShown() ? 'all' : 'none');

    @computed get annotationLayer() {
        return (
            <div className="mapBox-annotationLayer" style={{ height: Doc.NativeHeight(this.Document) || undefined }} ref={this._annotationLayer}>
                {this.inlineTextAnnotations
                    .sort((a, b) => NumCast(a.y) - NumCast(b.y))
                    .map(anno => (
                        <Annotation key={`${anno[Id]}-annotation`} {...this.props} fieldKey={this.annotationKey} pointerEvents={this.pointerEvents} showInfo={this.showInfo} dataDoc={this.dataDoc} anno={anno} />
                    ))}
            </div>
        );
    }

    // Old get anchor function
    // getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => AnchorMenu.Instance?.GetAnchor(this._savedAnnotations, addAsAnnotation) ?? this.rootDoc;

    /**
     * render contents in allMapMarkers (e.g. images with exifData) into google maps as map marker
     * @returns
     */
    private renderMarkers = () => {
        return this.allMapMarkers.map(place => (
            <Marker key={place[Id]} position={{ lat: NumCast(place.lat), lng: NumCast(place.lng) }} onLoad={marker => this.markerLoadHandler(marker, place)} onClick={(e: google.maps.MapMouseEvent) => this.markerClickHandler(e, place)} />
        ));
    };

    panelWidth = () => this.props.PanelWidth() / (this.props.NativeDimScaling?.() || 1) - this.sidebarWidth();
    panelHeight = () => this.props.PanelHeight() / (this.props.NativeDimScaling?.() || 1);
    scrollXf = () => this.props.ScreenToLocalTransform().translate(0, NumCast(this.layoutDoc._layout_scrollTop));
    transparentFilter = () => [...this.props.childFilters(), Utils.IsTransparentFilter()];
    opaqueFilter = () => [...this.props.childFilters(), Utils.IsOpaqueFilter()];
    infoWidth = () => this.props.PanelWidth() / 5;
    infoHeight = () => this.props.PanelHeight() / 5;
    anchorMenuClick = () => this._sidebarRef.current?.anchorMenuClick;
    savedAnnotations = () => this._savedAnnotations;

    _bingSearchManager: any;
    _bingMap: any;
    get MicrosoftMaps() {
        return (window as any).Microsoft.Maps;
    }
    // uses Bing Search to retrieve lat/lng for a location.  eg.,
    //   const results = this.geocodeQuery(map.map, 'Philadelphia, PA');
    // to move the map to that location:
    //     const location = await this.geocodeQuery(this._bingMap, 'Philadelphia, PA');
    //     this._bingMap.current.setView({
    //        mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
    //        center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
    //     });
    //
    bingGeocode = (map: any, query: string) => {
        return new Promise<{ latitude: number; longitude: number }>((res, reject) => {
            //If search manager is not defined, load the search module.
            if (!this._bingSearchManager) {
                //Create an instance of the search manager and call the geocodeQuery function again.
                this.MicrosoftMaps.loadModule('Microsoft.Maps.Search', () => {
                    this._bingSearchManager = new this.MicrosoftMaps.Search.SearchManager(map.current);
                    res(this.bingGeocode(map, query));
                });
            } else {
                this._bingSearchManager.geocode({
                    where: query,
                    callback: action((r: any) => {
                        res(r.results[0].location);
                    }),
                    errorCallback: (e: any) => reject(),
                });
            }
        });
    };

    /**
     *
     *
     *  ERIC'S BING MAP CODE BELOW
     *
     *
     *
     **/
    @observable
    bingSearchBarContents: any = 'Boston, MA'; // For Bing Maps: The contents of the Bing search bar (string)

    geoDataRequestOptions = {
        entityType: 'PopulatedPlace',
    };

    // incrementer: number = 0;
    /*
     * Creates Pushpin doc and adds it to the list of annotations
     */
    @action
    createPushpin = (latitude: number, longitude: number) => {
        // Stores the pushpin as a MapMarkerDocument
        const mapMarker = Docs.Create.PushpinDocument(
            NumCast(latitude),
            NumCast(longitude),
            false,
            [],
            {}
            // ,'pushpinIDamongus'+ this.incrementer++
        );
        this.addDocument(mapMarker, this.annotationKey);

        // mapMarker.infoWindowOpen = true;
    };

    /*
     * Pushpin dblclick
     */
    @action
    pushpinDblClicked = (pin: any, pinDoc?: Doc) => {
        if (pinDoc) this.removePushpin(pinDoc, pin);
        else this._bingMap.current.entities.remove(pin);
    };
    /*
     * Pushpin onclick
     */
    @action
    pushpinClicked = (pinDoc: Doc) => {
        // TODO:
        // if (sidebarannos is not open) open sidebarannos
        // creates button onclick removes the doc from annotations

        // pan to pushpin location
        this.dataDoc.latitude = pinDoc.lat;
        this.dataDoc.longitude = pinDoc.lng;
    };

    /**
     * Returns a list of Pushpin docs
     */
    @computed get allMapPushpins() {
        return DocListCast(this.dataDoc[this.annotationKey]);
    }

    /**
     * Map OnClick ~> creates a pushpin
     */
    @action
    mapOnClick = (e: { location: { latitude: any; longitude: any } }) => {
        if (this.placePinOn) {
            this.createPushpin(e.location.latitude, e.location.longitude);
            // this.addAllPins();
            // this.placePinOn = false;
        }
    };

    /*
     * Updates values of layout doc to match the current map
     */
    @action
    updateLayout = () => {
        this.dataDoc.latitude = this._bingMap.current.getCenter().latitude;
        this.dataDoc.longitude = this._bingMap.current.getCenter().longitude;
        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
        // if(this.dataDoc.mapType == 'x'){
        //     this.dataDoc.locationToLookAt
        // }
        // this.dataDoc.mapType = new this.MicrosoftMaps.MapTypeId();
    };
    /*
     * Updates maptype
     */
    @action
    updateMapType = () => {
        this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
    };

    searched_pin: any;
    /*
     * For Bing Maps
     * Called by search button's onClick
     * Finds the geocode of the searched contents and sets location to that location
     **/
    @action
    bingSearch = async () => {
        const location = await this.bingGeocode(this._bingMap, this.bingSearchBarContents);
        this.dataDoc.latitude = location.latitude;
        this.dataDoc.longitude = location.longitude;
        this.dataDoc.mapZoom = this._bingMap.current.getZoom();
        // this.dataDoc.mapType = this._bingMap.current.getMapTypeId();
        // this.bingSearchBarContents=location.name;

        // Centers on the searched location
        // this._bingMap.current.panTo(new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude));

        // this._bingMap.current.setView({
        //     center: new this.MicrosoftMaps.Location(this.dataDoc.latitude, this.dataDoc.longitude),
        // });

        // Creates a temporary pin but does not add it to the dataDoc
        var temp = new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(location.latitude, location.longitude), {
            color: 'blue',
        });

        this.MicrosoftMaps.Events.addHandler(temp, 'dblclick', (e: any) => this.pushpinDblClicked(temp));
        if (temp != this.searched_pin || this.searched_pin == null) {
            this._bingMap.current.entities.push(temp);
            this.searched_pin = temp;
        }
    };

    /**
     * Adds all pushpins in dataDoc onto the map (render) - OLD & UNUSED
     */
    // @action
    // addAllPins = () => {
    //     this._bingMap.current.entities.clear();
    //     if (this.searched_pin) this._bingMap.current.entities.push(this.searched_pin);
    //     // this.allMapPushpins.map(pin => this.addPushpin(pin));
    // };

    /*
     * Returns doc w/ relevant info
     */
    getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps) => {
        const anchor = Docs.Create.MapanchorDocument({
            title: 'MapAnchor:' + this.rootDoc.title,
            config_latitude: NumCast(this.dataDoc.latitude),
            config_longitude: NumCast(this.dataDoc.longitude),
            config_mapZoom: NumCast(this.dataDoc.mapZoom),
            config_mapType: StrCast(this.dataDoc.mapType),
            // preslocationToLookAt:this.dataDoc.locationToLookAt,
            // presType:
            layout_unrendered: true,
            annotationOn: this.rootDoc,
        });
        if (anchor) {
            if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
            /* addAsAnnotation &&*/ this.addDocument(anchor);
            PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
            return anchor;
        }
        return this.rootDoc;
    };

    /*
     *   Input: pin doc
     *   Adds MicrosoftMaps Pushpin to the map (render)
     */
    @action
    addPushpin = (pin: Doc) => {
        const pushPin = pin.infoWindowOpen
            ? new this.MicrosoftMaps.Pushpin(new this.MicrosoftMaps.Location(pin.latitude, pin.longitude), {})
            : new this.MicrosoftMaps.Pushpin(
                  new this.MicrosoftMaps.Location(pin.latitude, pin.longitude)
                  // {icon: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/24/Map-Marker-Marker-Outside-Chartreuse-icon.png'}
              );

        this._bingMap.current.entities.push(pushPin);

        this.MicrosoftMaps.Events.addHandler(pushPin, 'click', (e: any) => this.pushpinClicked(pin));
        this.MicrosoftMaps.Events.addHandler(pushPin, 'dblclick', (e: any) => this.pushpinDblClicked(pushPin, pin));
    };

    @observable
    pinIsSelected_TEMPORARY: boolean = false; // toggles if remove pin button appears

    /*
     *   Input: pin doc
     *   Removes MicrosoftMaps Pushpin to the map (render)
     */
    @action
    removePushpin = (pinDoc: Doc, pin: any) => {
        // this.allMapPushpins
        // this.allMapPushpins.map(pin => this.addPushpin(pin));
        // this._bingMap.current.entities.clear();
        this._bingMap.current.entities.remove(pin);
        this.removeDocument(pinDoc, this.annotationKey);

        // this.dataDoc[this.annotationKey]
    };

    /**
     * View options for bing maps
     */
    bingViewOptions = {
        center: { latitude: this.dataDoc.latitude ?? defaultCenter.lat, longitude: this.dataDoc.longitude ?? defaultCenter.lng },
        zoom: this.dataDoc.latitude ?? 10,
        mapTypeId: 'grayscale',
    };

    /**
     * Map options
     */
    bingMapOptions = {
        navigationBarMode: 'square',
        backgroundColor: '#f1f3f4',
        enableInertia: true,
        supportedMapTypes: ['grayscale', 'canvasLight'],
        disableMapTypeSelectorMouseOver: true,
        // showScalebar:true
        // disableRoadView:true,
        // disableBirdseye:true
        streetsideOptions: {
            showProblemReporting: false,
            showCurrentAddress: false,
        },
    };

    /**
     * Toggles mode for placing a pin down on map
     */
    @observable
    placePinOn: boolean | undefined;
    @action
    togglePlacePin = () => {
        if (this.placePinOn) this.placePinOn = false;
        else this.placePinOn = true;
    };

    @action
    searchbarOnEdit = (newText: string) => {
        this.bingSearchBarContents = newText;
    };

    /*
     * Called when BingMap is first rendered
     * Initializes starting values
     */
    bingMapReady = (map: any) => {
        this._bingMap = map.map;
        if (!this._bingMap.current) {
            alert('NO Map!?');
        }
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'click', undoable(this.mapOnClick, 'Added Pin to Map'));
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'viewchangeend', undoable(this.updateLayout, 'Map Layout Change'));
        this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
        //this.updateLayout();
        // this.updateMapType();
        this._disposer.location = reaction(
            () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.mapZoom, mapType: this.rootDoc.mapType }),
            locationObject => {
                // if (this._bingMap.current)
                this._bingMap.current?.setView({
                    mapTypeId: locationObject.mapType,
                    zoom: locationObject.zoom,
                    center: new this.MicrosoftMaps.Location(locationObject.lat, locationObject.lng),
                });
            },
            { fireImmediately: true }
        );
        // this._disposer.pins = reaction(() => ({pinList:this.dataDoc[this.annotationKey]}),
        // (pinsObject) => {
        //     this._bingMap.current.entities.clear();
        //     pinsObject.pinList.map((pushpin: Doc) => (
        //                         <DocumentView
        //                             {...this.props}
        //                             Document={pushpin}
        //                             DataDoc={undefined}
        //                             PanelWidth={returnOne}
        //                             PanelHeight={returnOne}
        //                             NativeWidth={returnOne}
        //                             NativeHeight={returnOne}
        //                             onClick={()=>new ScriptField(undefined)}
        //                             onKey={undefined}
        //                             onDoubleClick={undefined}
        //                             onBrowseClick={undefined}
        //                             docFilters={returnEmptyDoclist}
        //                             docRangeFilters={returnEmptyDoclist}
        //                             searchFilterDocs={returnEmptyDoclist}
        //                             isDocumentActive={returnFalse}
        //                             isContentActive={returnFalse}
        //                             addDocTab={returnFalse}
        //                             ScreenToLocalTransform={()=>new Transform(0,0,0)}
        //                             fitContentsToBox={undefined}
        //                             focus={returnOne}
        //                 />));

        // }, {fireImmediately: false});
    };
    searchbarKeyDown = (e: any) => {
        if (e.key === 'Enter') {
            this.bingSearch();
        }
    };
    render() {
        const renderAnnotations = (childFilters?: () => string[]) => null;
        return (
            <div className="mapBox" ref={this._ref}>
                <div
                    className="mapBox-wrapper"
                    onWheel={e => e.stopPropagation()}
                    onPointerDown={async e => {
                        e.button === 0 && !e.ctrlKey && e.stopPropagation();
                        // just a simple test of bing maps geocode api
                        // const loc = await this.bingGeocode(this._bingMap, 'Philadelphia, PA');
                        // this._bingMap.current.setView({
                        //     mapTypeId: this.MicrosoftMaps.MapTypeId.aerial,
                        //     center: new this.MicrosoftMaps.Location(loc.latitude, loc.longitude),
                        //     zoom: 15,
                        // });
                    }}
                    style={{ width: `calc(100% - ${this.layout_sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
                    <div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
                    {renderAnnotations(this.opaqueFilter)}
                    {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
                    {this.annotationLayer}

                    <div className="mapBox-searchbar">
                        <EditableText
                            // editing
                            setVal={(newText: string | number) => typeof newText === 'string' && this.searchbarOnEdit(newText)}
                            placeholder="Boston"
                            // text="text"
                        />
                        <IconButton
                            icon={
                                <svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="magnifying-glass" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" color="#DFDFDF">
                                    <path
                                        fill="currentColor"
                                        d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"></path>
                                </svg>
                            }
                            onClick={this.bingSearch}
                            onDoubleClick={function noRefCheck() {}}
                            onPointerDown={function noRefCheck() {}}
                            onPointerDownCapture={function noRefCheck() {}}
                            onPointerMove={function noRefCheck() {}}
                            onPointerMoveCapture={function noRefCheck() {}}
                            onPointerUp={function noRefCheck() {}}
                            type={Type.TERT}
                        />
                        <input type="checkbox" id="switch" className="checkbox" checked={this.placePinOn} onChange={this.togglePlacePin} />

                        <label htmlFor="switch" className="toggle">
                            <p>{this.placePinOn ? 'Place Pin ON' : 'Place Pin OFF'}</p>
                        </label>
                    </div>

                    <div className="mapBox-topbar">
                        {/* <input type="button" value="Search" onClick={this.bingSearch} /> */}

                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />} */}
                        {/* {this.placePinOn ? <input type="button" value="Place Pin Mode On" onClick={this.togglePlacePin} /> : <input type="button" value="Place Pin Mode Off" onClick={this.togglePlacePin} />}

                        <Toggle
                            onClick={this.togglePlacePin}
                            onPointerDown={function noRefCheck(){}}
                            onPointerDownCapture={function noRefCheck(){}}
                            onPointerMove={function noRefCheck(){}}
                            onPointerMoveCapture={function noRefCheck(){}}
                            onPointerUp={function noRefCheck(){}}
                            text="Place Pin"
                            toggleType = "checkbox"
                            toggleStatus= {false}
                            type="secondary"
                        /> */}
                    </div>

                    {/* {this.pinIsSelected_TEMPORARY? <input type="button" value="Delete Pin" onClick={this.removeSelectedPin} /> : null} */}
                    <BingMapsReact onMapReady={this.bingMapReady} bingMapsKey={bingApiKey} height="100%" mapOptions={this.bingMapOptions} width="100%" viewOptions={this.bingViewOptions}></BingMapsReact>
                    <div>
                        {this.allMapPushpins.map(pushpin => (
                            <DocumentView
                                {...this.props}
                                Document={pushpin}
                                DataDoc={undefined}
                                PanelWidth={returnOne}
                                PanelHeight={returnOne}
                                NativeWidth={returnOne}
                                NativeHeight={returnOne}
                                onClick={() => new ScriptField(undefined)}
                                onKey={undefined}
                                onDoubleClick={undefined}
                                onBrowseClick={undefined}
                                childFilters={returnEmptyFilter}
                                childFiltersByRanges={returnEmptyFilter}
                                searchFilterDocs={returnEmptyDoclist}
                                isDocumentActive={returnFalse}
                                isContentActive={returnFalse}
                                addDocTab={returnFalse}
                                ScreenToLocalTransform={() => new Transform(0, 0, 0)}
                                fitContentsToBox={undefined}
                                focus={returnOne}
                            />
                        ))}
                    </div>
                    {/* <MapBoxInfoWindow
                                    key={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})[Id]}
                                    {...OmitKeys(this.props, ['NativeWidth', 'NativeHeight', 'setContentView']).omit}
                                    place={Docs.Create.MapMarkerDocument(NumCast(40), NumCast(40), false, [], {})}
                                    markerMap={this.markerMap}
                                    PanelWidth={this.infoWidth}
                                    PanelHeight={this.infoHeight}
                                    moveDocument={this.moveDocument}
                                    isAnyChildContentActive={this.isAnyChildContentActive}
                                    whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                                /> */}

                    {!this._marqueeing || !this._mainCont.current || !this._annotationLayer.current ? null : (
                        <MarqueeAnnotator
                            rootDoc={this.rootDoc}
                            anchorMenuClick={this.anchorMenuClick}
                            scrollTop={0}
                            down={this._marqueeing}
                            scaling={returnOne}
                            addDocument={this.addDocumentWrapper}
                            docView={this.props.docViewPath().lastElement()}
                            finishMarquee={this.finishMarquee}
                            savedAnnotations={this.savedAnnotations}
                            annotationLayer={this._annotationLayer.current}
                            selectionText={returnEmptyString}
                            mainCont={this._mainCont.current}
                        />
                    )}
                </div>
                {/* </LoadScript > */}
                <div className="mapBox-sidebar" style={{ width: `${this.layout_sidebarWidthPercent}`, backgroundColor: `${this.sidebarColor}` }}>
                    <SidebarAnnos
                        ref={this._sidebarRef}
                        {...this.props}
                        fieldKey={this.fieldKey}
                        rootDoc={this.rootDoc}
                        layoutDoc={this.layoutDoc}
                        dataDoc={this.dataDoc}
                        usePanelWidth={true}
                        showSidebar={this.SidebarShown}
                        nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
                        whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
                        PanelWidth={this.sidebarWidth}
                        sidebarAddDocument={this.sidebarAddDocument}
                        moveDocument={this.moveDocument}
                        removeDocument={this.sidebarRemoveDocument}
                    />
                </div>
                {this.sidebarHandle}
            </div>
        );
    }
}
