import { useCallback, useEffect, useRef, useState } from 'react'
import GeoJSON from 'ol/format/GeoJSON'
import 'ol/ol.css'
import Map from 'ol/Map'
import View from 'ol/View'
import { Draw, Modify, Select, Snap, Translate } from 'ol/interaction'
import { ImageStatic, OSM, Vector as VectorSource, XYZ } from 'ol/source'
import { Tile as TileLayer, Vector as VectorLayer } from 'ol/layer'
import { Type } from 'ol/geom/Geometry'
import { click, never, noModifierKeys, platformModifierKeyOnly, primaryAction, shiftKeyOnly } from 'ol/events/condition'
import Feature from 'ol/Feature'
import { SatelliteMapsProvider } from '../../interfaces/map'
import { containsExtent, Extent } from 'ol/extent'
import { drawingLayerStyle, regionsLayerStyle, selectStyle } from './MapStyles'
import { googleMapsSatelliteSource, regionsLayerSource, yandexMapsSatelliteSource } from './MapSources'
import { mapCenter } from './MapConstants'
import ImageLayer from 'ol/layer/Image'
import VectorImageLayer from 'ol/layer/VectorImage'
import { Circle, LineString, MultiPoint, Point, Polygon, SimpleGeometry } from 'ol/geom'
import { fromCircle, fromExtent } from 'ol/geom/Polygon'
import Collection from 'ol/Collection'
import { Coordinate } from 'ol/coordinate'
import { Stroke, Fill, Circle as CircleStyle, Style, Text } from 'ol/style'
import { calculateCenter, calculateExtent, calculateRotationAngle, rotateProjection } from './mapUtils'
import MapBrowserEvent from 'ol/MapBrowserEvent'
import { get, transform } from 'ol/proj'
import { useCities } from '../../hooks/swrHooks'
import useSWR from 'swr'
import { fetcher } from '../../http/axiosInstance'
import { BASE_URL } from '../../constants'
import { Accordion, ActionIcon, Autocomplete, Box, Button, CloseButton, Flex, Grid, Select as MantineSelect, Text as MantineText, MantineStyleProp, rem, ScrollAreaAutosize, Slider, useMantineColorScheme, Divider, Portal } from '@mantine/core'
import { IconApi, IconArrowBackUp, IconArrowsMove, IconCircle, IconExclamationCircle, IconLine, IconPlus, IconPoint, IconPolygon, IconRuler, IconSettings, IconTable, IconUpload } from '@tabler/icons-react'
import { getGridCellPosition } from './mapUtils'
import { IFigure, ILine } from '../../interfaces/gis'
import axios from 'axios'
import ObjectParameter from './ObjectParameter'
import { IObjectData, IObjectParam } from '../../interfaces/objects'
import ObjectData from './ObjectData'

const MapComponent = () => {
    //const { cities } = useCities(100, 1)

    // useEffect(() => {
    //     if (cities) {
    //         cities.map((city: any) => {
    //             citiesLayer.current?.getSource()?.addFeature(new Feature(new Point(fromLonLat([city.longitude, city.width]))))
    //         })
    //     }
    // }, [cities])

    const [currentCoordinate, setCurrentCoordinate] = useState<Coordinate | null>(null)
    const [currentZ, setCurrentZ] = useState<number | undefined>(undefined)
    const [currentX, setCurrentX] = useState<number | undefined>(undefined)
    const [currentY, setCurrentY] = useState<number | undefined>(undefined)

    const [file, setFile] = useState(null)
    const [polygonExtent, setPolygonExtent] = useState<Extent | undefined>(undefined)
    const [bottomLeft, setBottomLeft] = useState<Coordinate | undefined>(undefined)
    const [topLeft, setTopLeft] = useState<Coordinate | undefined>(undefined)
    const [topRight, setTopRight] = useState<Coordinate | undefined>(undefined)
    const [bottomRight, setBottomRight] = useState<Coordinate | undefined>(undefined)

    const mapElement = useRef<HTMLDivElement | null>(null)
    const [currentTool, setCurrentTool] = useState<Type | null>(null)

    const map = useRef<Map | null>(null)

    const [satMapsProvider, setSatMapsProvider] = useState<SatelliteMapsProvider>('custom')

    const gMapsSatSource = useRef<XYZ>(googleMapsSatelliteSource)

    const customMapSource = useRef<XYZ>(new XYZ({
        url: `${import.meta.env.VITE_API_EMS_URL}/tile/custom/{z}/{x}/{y}`,
        attributions: 'Custom map data'
    }))

    const yMapsSatSource = useRef<XYZ>(yandexMapsSatelliteSource)

    const satLayer = useRef<TileLayer>(new TileLayer({
        source: gMapsSatSource.current,
    }))

    const draw = useRef<Draw | null>(null)
    const snap = useRef<Snap | null>(null)

    const selectFeature = useRef<Select>(new Select({
        condition: function (mapBrowserEvent) {
            return click(mapBrowserEvent) && shiftKeyOnly(mapBrowserEvent);
        },
    }))

    const nodeLayer = useRef<VectorLayer | null>(null)
    const nodeLayerSource = useRef<VectorSource>(new VectorSource())

    const overlayLayer = useRef<VectorLayer | null>(null)
    const overlayLayerSource = useRef<VectorSource>(new VectorSource())

    const drawingLayer = useRef<VectorLayer | null>(null)
    const drawingLayerSource = useRef<VectorSource>(new VectorSource())

    const citiesLayer = useRef<VectorLayer>(new VectorLayer({
        source: new VectorSource()
    }))

    const figuresLayer = useRef<VectorLayer>(new VectorLayer({
        source: new VectorSource()
    }))

    const linesLayer = useRef<VectorLayer>(new VectorLayer({
        source: new VectorSource()
    }))

    const regionsLayer = useRef<VectorImageLayer>(new VectorImageLayer({
        source: regionsLayerSource,
        style: regionsLayerStyle
    }))

    const selectedRegion = useRef<Feature | null>(null)

    const baseLayer = useRef<TileLayer>(new TileLayer({
        source: new OSM(),
    }))

    const imageLayer = useRef<ImageLayer<ImageStatic>>(new ImageLayer())

    const addInteractions = () => {
        if (currentTool) {
            draw.current = new Draw({
                source: drawingLayerSource.current,
                type: currentTool,
                condition: noModifierKeys
            })

            draw.current.on('drawend', function (s) {
                console.log(s.feature.getGeometry()?.getType())
                let type = 'POLYGON'

                switch (s.feature.getGeometry()?.getType()) {
                    case 'LineString':
                        type = 'LINE'
                        break
                    case 'Polygon':
                        type = 'POLYGON'
                        break
                    default:
                        type = 'POLYGON'
                        break
                }
                const coordinates = (s.feature.getGeometry() as SimpleGeometry).getCoordinates()
                uploadCoordinates(coordinates, type)
            })

            map?.current?.addInteraction(draw.current)
            snap.current = new Snap({ source: drawingLayerSource.current })
            map?.current?.addInteraction(snap.current)
        }
    }

    // Function to save features to localStorage
    const saveFeatures = () => {
        const features = drawingLayer.current?.getSource()?.getFeatures()
        if (features && features.length > 0) {
            const geoJSON = new GeoJSON()
            const featuresJSON = geoJSON.writeFeatures(features)
            localStorage.setItem('savedFeatures', featuresJSON)
        }

        console.log(drawingLayer.current?.getSource()?.getFeatures())
    }

    // Function to load features from localStorage
    const loadFeatures = () => {
        const savedFeatures = localStorage.getItem('savedFeatures')
        if (savedFeatures) {
            const geoJSON = new GeoJSON()
            const features = geoJSON.readFeatures(savedFeatures, {
                featureProjection: 'EPSG:4326', // Ensure the projection is correct
            })
            drawingLayerSource.current?.addFeatures(features)  // Add features to the vector source
            //drawingLayer.current?.getSource()?.changed()
        }
    }

    const handleToolSelect = (tool: Type) => {
        if (currentTool == tool) {
            setCurrentTool(null)
        } else {
            setCurrentTool(tool)
        }
    }

    const zoomToFeature = (feature: Feature) => {
        const geometry = feature.getGeometry()
        const extent = geometry?.getExtent()

        if (map.current && extent) {
            map.current.getView().fit(extent, {
                duration: 300,
                maxZoom: 19,
            })
        }
    }

    const style = new Style({
        geometry: function (feature) {
            const modifyGeometry = feature.get('modifyGeometry');
            return modifyGeometry ? modifyGeometry.geometry : feature.getGeometry();
        },
        fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)',
        }),
        stroke: new Stroke({
            color: '#ffcc33',
            width: 2,
        }),
        image: new CircleStyle({
            radius: 7,
            fill: new Fill({
                color: '#ffcc33',
            }),
        }),
    });

    // tile processing
    const handleImageDrop = useCallback((event: any) => {
        event.preventDefault();
        event.stopPropagation();

        const files = event.dataTransfer.files;
        if (files.length > 0) {
            const file = files[0];
            setFile(file)

            if (file.type.startsWith('image/')) {
                const reader = new FileReader();

                reader.onload = () => {
                    const imageUrl = reader.result as string;
                    const img = new Image();
                    img.src = imageUrl;
                    img.onload = () => {
                        if (map.current) {
                            const view = map.current.getView();
                            const center = view.getCenter() || [0, 0];

                            const width = img.naturalWidth;
                            const height = img.naturalHeight;
                            const resolution = view.getResolution() || 0;

                            const extent = [
                                center[0] - (width * resolution) / 20,
                                center[1] - (height * resolution) / 20,
                                center[0] + (width * resolution) / 20,
                                center[1] + (height * resolution) / 20,
                            ];

                            // Create a polygon feature with the same extent as the image
                            const polygonFeature = new Feature({
                                geometry: fromExtent(extent),
                            });

                            // Add the polygon feature to the drawing layer source
                            overlayLayerSource.current?.addFeature(polygonFeature);

                            // Set up the initial image layer with the extent
                            const imageSource = new ImageStatic({
                                url: imageUrl,
                                imageExtent: extent,
                            });
                            imageLayer.current.setSource(imageSource);

                            //map.current.addLayer(imageLayer.current);

                            // Add interactions for translation and scaling
                            const translate = new Translate({
                                layers: [imageLayer.current],
                                features: new Collection([polygonFeature]),
                            });

                            const defaultStyle = new Modify({ source: overlayLayerSource.current })
                                .getOverlay()
                                .getStyleFunction();

                            const modify = new Modify({
                                insertVertexCondition: never,
                                source: overlayLayerSource.current,
                                condition: function (event) {
                                    return primaryAction(event) && !platformModifierKeyOnly(event);
                                },
                                deleteCondition: never,
                                features: new Collection([polygonFeature]),
                                style: function (feature) {
                                    feature.get('features').forEach(function (modifyFeature: Feature) {
                                        const modifyGeometry = modifyFeature.get('modifyGeometry')
                                        if (modifyGeometry) {
                                            const point = (feature.getGeometry() as Point).getCoordinates()
                                            let modifyPoint = modifyGeometry.point
                                            if (!modifyPoint) {
                                                // save the initial geometry and vertex position
                                                modifyPoint = point;
                                                modifyGeometry.point = modifyPoint;
                                                modifyGeometry.geometry0 = modifyGeometry.geometry;
                                                // get anchor and minimum radius of vertices to be used
                                                const result = calculateCenter(modifyGeometry.geometry0);
                                                modifyGeometry.center = result.center;
                                                modifyGeometry.minRadius = result.minRadius;
                                            }
                                            const center = modifyGeometry.center;
                                            const minRadius = modifyGeometry.minRadius;
                                            let dx, dy;
                                            dx = modifyPoint[0] - center[0];
                                            dy = modifyPoint[1] - center[1];
                                            const initialRadius = Math.sqrt(dx * dx + dy * dy);
                                            if (initialRadius > minRadius) {
                                                const initialAngle = Math.atan2(dy, dx);
                                                dx = point[0] - center[0];
                                                dy = point[1] - center[1];
                                                const currentRadius = Math.sqrt(dx * dx + dy * dy);
                                                if (currentRadius > 0) {
                                                    const currentAngle = Math.atan2(dy, dx);
                                                    const geometry = modifyGeometry.geometry0.clone();
                                                    geometry.scale(currentRadius / initialRadius, undefined, center);
                                                    geometry.rotate(currentAngle - initialAngle, center);
                                                    modifyGeometry.geometry = geometry;
                                                }
                                            }
                                        }
                                    })
                                    const res = map?.current?.getView()?.getResolution()
                                    if (typeof res === 'number' && feature && defaultStyle) {
                                        return defaultStyle(feature, res)
                                    }
                                }
                            });

                            // Function to update the image layer with a new source when extent changes
                            const updateImageSource = () => {
                                const newExtent = polygonFeature.getGeometry()?.getExtent();

                                const bottomLeft = polygonFeature.getGeometry()?.getCoordinates()[0][0]
                                const topLeft = polygonFeature.getGeometry()?.getCoordinates()[0][1]
                                const topRight = polygonFeature.getGeometry()?.getCoordinates()[0][2]
                                const bottomRight = polygonFeature.getGeometry()?.getCoordinates()[0][3]

                                setPolygonExtent(newExtent)
                                setBottomLeft(bottomLeft)
                                setTopLeft(topLeft)
                                setTopRight(topRight)
                                setBottomRight(bottomRight)

                                if (newExtent && bottomLeft && bottomRight && topRight && topLeft) {
                                    const originalExtent = calculateExtent(bottomLeft, topLeft, topRight, bottomRight)

                                    const worldExtent = get('EPSG:3857')?.getExtent() as Extent
                                    const zoomLevel = Number(map.current?.getView().getZoom()?.toFixed(0))
                                    const { tileX: blX, tileY: blY } = getGridCellPosition(bottomLeft[0], bottomLeft[1], worldExtent, zoomLevel)
                                    const { tileX: tlX, tileY: tlY } = getGridCellPosition(topLeft[0], topLeft[1], worldExtent, zoomLevel)
                                    const { tileX: trX, tileY: trY } = getGridCellPosition(topRight[0], topRight[1], worldExtent, zoomLevel)
                                    const { tileX: brX, tileY: brY } = getGridCellPosition(bottomRight[0], topRight[1], worldExtent, zoomLevel)
                                    const minX = Math.min(blX, tlX, trX, brX)
                                    const maxX = Math.max(blX, tlX, trX, brX)
                                    const minY = Math.min(blY, tlY, trY, brY)
                                    const maxY = Math.max(blY, tlY, trY, brY)

                                    const mapWidth = Math.abs(worldExtent[0] - worldExtent[2])
                                    const mapHeight = Math.abs(worldExtent[1] - worldExtent[3])

                                    const tilesH = Math.sqrt(Math.pow(4, zoomLevel))
                                    const tileWidth = mapWidth / (Math.sqrt(Math.pow(4, zoomLevel)))
                                    const tileHeight = mapHeight / (Math.sqrt(Math.pow(4, zoomLevel)))

                                    const minPosX = minX - (tilesH / 2)
                                    const maxPosX = maxX - (tilesH / 2) + 1
                                    const minPosY = -(minY - (tilesH / 2))
                                    const maxPosY = -(maxY - (tilesH / 2) + 1)
                                    console.log(`tileWidth: ${tileWidth} minPosX: ${minPosX} maxPosX: ${maxPosX} minPosY: ${minPosY} maxPosY: ${maxPosY}`)

                                    const newMinX = tileWidth * minPosX
                                    const newMaxX = tileWidth * maxPosX
                                    const newMinY = tileHeight * maxPosY
                                    const newMaxY = tileHeight * minPosY

                                    console.log('Tile slippy bounds: ', minX, maxX, minY, maxY)
                                    console.log('Tile bounds: ', newMinX, newMaxX, newMinY, newMaxY)

                                    const angleDegrees = calculateRotationAngle(bottomLeft, bottomRight) * 180 / Math.PI

                                    const paddingLeft = Math.abs(newExtent[0] - newMinX)
                                    const paddingRight = Math.abs(newExtent[2] - newMaxX)
                                    const paddingTop = Math.abs(newExtent[3] - newMaxY)
                                    const paddingBottom = Math.abs(newExtent[1] - newMinY)

                                    const pixelWidth = Math.abs(minX - (maxX + 1)) * 256
                                    //const pixelHeight = Math.abs(minY - (maxY + 1)) * 256

                                    const width = Math.abs(newMinX - newMaxX)
                                    const perPixel = width / pixelWidth

                                    const paddingLeftPixel = paddingLeft / perPixel
                                    const paddingRightPixel = paddingRight / perPixel
                                    const paddingTopPixel = paddingTop / perPixel
                                    const paddingBottomPixel = paddingBottom / perPixel

                                    console.log('Rotation angle degrees: ', angleDegrees)

                                    console.log('Padding top pixel: ', paddingTopPixel)
                                    console.log('Padding left pixel: ', paddingLeftPixel)
                                    console.log('Padding right pixel: ', paddingRightPixel)
                                    console.log('Padding bottom pixel: ', paddingBottomPixel)

                                    console.log('Per pixel: ', width / pixelWidth)

                                    const boundsWidthPixel = Math.abs(newExtent[0] - newExtent[2]) / perPixel
                                    const boundsHeightPixel = Math.abs(newExtent[1] - newExtent[3]) / perPixel
                                    console.log('Bounds width pixel', boundsWidthPixel)
                                    console.log('Bounds height pixel', boundsHeightPixel)

                                    // Result will be sharp rotate(angleDegrees), resize(boundsWidthPixel), extend()

                                    const newImageSource = new ImageStatic({
                                        url: imageUrl,
                                        imageExtent: originalExtent,
                                        projection: rotateProjection('EPSG:3857', calculateRotationAngle(bottomLeft, bottomRight), originalExtent)
                                    });
                                    imageLayer.current.setSource(newImageSource);
                                }
                            };

                            translate.on('translateend', updateImageSource);
                            //modify.on('modifyend', updateImageSource);

                            modify.on('modifystart', function (event) {
                                event.features.forEach(function (feature) {
                                    feature.set(
                                        'modifyGeometry',
                                        { geometry: feature.getGeometry()?.clone() },
                                        true,
                                    );
                                });
                            });

                            modify.on('modifyend', function (event) {
                                event.features.forEach(function (feature) {
                                    const modifyGeometry = feature.get('modifyGeometry');
                                    if (modifyGeometry) {
                                        feature.setGeometry(modifyGeometry.geometry);
                                        feature.unset('modifyGeometry', true);
                                    }
                                })
                                updateImageSource()
                            })

                            map.current.addInteraction(translate);
                            map.current.addInteraction(modify);
                        }
                    };
                };
                reader.readAsDataURL(file);
            }
        }
    }, [])

    function regionsInit() {
        map.current?.on('click', function (e) {
            if (selectedRegion.current !== null) {
                selectedRegion.current = null
            }

            if (map.current) {
                map.current.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
                    if (layer === regionsLayer.current) {
                        selectedRegion.current = feature as Feature
                        // Zoom to the selected feature
                        zoomToFeature(selectedRegion.current)

                        return true
                    } else return false
                });
            }
        })

        // Show current selected region
        map.current?.on('pointermove', function (e) {
            if (selectedRegion.current !== null) {
                selectedRegion.current.setStyle(undefined)
                selectedRegion.current = null
            }

            if (map.current) {
                map.current.forEachFeatureAtPixel(e.pixel, function (feature, layer) {
                    if (layer === regionsLayer.current) {
                        selectedRegion.current = feature as Feature
                        selectedRegion.current.setStyle(selectStyle)

                        if (feature.get('district')) {
                            setStatusText(feature.get('district'))
                        }

                        return true
                    } else return false
                })
            }
        })

        // Hide regions layer when fully visible
        map.current?.on('moveend', function () {
            const viewExtent = map.current?.getView().calculateExtent(map.current.getSize())
            const features = regionsLayer.current.getSource()?.getFeatures()

            let isViewCovered = false

            features?.forEach((feature: Feature) => {
                const featureExtent = feature?.getGeometry()?.getExtent()
                if (viewExtent && featureExtent) {
                    if (containsExtent(featureExtent, viewExtent)) {
                        isViewCovered = true
                    }
                }
            })

            regionsLayer.current.setVisible(!isViewCovered)
        })
    }

    useEffect(() => {
        drawingLayer.current = new VectorLayer({
            source: drawingLayerSource.current,
            style: drawingLayerStyle
        })

        overlayLayer.current = new VectorLayer({
            source: overlayLayerSource.current,
            style: function (feature) {
                const styles = [style]
                const modifyGeometry = feature.get('modifyGeometry')
                const geometry = modifyGeometry ? modifyGeometry.geometry : feature.getGeometry()
                const result = calculateCenter(geometry)
                const center = result.center
                if (center) {
                    styles.push(
                        new Style({
                            geometry: new Point(center),
                            image: new CircleStyle({
                                radius: 4,
                                fill: new Fill({
                                    color: '#ff3333'
                                })
                            })
                        })
                    )
                    const coordinates = result.coordinates
                    if (coordinates) {
                        const minRadius = result.minRadius
                        const sqDistances = result.sqDistances
                        const rsq = minRadius * minRadius
                        if (Array.isArray(sqDistances)) {
                            const points = coordinates.filter(function (_coordinate, index) {
                                return sqDistances[index] > rsq
                            })
                            styles.push(
                                new Style({
                                    geometry: new MultiPoint(points),
                                    image: new CircleStyle({
                                        radius: 4,
                                        fill: new Fill({
                                            color: '#33cc33'
                                        })
                                    })
                                })
                            )
                        }
                    }
                }
                return styles
            },
        })

        nodeLayer.current = new VectorLayer({
            source: nodeLayerSource.current,
            style: drawingLayerStyle
        })

        map.current = new Map({
            controls: [],
            layers: [baseLayer.current, satLayer.current, regionsLayer.current, citiesLayer.current, linesLayer.current, figuresLayer.current, drawingLayer.current, imageLayer.current, overlayLayer.current, nodeLayer.current],
            target: mapElement.current as HTMLDivElement,
            view: new View({
                center: transform([129.7659541, 62.009504], 'EPSG:4326', 'EPSG:3857'),//center: fromLonLat([130.401113, 67.797368]),
                zoom: 17,
                maxZoom: 21,
                //extent: mapExtent,
            }),
        })

        map.current.on('pointermove', function (e: MapBrowserEvent<any>) {
            setCurrentCoordinate(e.coordinate)
            const currentExtent = get('EPSG:3857')?.getExtent() as Extent

            const { tileX, tileY } = getGridCellPosition(e.coordinate[0], e.coordinate[1], currentExtent, Number(map.current?.getView().getZoom()?.toFixed(0)))
            setCurrentZ(Number(map.current?.getView().getZoom()?.toFixed(0)))
            setCurrentX(tileX)
            setCurrentY(tileY)
        })

        map.current.on('click', function (e: MapBrowserEvent<any>) {
            const pixel = map.current?.getEventPixel(e.originalEvent)

            map.current?.forEachFeatureAtPixel(pixel, function (feature) {
                setCurrentObjectId(feature.get('object_id'))
            })
        })

        const modify = new Modify({ source: drawingLayerSource.current })
        map.current.addInteraction(modify)

        map.current.addInteraction(selectFeature.current)

        selectFeature.current.on('select', (e) => {
            const selectedFeatures = e.selected

            if (selectedFeatures.length > 0) {
                selectedFeatures.forEach((feature) => {
                    drawingLayerSource.current?.removeFeature(feature)
                })
            }
        })

        loadFeatures()

        regionsInit()

        if (mapElement.current) {
            mapElement.current.addEventListener('dragover', (e) => {
                e.preventDefault()
            })

            mapElement.current.addEventListener('drop', handleImageDrop)
        }

        return () => {
            map?.current?.setTarget(undefined)

            if (mapElement.current) {
                mapElement.current.removeEventListener('drop', handleImageDrop)
            }
        }
    }, [])

    useEffect(() => {
        if (currentTool) {
            if (draw.current) map?.current?.removeInteraction(draw.current)
            if (snap.current) map?.current?.removeInteraction(snap.current)
            addInteractions()
        } else {
            if (draw.current) map?.current?.removeInteraction(draw.current)
            if (snap.current) map?.current?.removeInteraction(snap.current)
        }
    }, [currentTool])

    const uploadCoordinates = async (coordinates: any, type: any) => {
        try {
            const response = await fetch(`${import.meta.env.VITE_API_EMS_URL}/nodes`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ coordinates, object_id: 1, type: type }) // Replace with actual object_id
            });

            if (response.ok) {
                const data = await response.json();
                console.log('Node created:', data);
            } else {
                console.error('Failed to upload coordinates');
            }
        } catch (error) {
            console.error('Error:', error);
        }
    };

    const [satelliteOpacity, setSatelliteOpacity] = useState<number>(1)

    const [statusText, setStatusText] = useState('')

    // Visibility setting
    useEffect(() => {
        satLayer.current?.setOpacity(satelliteOpacity)

        if (satelliteOpacity == 0) {
            baseLayer.current?.setVisible(true)
            satLayer.current?.setVisible(false)
        } if (satelliteOpacity == 1) {
            baseLayer.current?.setVisible(false)
            satLayer.current?.setVisible(true)
        } else if (satelliteOpacity > 0 && satelliteOpacity < 1) {
            baseLayer.current?.setVisible(true)
            satLayer.current?.setVisible(true)
        }
    }, [satelliteOpacity])

    // Satellite tiles setting
    useEffect(() => {
        satLayer.current?.setSource(satMapsProvider == 'google' ? gMapsSatSource.current : satMapsProvider == 'yandex' ? yMapsSatSource.current : satMapsProvider == 'custom' ? customMapSource.current : gMapsSatSource.current)
        satLayer.current?.getSource()?.refresh()
    }, [satMapsProvider])

    const submitOverlay = async () => {
        if (file && polygonExtent && bottomLeft && topLeft && topRight && bottomRight) {
            const formData = new FormData()
            formData.append('file', file)
            formData.append('extentMinX', polygonExtent[0].toString())
            formData.append('extentMinY', polygonExtent[1].toString())
            formData.append('extentMaxX', polygonExtent[2].toString())
            formData.append('extentMaxY', polygonExtent[3].toString())
            formData.append('blX', bottomLeft[0].toString())
            formData.append('blY', bottomLeft[1].toString())
            formData.append('tlX', topLeft[0].toString())
            formData.append('tlY', topLeft[1].toString())
            formData.append('trX', topRight[0].toString())
            formData.append('trY', topRight[1].toString())
            formData.append('brX', bottomRight[0].toString())
            formData.append('brY', bottomRight[1].toString())

            await fetch(`${import.meta.env.VITE_API_EMS_URL}/upload`, { method: 'POST', body: formData })
        }
    }

    const { colorScheme } = useMantineColorScheme();

    const mapControlsStyle: MantineStyleProp = {
        borderRadius: '4px',
        position: 'absolute',
        zIndex: '1',
        // backgroundColor: (theme) =>
        //     theme.palette.mode === 'light'
        //         ? '#FFFFFFAA'
        //         : '#000000AA',
        backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA',
        backdropFilter: 'blur(8px)'
    }

    const { data: nodes } = useSWR('/nodes/all', () => fetcher('/nodes/all', BASE_URL.ems), { revalidateOnFocus: false })

    useEffect(() => {
        // Draw features based on database data
        if (Array.isArray(nodes)) {
            nodes.map(node => {
                if (node.shape_type === 'LINE') {
                    const coordinates: Coordinate[] = []
                    if (Array.isArray(node.shape)) {
                        node.shape.map((point: any) => {
                            const coordinate = [point.x as number, point.y as number] as Coordinate
                            coordinates.push(coordinate)
                        })
                    }
                    //console.log(coordinates)
                    nodeLayerSource.current.addFeature(new Feature({ geometry: new LineString(coordinates) }))
                }
            })
        }
    }, [nodes])

    function styleFunction(feature: Feature) {
        return [
            new Style({
                fill: new Fill({
                    color: 'rgba(255,255,255,0.4)'
                }),
                stroke: new Stroke({
                    color: '#3399CC',
                    width: 1.25
                }),
                text: new Text({
                    font: '12px Calibri,sans-serif',
                    fill: new Fill({ color: '#000' }),
                    stroke: new Stroke({
                        color: '#fff', width: 2
                    }),
                    // get the text from the feature - `this` is ol.Feature
                    // and show only under certain resolution
                    text: feature.get('object_id')
                })
            })
        ];
    }

    function fourthStyleFunction(feature: Feature) {
        return [
            new Style({
                fill: new Fill({
                    color: 'rgba(255,255,255,0.4)'
                }),
                stroke: new Stroke({
                    color: '#3399CC',
                    width: 1.25
                }),
                text: new Text({
                    font: '12px Calibri,sans-serif',
                    fill: new Fill({ color: '#000' }),
                    stroke: new Stroke({
                        color: '#fff', width: 2
                    }),
                    // get the text from the feature - `this` is ol.Feature
                    // and show only under certain resolution
                    text: `${feature.get('object_id')}\n ${feature.get('angle')}`
                })
            })
        ];
    }

    function thirdStyleFunction(feature: Feature) {
        return [
            new Style({
                fill: new Fill({
                    color: 'rgba(255,255,255,0.4)'
                }),
                stroke: new Stroke({
                    color: '#33ccb3',
                    width: 1.25
                }),
                text: new Text({
                    font: '12px Calibri,sans-serif',
                    fill: new Fill({ color: '#000' }),
                    stroke: new Stroke({
                        color: '#fff', width: 2
                    }),
                    // get the text from the feature - `this` is ol.Feature
                    // and show only under certain resolution
                    text: feature.get('object_id')
                })
            })
        ];
    }

    function firstStyleFunction(feature: Feature) {
        return [
            new Style({
                fill: new Fill({
                    color: 'rgba(255,255,255,0.4)'
                }),
                stroke: new Stroke({
                    color: 'red',
                    width: 1.25
                }),
                text: new Text({
                    font: '12px Calibri,sans-serif',
                    fill: new Fill({ color: '#000' }),
                    stroke: new Stroke({
                        color: '#fff', width: 2
                    }),
                    // get the text from the feature - `this` is ol.Feature
                    // and show only under certain resolution
                    text: feature.get('object_id')
                })
            })
        ];
    }

    const [currentObjectId, setCurrentObjectId] = useState<string | null>(null)

    const [selectedCity, setSelectedCity] = useState<number | null>(null)
    const [selectedYear, setSelectedYear] = useState<number | null>(2023)
    const [citiesPage, setCitiesPage] = useState<number>(0)

    const [searchCity, setSearchCity] = useState<string | undefined>("")

    const { data: currentObjectData } = useSWR(
        currentObjectId ? `/general/objects/${currentObjectId}` : null,
        (url) => fetcher(url, BASE_URL.ems),
        {
            revalidateOnFocus: false
        }
    )

    const { data: valuesData } = useSWR(
        currentObjectId ? `/general/values/all?object_id=${currentObjectId}` : null,
        (url) => fetcher(url, BASE_URL.ems),
        {
            revalidateOnFocus: false
        }
    )

    const { data: citiesData } = useSWR(
        `/general/cities/all?limit=${10}&offset=${citiesPage || 0}${searchCity ? `&search=${searchCity}` : ''}`,
        (url) => fetcher(url, BASE_URL.ems),
        {
            revalidateOnFocus: false
        }
    )

    const { data: figuresData, isValidating: figuresValidating } = useSWR(
        selectedCity && selectedYear ? `/gis/figures/all?city_id=${selectedCity}&year=${selectedYear}&offset=0&limit=${10000}` : null,
        (url) => axios.get(url, {
            baseURL: BASE_URL.ems
        }).then((res) => res.data),
        {
            revalidateOnFocus: false
        }
    )

    const { data: linesData } = useSWR(
        !figuresValidating && selectedCity && selectedYear ? `/gis/lines/all?city_id=${selectedCity}&year=${selectedYear}&offset=0&limit=${10000}` : null,
        (url) => axios.get(url, {
            baseURL: BASE_URL.ems
        }).then((res) => {
            return res.data
        }),
        {
            revalidateOnFocus: false
        }
    )

    useEffect(() => {
        if (Array.isArray(figuresData)) {
            figuresLayer.current.getSource()?.clear()
            if (figuresData.length > 0) {
                const scaling = {
                    w: 10000, // responseData[0].width
                    h: 10000 // responseData[0].width
                }

                figuresData.map((figure: IFigure) => {
                    if (figure.figure_type_id == 1) {
                        const width = figure.width * scaling.w
                        const height = figure.height * scaling.h

                        const left = figure.left * scaling.w
                        const top = figure.top * scaling.h

                        const centerX = mapCenter[0] + left + (width / 2)
                        const centerY = mapCenter[1] - top - (height / 2)

                        const radius = width / 2;
                        const circleGeom = new Circle([centerX, centerY], radius)

                        const ellipseGeom = fromCircle(circleGeom, 64)
                        ellipseGeom.scale(1, height / width)

                        const feature = new Feature(ellipseGeom)

                        feature.setStyle(firstStyleFunction(feature))
                        feature.set('object_id', figure.object_id)
                        figuresLayer.current?.getSource()?.addFeature(feature)
                    }

                    if (figure.figure_type_id == 3) {
                        const x = figure.left * scaling.w
                        const y = figure.top * scaling.h

                        const center = [mapCenter[0] + x, mapCenter[1] - y]

                        const coords = figure.points?.split(' ').map(pair => {
                            const [x, y] = pair.split(';').map(Number)
                            return [
                                center[0] + (x * scaling.w),
                                center[1] - (y * scaling.h)
                            ]
                        })

                        if (coords) {
                            const polygon = new Polygon([coords])

                            const feature = new Feature({
                                geometry: polygon
                            })

                            feature.set('object_id', figure.object_id)
                            feature.setStyle(thirdStyleFunction(feature))
                            figuresLayer.current?.getSource()?.addFeature(feature)
                        }
                    }

                    if (figure.figure_type_id == 4) {
                        const width = figure.width * scaling.w
                        const height = figure.height * scaling.h
                        const left = figure.left * scaling.w
                        const top = figure.top * scaling.h

                        const halfWidth = width / 2
                        const halfHeight = height / 2

                        const center = [mapCenter[0] + left + halfWidth, mapCenter[1] - top - halfHeight]

                        const testCoords = [
                            [center[0] - halfWidth, center[1] - halfHeight],
                            [center[0] - halfWidth, center[1] + halfHeight],
                            [center[0] + halfWidth, center[1] + halfHeight],
                            [center[0] + halfWidth, center[1] - halfHeight],
                            [center[0] - halfWidth, center[1] - halfHeight]
                        ]

                        const geometry1 = new Polygon([testCoords])
                        const anchor1 = center
                        geometry1.rotate(-figure.angle * Math.PI / 180, anchor1)
                        const feature1 = new Feature(geometry1)
                        feature1.set('object_id', figure.object_id)
                        feature1.set('angle', figure.angle)
                        feature1.setStyle(fourthStyleFunction(feature1))
                        figuresLayer.current?.getSource()?.addFeature(feature1)
                    }
                })
            }
        }

        if (Array.isArray(linesData)) {
            linesLayer.current.getSource()?.clear()
            if (linesData.length > 0) {
                const scaling = {
                    w: 10000, // responseData[0].width
                    h: 10000 // responseData[0].width
                }

                linesData.map((line: ILine) => {
                    const x1 = line.x1 * scaling.w
                    const y1 = line.y1 * scaling.h
                    const x2 = line.x2 * scaling.w
                    const y2 = line.y2 * scaling.h

                    const center = [mapCenter[0], mapCenter[1]]

                    const testCoords = [
                        [center[0] + x1, center[1] - y1],
                        [center[0] + x2, center[1] - y2],
                    ]

                    const feature = new Feature(new LineString(testCoords))
                    feature.setStyle(styleFunction(feature))
                    feature.set('object_id', line.object_id)

                    linesLayer.current?.getSource()?.addFeature(feature)
                })
            }
        }
    }, [figuresData, linesData, selectedCity, selectedYear])

    return (
        <Box w={'100%'} h='100%' pos={'relative'}>
            <Portal target='#header-portal'>
                <Flex gap={'sm'} direction={'row'}>
                    <form>
                        <Autocomplete
                            placeholder="Район"
                            flex={'1'}
                            data={citiesData ? citiesData.map((item: any) => ({ label: item.name, value: item.id.toString() })) : []}
                            //onSelect={(e) => console.log(e.currentTarget.value)}
                            onChange={(value) => setSearchCity(value)}
                            onOptionSubmit={(value) => setSelectedCity(Number(value))}
                            rightSection={
                                searchCity !== '' && (
                                    <CloseButton
                                        size="sm"
                                        onMouseDown={(event) => event.preventDefault()}
                                        onClick={() => {
                                            setSearchCity('')
                                            setSelectedCity(null)
                                        }}
                                        aria-label="Clear value"
                                    />
                                )
                            }
                            value={searchCity}
                        />
                    </form>

                    <MantineSelect
                        data={[
                            { label: '2018', value: '2018' },
                            { label: '2019', value: '2019' },
                            { label: '2020', value: '2020' },
                            { label: '2021', value: '2021' },
                            { label: '2022', value: '2022' },
                            { label: '2023', value: '2023' },
                            { label: '2024', value: '2024' },
                        ]}
                        onChange={(e) => {
                            setSelectedYear(Number(e))
                        }}
                        defaultValue={selectedYear?.toString()}
                        //variant="unstyled"
                        allowDeselect={false}
                    />

                    <ActionIcon
                        size='lg'
                        variant='transparent'
                        title='Настройки ИКС'
                    >
                        <IconSettings style={{ width: rem(20), height: rem(20) }} />
                    </ActionIcon>
                </Flex>
            </Portal>

            <Flex w={'100%'} h={'100%'} pos={'absolute'}>
                <ActionIcon.Group orientation='vertical' pos='absolute' top='8px' right='8px' style={{ zIndex: 1, backdropFilter: 'blur(8px)', backgroundColor: colorScheme === 'light' ? '#FFFFFFAA' : '#000000AA', borderRadius: '4px' }}>
                    <ActionIcon size='lg' variant='transparent' onClick={() => {
                        fetch(`${import.meta.env.VITE_API_EMS_URL}/hello`, { method: 'GET' }).then(res => console.log(res))
                    }}>
                        <IconApi />
                    </ActionIcon>

                    <ActionIcon size='lg' variant='transparent' onClick={() => {
                        saveFeatures()
                    }}>
                        <IconExclamationCircle />
                    </ActionIcon>

                    <ActionIcon size='lg' variant='transparent' onClick={() => {
                        draw.current?.removeLastPoint()
                    }}>
                        <IconArrowBackUp />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant={currentTool === 'Point' ? 'filled' : 'transparent'}
                        onClick={() => {
                            handleToolSelect('Point')
                        }}>
                        <IconPoint />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant={currentTool === 'LineString' ? 'filled' : 'transparent'}
                        onClick={() => {
                            handleToolSelect('LineString')
                        }}>
                        <IconLine />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant={currentTool === 'Polygon' ? 'filled' : 'transparent'}
                        onClick={() => {
                            handleToolSelect('Polygon')
                        }}>
                        <IconPolygon />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant={currentTool === 'Circle' ? 'filled' : 'transparent'}
                        onClick={() => {
                            handleToolSelect('Circle')
                        }}>
                        <IconCircle />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant='transparent'
                        onClick={() => map?.current?.addInteraction(new Translate())}
                    >
                        <IconArrowsMove />
                    </ActionIcon>

                    <ActionIcon
                        size='lg'
                        variant='transparent'
                    >
                        <IconRuler />
                    </ActionIcon>
                </ActionIcon.Group>

                <Flex direction='column' mah={'86%'} pl='sm' style={{
                    ...mapControlsStyle,
                    maxWidth: '300px',
                    width: '100%',
                    top: '8px',
                    left: '8px',
                }}>
                    <ScrollAreaAutosize offsetScrollbars>
                        <Flex direction='row'>
                            <ActionIcon
                                size='lg'
                                variant='transparent'
                                onClick={() => submitOverlay()}
                            >
                                <IconUpload style={{ width: rem(20), height: rem(20) }} />
                            </ActionIcon>

                            <ActionIcon
                                size='lg'
                                variant='transparent'
                                title='Добавить подложку'
                            >
                                <IconPlus style={{ width: rem(20), height: rem(20) }} />
                            </ActionIcon>
                        </Flex>

                        <Flex align='center' direction='row' p='sm' gap='sm'>
                            <Slider w='100%' min={0} max={1} step={0.001} value={satelliteOpacity} defaultValue={satelliteOpacity} onChange={(value) => setSatelliteOpacity(Array.isArray(value) ? value[0] : value)} />

                            <MantineSelect variant='filled' value={satMapsProvider} data={[{ label: 'Google', value: 'google' }, { label: 'Yandex', value: 'yandex' }, { label: 'Custom', value: 'custom' }]} onChange={(value) => setSatMapsProvider(value as SatelliteMapsProvider)} />
                        </Flex>

                        <Accordion variant='filled' style={{ backgroundColor: 'transparent' }} defaultValue='Объекты'>
                            <Accordion.Item key={'objects'} value={'Объекты'}>
                                <Accordion.Control icon={<IconTable />}>{'Объекты'}</Accordion.Control>
                                <Accordion.Panel>
                                    
                                </Accordion.Panel>
                            </Accordion.Item>

                            <Accordion.Item key={'current_object'} value='current_object'>
                                <Accordion.Control icon={<IconTable />}>
                                    {'Текущий объект'}
                                </Accordion.Control>
                                <Accordion.Panel>
                                    <ObjectData {...currentObjectData as IObjectData} />
                                </Accordion.Panel>
                            </Accordion.Item>

                            {valuesData && <Accordion.Item key={'parameters'} value={'Параметры объекта'}>
                                <Accordion.Control icon={<IconTable />}>{'Параметры объекта'}</Accordion.Control>
                                <Accordion.Panel>
                                    <Flex gap={'sm'} direction={'column'}>
                                        {Array.isArray(valuesData) && valuesData.map((param: IObjectParam) => {
                                            return (
                                                <ObjectParameter key={param.id_param} value={param.value} id_param={param.id_param} />
                                            )
                                        })}
                                    </Flex>
                                </Accordion.Panel>
                            </Accordion.Item>}
                        </Accordion>
                    </ScrollAreaAutosize>

                </Flex>

                <Flex gap='sm' p={'4px'} miw={'100%'} fz={'xs'} pos='absolute' bottom='0px' left='0px' style={{ ...mapControlsStyle, borderRadius: 0 }}>
                    <MantineText fz='xs' w={rem(130)}>
                        x: {currentCoordinate?.[0]}
                    </MantineText>

                    <MantineText fz='xs' w={rem(130)}>
                        y: {currentCoordinate?.[1]}
                    </MantineText>

                    <Divider orientation='vertical' />

                    <MantineText fz='xs'>
                        Z={currentZ}
                    </MantineText>

                    <MantineText fz='xs'>
                        X={currentX}
                    </MantineText>

                    <MantineText fz='xs'>
                        Y={currentY}
                    </MantineText>

                    <MantineText fz='xs' ml='auto'>
                        {statusText}
                    </MantineText>
                </Flex>
            </Flex>


            <div
                id="map-container"
                ref={mapElement}
                style={{
                    width: '100%',
                    height: '100%',
                    maxHeight: '100%',
                    position: 'fixed',
                    flexGrow: 1
                }}
            >
            </div>
        </Box >
    );
};

export default MapComponent
