import { Coordinate, distance, rotate } from "ol/coordinate";
import { Extent, getCenter, getHeight, getWidth } from "ol/extent";
import Feature from "ol/Feature";
import GeoJSON from "ol/format/GeoJSON";
import { Circle, Geometry, LineString, Point, Polygon, SimpleGeometry } from "ol/geom";
import VectorLayer from "ol/layer/Vector";
import { addCoordinateTransforms, addProjection, fromLonLat, get, getTransform, Projection, ProjectionLike, transform } from "ol/proj";
import proj4 from "proj4";
import { Type } from "ol/geom/Geometry";
import { Draw, Modify, Snap, Translate } from "ol/interaction";
import { never, noModifierKeys, platformModifierKeyOnly, primaryAction } from "ol/events/condition";
import { IGeometryType } from "../../interfaces/map";
import { uploadCoordinates } from "../../actions/map";
import { ImageStatic } from "ol/source";
import { ICitySettings, IFigure, ILine } from "../../interfaces/gis";
import { fromCircle, fromExtent } from "ol/geom/Polygon";
import { measureStyleFunction, modifyStyle } from "./Measure/MeasureStyles";
import { addChanges, getChanges, getCurrentTool, getDraw, getDrawingLayerSource, getImageLayer, getMap, getMeasureClearPrevious, getMeasureDraw, getMeasureModify, getMeasureSource, getMeasureType, getOverlayLayerSource, getSelectedFeatures, getSnap, getTipPoint, getTranslate, PrintOrientation, setDraw, setFile, setMeasureDraw, setPolygonExtent, setRectCoords, setSnap, setTranslate, useMapStore } from "../../store/map";
import Collection from "ol/Collection";
import { SketchCoordType } from "ol/interaction/Draw";
import VectorImageLayer from "ol/layer/VectorImage";
import VectorSource from "ol/source/Vector";
import Map from "ol/Map";
import { Icon, Style } from "ol/style";

export function getCitySettings() {
    return {
        city_id: 0,
        image_width: 8500,
        image_height: 12544,
        scale: 9000,
        offset_x: 0,//14442665.697619518,
        offset_y: 0,//8884520.63524492,
        rotation: 0
    } as ICitySettings
}

export const loadRegionCapitals = (regionsData: any, capitalsLayer: VectorLayer<VectorSource<any>, any>) => {
    if (Array.isArray(regionsData)) {
        regionsData.map((region) => {
            const lon = region.capital_longitude
            const lat = region.capital_latitude

            if (lon != null && lat != null) {
                const marker = new Feature({
                    geometry: new Point(fromLonLat([lon, lat], 'EPSG:3857')),
                    regionId: region.id, // optional, useful for interactivity
                });

                marker.setStyle(new Style({
                    image: new Icon({
                        src: '/map_pin_icon.svg',
                        scale: 0.2
                    })
                }))

                if (region.capital) {
                    marker.setProperties({
                        entity_id: region.capital
                    })
                }

                // Optional styling for capital marker

                capitalsLayer.getSource()?.addFeature(marker);
            }
        })
    }
}

export const loadRegionBounds = (regionBoundsData: any, regionsLayer: VectorImageLayer<Feature<Geometry>, VectorSource<Feature<Geometry>>>) => {
    if (Array.isArray(regionBoundsData)) {
        regionBoundsData.map(bound => {
            const geoJson = new GeoJSON() //new GeoJSON({ featureProjection: 'EPSG:3857' })
            const geometry = geoJson.readGeometry(bound) as Geometry

            const feature = new Feature(geometry)
            feature.setProperties(bound.properties)

            regionsLayer.getSource()?.addFeature(feature)
        })
    }
}

export const addFigures = (
    center: Coordinate,
    figuresData: any,
    figuresLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>>,
    settings: ICitySettings,
    map: Map
) => {
    if (Array.isArray(figuresData)) {
        figuresLayer.getSource()?.clear()
        if (figuresData.length > 0) {
            const geoJsonObject = {
                type: "FeatureCollection",
                features: figuresData.map((figure: IFigure) => {
                    if (figure.modified) {
                        return {
                            ...JSON.parse(figure.modified), properties: {
                                year: figure.year,
                                figure_type_id: figure.figure_type_id,
                                type: figure.type,
                                object_id: figure.object_id,
                                planning: figure.planning
                            }
                        }
                    } else {
                        return processFigure(
                            figure,
                            settings.scale,
                            [center[0], center[1]]
                        )
                    }
                }),
            }

            const features = new GeoJSON().readFeatures(geoJsonObject)

            figuresLayer.getSource()?.addFeatures(features)

            if (map) {
                const extent = figuresLayer.getSource()?.getExtent()

                if (extent) {
                    map.getView().fit(fromExtent(extent), { duration: 500 })
                }
            }
        }
    }
}

export const addLines = (
    center: Coordinate,
    linesData: any,
    linesLayer: VectorLayer<VectorSource<Feature<Geometry>>, Feature<Geometry>>,
    settings: ICitySettings
) => {
    if (Array.isArray(linesData)) {
        linesLayer.getSource()?.clear()
        if (linesData.length > 0) {
            const geoJsonObject = {
                type: "FeatureCollection",
                features: linesData.map((line: ILine) => {
                    if (line.modified) {
                        return {
                            ...JSON.parse(line.modified), properties: {
                                year: line.year,
                                type: line.type,
                                geometry_type: 'line',
                                object_id: line.object_id,
                                planning: line.planning,
                            }
                        }
                    } else {
                        return processLine(line, settings.scale, [center[0], center[1]])
                    }

                }),
            }

            const features = new GeoJSON().readFeatures(geoJsonObject)

            linesLayer.getSource()?.addFeatures(features)
        }
    }
}

export const calculateAngle = (coords: [number, number][]) => {
    const [start, end] = coords;
    const dx = end[0] - start[0];
    const dy = end[1] - start[1];
    return Math.atan2(dy, dx); // Angle in radians
}

export function processLineFeatureToLine(
    feature: Feature,
    scaling: number,
    mapCenter: Coordinate
): Partial<ILine> {
    const geometry = feature.getGeometry() as LineString;

    // Get the line coordinates
    const coordinates = geometry.getCoordinates();

    if (coordinates.length < 2) {
        throw new Error('Line must have at least 2 points');
    }

    // Get start and end points
    const startPoint = coordinates[0];
    const endPoint = coordinates[coordinates.length - 1];

    // Convert from map coordinates to database coordinates
    const x1 = (startPoint[0] - mapCenter[0]) / scaling;
    const y1 = (mapCenter[1] - startPoint[1]) / scaling;

    const x2 = (endPoint[0] - mapCenter[0]) / scaling;
    const y2 = (mapCenter[1] - endPoint[1]) / scaling;

    return {
        x1: parseFloat(x1.toFixed(6)),
        y1: parseFloat(y1.toFixed(6)),
        x2: parseFloat(x2.toFixed(6)),
        y2: parseFloat(y2.toFixed(6)),
    };
}

export function processLine(
    line: ILine,
    scaling: number,
    mapCenter: Coordinate
) {
    const x1 = line.x1 * scaling
    const y1 = line.y1 * scaling
    const x2 = line.x2 * scaling
    const y2 = line.y2 * scaling

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

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

    const geometry = new LineString(testCoords)

    return {
        type: "Feature",
        geometry: new GeoJSON().writeGeometryObject(geometry),
        properties: {
            year: line.year,
            type: line.type,
            geometry_type: 'line',
            object_id: line.object_id,
            planning: line.planning,
            rotation: calculateAngle(testCoords)
        }
    }
}

export function processFeatureToFigure(
    feature: Feature,
    figureTypeId: number,
    scaling: number,
    mapCenter: Coordinate,
): Partial<IFigure> {
    const geometry = feature.getGeometry()
    const properties = feature.getProperties();

    switch (figureTypeId) {
        case 1: // Ellipse
            return processEllipseFeature(geometry as Polygon, scaling, mapCenter);

        case 3: // Custom polygon
            return processPolygonFeature(geometry as Polygon, scaling, mapCenter);

        case 4: // Rotated rectangle
            return processRectangleFeature(geometry as Polygon, scaling, mapCenter, properties.angle);

        default:
            throw new Error(`Unsupported figure type: ${figureTypeId}`);
    }
}

function processEllipseFeature(geometry: Polygon, scaling: number, mapCenter: Coordinate): Partial<IFigure> {
    // Get the extent of the ellipse
    const extent = geometry.getExtent();

    // Calculate dimensions
    const width = (extent[2] - extent[0]) / scaling;
    const height = (extent[3] - extent[1]) / scaling;

    // Calculate center in map coordinates
    const centerX = (extent[0] + extent[2]) / 2;
    const centerY = (extent[1] + extent[3]) / 2;

    // Convert to database coordinates
    const left = (centerX - mapCenter[0]) / scaling;
    const top = (mapCenter[1] - centerY) / scaling;

    return {
        figure_type_id: 1,
        width: parseFloat(width.toFixed(6)),
        height: parseFloat(height.toFixed(6)),
        left: parseFloat(left.toFixed(6)),
        top: parseFloat(top.toFixed(6))
    };
}

function processPolygonFeature(
    geometry: Polygon,
    scaling: number,
    mapCenter: Coordinate,
): Partial<IFigure> {
    // Get polygon coordinates (first ring only)
    const coordinates = geometry.getCoordinates()[0];

    // Calculate the bounding box to get left/top
    let minX = Infinity, maxX = -Infinity;
    let minY = Infinity, maxY = -Infinity;

    coordinates.forEach(coord => {
        minX = Math.min(minX, coord[0]);
        maxX = Math.max(maxX, coord[0]);
        minY = Math.min(minY, coord[1]);
        maxY = Math.max(maxY, coord[1]);
    });

    // Calculate center of the polygon
    const centerX = (minX + maxX) / 2;
    const centerY = (minY + maxY) / 2;

    // Convert center to database coordinates for left/top
    const left = (centerX - mapCenter[0]) / scaling;
    const top = (mapCenter[1] - centerY) / scaling;

    // Now convert all points relative to this center
    const pointsArray = coordinates.map(coord => {
        // Points are relative to (left, top), which is our calculated center
        const relativeX = (coord[0] - mapCenter[0]) / scaling - left;
        const relativeY = (mapCenter[1] - coord[1]) / scaling - top;
        return `${relativeX.toFixed(6)};${relativeY.toFixed(6)}`;
    });

    // Join points with space (skip last point if it's duplicate of first)
    const points = pointsArray.slice(0, -1).join(' ');

    return {
        figure_type_id: 3,
        points,
        left: parseFloat(left.toFixed(6)),
        top: parseFloat(top.toFixed(6))
    };
}

function processRectangleFeature(
    geometry: Polygon,
    scaling: number,
    mapCenter: Coordinate,
    angle: number = 0
): Partial<IFigure> {
    // Clone geometry to avoid modifying original
    const workingGeometry = geometry.clone();

    // Get the current center
    const currentCenter = getCenter(workingGeometry.getExtent());

    // If there's rotation, we need to unrotate to get axis-aligned bounds
    if (angle !== 0) {
        workingGeometry.rotate(angle * Math.PI / 180, currentCenter);
    }

    // Get the unrotated extent
    const extent = workingGeometry.getExtent();

    // Calculate dimensions from unrotated geometry
    const width = (extent[2] - extent[0]) / scaling;
    const height = (extent[3] - extent[1]) / scaling;

    // Calculate center of unrotated geometry
    const unrotatedCenter = getCenter(extent);

    // Convert to database coordinates
    const left = (unrotatedCenter[0] - mapCenter[0]) / scaling;
    const top = (mapCenter[1] - unrotatedCenter[1]) / scaling;

    return {
        figure_type_id: 4,
        width: parseFloat(width.toFixed(6)),
        height: parseFloat(height.toFixed(6)),
        left: parseFloat(left.toFixed(6)),
        top: parseFloat(top.toFixed(6)),
        angle: angle // Preserve the angle
    };
}

// Helper function to update a feature after movement
export function updateFeatureCoordinates(
    feature: Feature,
    scaling: number,
    mapCenter: Coordinate
): void {
    const figureTypeId = feature.get('figure_type_id');
    const updatedFigure = processFeatureToFigure(feature, figureTypeId, scaling, mapCenter);

    // You can now send updatedFigure to your backend
    console.log('Updated figure data:', updatedFigure);

    // Example: Update feature properties with database coordinates for reference
    feature.set('db_coordinates', updatedFigure);
}

export function processFigure(
    figure: IFigure,
    scaling: number,
    mapCenter: Coordinate,
) {
    if (figure.figure_type_id == 1) {
        const width = figure.width * scaling
        const height = figure.height * scaling

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

        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)

        return {
            type: "Feature",
            geometry: new GeoJSON().writeGeometryObject(ellipseGeom),
            properties: {
                year: figure.year,
                figure_type_id: figure.figure_type_id,
                type: figure.type,
                object_id: figure.object_id,
                planning: figure.planning
            }
        }
    }

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

        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),
                center[1] - (y * scaling)
            ]
        })

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

            return {
                type: "Feature",
                geometry: new GeoJSON().writeGeometryObject(polygon),
                properties: {
                    year: figure.year,
                    figure_type_id: figure.figure_type_id,
                    type: figure.type,
                    object_id: figure.object_id,
                    planning: figure.planning
                }
            }
        }
    }

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

        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)

        return {
            type: "Feature",
            geometry: new GeoJSON().writeGeometryObject(geometry1),
            properties: {
                year: figure.year,
                figure_type_id: figure.figure_type_id,
                type: figure.type,
                object_id: figure.object_id,
                planning: figure.planning,
                angle: figure.angle
            }
        }
    }
}

export const handleImageDrop = (
    event: React.DragEvent<HTMLDivElement>,
    map_id: string,
) => {
    event.preventDefault()
    event.stopPropagation()

    if (!event.dataTransfer?.files) return

    const files = event.dataTransfer.files
    if (files.length > 0) {
        const file = files[0]
        setFile(map_id, 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 = () => {
                    const map = getMap(map_id)

                    if (map) {
                        const center = map.getView().getCenter() || [0, 0];

                        const width = img.naturalWidth;
                        const height = img.naturalHeight;
                        const resolution = map.getView().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), });

                        const overlayLayerSource = getOverlayLayerSource(map_id)
                        // Add the polygon feature to the drawing layer source
                        overlayLayerSource.addFeature(polygonFeature);

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

                        updateImageSource(map_id, imageUrl, polygonFeature)

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

                        // Add interactions for translation and scaling
                        setTranslate(map_id, new Translate({
                            layers: [imageLayer],
                            features: new Collection([polygonFeature]),
                        }))

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

                        const modify = new Modify({
                            insertVertexCondition: never,
                            source: overlayLayerSource,
                            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?.getView()?.getResolution()
                                if (typeof res === 'number' && feature && defaultStyle) {
                                    return defaultStyle(feature, res)
                                }
                            }
                        });

                        const translate = getTranslate(map_id)

                        if (translate) {
                            translate.on('translateend', () => updateImageSource(map_id, imageUrl, polygonFeature));
                            map.addInteraction(translate);
                        }

                        //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_id, imageUrl, polygonFeature)
                        })

                        map.addInteraction(modify);
                    }
                };
            };
            reader.readAsDataURL(file);
        }
    }
}

// Function to update the image layer with a new source when extent changes
export const updateImageSource = (
    map_id: string,
    imageUrl: string,
    polygonFeature: Feature<Polygon>
) => {
    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]

    setRectCoords(map_id, {
        bl: bottomLeft,
        tl: topLeft,
        tr: topRight,
        br: bottomRight
    })

    setPolygonExtent(map_id, newExtent)

    if (newExtent && bottomLeft && bottomRight && topRight && topLeft) {
        const originalExtent = calculateExtent(bottomLeft, topLeft, topRight, bottomRight)
        const newImageSource = new ImageStatic({
            url: imageUrl,
            imageExtent: originalExtent,
            projection: rotateProjection('EPSG:3857', calculateRotationAngle(bottomLeft, bottomRight), originalExtent)
        })
        getImageLayer(map_id).setSource(newImageSource)
    }
};

export const fixedAspectRatioBox = (
    coordinates: SketchCoordType,
    geometry: SimpleGeometry | undefined,
    orientation?: PrintOrientation
): SimpleGeometry => {
    if (!Array.isArray(coordinates) || coordinates.length < 2) {
        return geometry ?? new Polygon([]);
    }

    const [start, end] = coordinates as Coordinate[];
    const minX = start[0];
    const minY = start[1];
    const maxX = end[0];

    const width = maxX - minX;
    const height = orientation === 'horizontal' ? Math.abs(width / Math.sqrt(2)) : Math.abs(width * Math.sqrt(2)); // Maintain 1:√2 ratio

    const maxY = end[1] > minY ? minY + height : minY - height;

    const boxCoords: Coordinate[][] = [[
        [minX, minY],
        [maxX, minY],
        [maxX, maxY],
        [minX, maxY],
        [minX, minY], // Close the polygon
    ]];

    if (geometry) {
        geometry.setCoordinates(boxCoords);
        return geometry;
    }
    return new Polygon(boxCoords);
};


export const addInteractions = (
    map_id: string
) => {
    console.log("Adding interactions")
    const currentTool = getCurrentTool(map_id)
    const clearPrevious = getMeasureClearPrevious(map_id)
    const measureType = getMeasureType(map_id)
    const tipPoint = getTipPoint(map_id)
    const map = getMap(map_id)
    const measureModify = getMeasureModify(map_id)

    if (currentTool !== 'Measure' && currentTool !== 'Mover' && currentTool !== 'Edit' && currentTool !== 'Selection') {
        //map?.getInteractions().clear()

        setDraw(map_id, new Draw({
            source: getDrawingLayerSource(map_id),
            type: currentTool as Type,
            condition: noModifierKeys
        }))

        const draw = getDraw(map_id)

        if (draw) {
            draw.on('drawend', function (s) {
                console.log(s.feature.getGeometry()?.getType())
                let type: IGeometryType = '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() as Coordinate[]
                uploadCoordinates(coordinates, type)
            })

            map?.addInteraction(draw)
            setSnap(map_id, new Snap({ source: getDrawingLayerSource(map_id) }))

            const snap = getSnap(map_id)
            if (snap) {
                map?.addInteraction(snap)
            }
        }
    }

    if (currentTool == 'Measure') {
        //map?.getInteractions().clear()

        const drawType = measureType;
        const activeTip =
            'Кликните, чтобы продолжить рисовать ' +
            (drawType === 'Polygon' ? 'многоугольник' : 'линию');
        const idleTip = 'Кликните, чтобы начать измерение';
        let tip = idleTip;

        setMeasureDraw(map_id, new Draw({
            source: getMeasureSource(map_id),
            type: drawType,
            style: function (feature) {
                return measureStyleFunction(map_id, feature, drawType, tip);
            },
        }))

        const measureDraw = getMeasureDraw(map_id)

        if (measureDraw) {
            measureDraw.on('drawstart', function () {
                if (clearPrevious) {
                    getMeasureSource(map_id).clear();
                }
                measureModify.setActive(false);
                tip = activeTip;
            });
            measureDraw.on('drawend', function () {
                modifyStyle.setGeometry(tipPoint as Geometry);
                measureModify.setActive(true);
                map?.once('pointermove', function () {
                    modifyStyle.setGeometry('');
                });
                tip = idleTip;
            });
            measureModify.setActive(true)

            map?.addInteraction(measureDraw)
        }
    }

    if (currentTool == 'Mover') {
        const translateMode = new Translate({
            features: new Collection(getSelectedFeatures(map_id))
        })

        translateMode.on('translatestart', async (e) => {
            const features = e.features.getArray();
            const existingChanges = getChanges(map_id);
            const newChanges: any = [];

            features.forEach((f: Feature<Geometry>) => {
                const objectId = f.get('object_id');
                const year = f.get('year');

                // Check if this specific feature already has pending changes
                const hasExistingChange = Array.from(existingChanges.values()).some(
                    change => change.object_id === objectId && change.year === year
                );

                // Only add if no existing change
                if (!hasExistingChange) {
                    const json = new GeoJSON();

                    newChanges.push({
                        object_id: objectId,
                        year: year,
                        type: f.get('geometry_type') === 'line' ? 'line' : 'figure',
                        feature: JSON.parse(json.writeFeature(f, {
                            featureProjection: 'EPSG:3857',
                            dataProjection: 'EPSG:3857'
                        }))
                    });
                }
            });

            // Add only new changes
            if (newChanges.length > 0) {
                addChanges(map_id, newChanges);
            }
        });


        translateMode.on('translateend', async (e) => {
            const features = e.features.getArray()

            let changesJSON: any = []

            features.map((f: Feature<Geometry>) => {
                if (f.get('geometry_type') === 'line') {
                    const json = new GeoJSON()

                    changesJSON.push({
                        object_id: f.get('object_id'),
                        year: f.get('year'),
                        type: 'line',
                        feature: JSON.parse(json.writeFeature(f, {
                            featureProjection: 'EPSG:3857',
                            dataProjection: 'EPSG:3857'
                        }))
                    })
                } else {
                    const json = new GeoJSON()

                    changesJSON.push({
                        object_id: f.get('object_id'),
                        year: f.get('year'),
                        type: 'figure',
                        feature: JSON.parse(json.writeFeature(f, {
                            featureProjection: 'EPSG:3857',
                            dataProjection: 'EPSG:3857'
                        }))
                    })
                }
            })

            addChanges(map_id, changesJSON)
        })

        setTranslate(map_id, translateMode)

        const translate = getTranslate(map_id)
        if (translate) {
            //map?.getInteractions().clear()
            map?.addInteraction(translate)
        }
    }

    if (currentTool == 'Edit') {
        //const modify = new Modify()
        //map?.current?.addInteraction(translate.current)
    }

    if (currentTool == 'Selection') {
        console.log(map?.getInteractions().getArray())
        //map?.getInteractions().clear()

        const selectionDragBox = useMapStore.getState().id[map_id].selectionDragBox

        if (selectionDragBox) {
            map?.addInteraction(selectionDragBox)
        }
    }
}

export const zoomToFeature = (map_id: string, feature: Feature | undefined) => {
    if (feature) {
        const geometry = feature.getGeometry()
        const extent = geometry?.getExtent()

        if (getMap(map_id) && extent) {
            getMap(map_id)?.getView().fit(extent, {
                duration: 300,
                maxZoom: 19,
            })
        }
    }
}

export const getFeatureByEntityId = (entity_id: number, layer: VectorImageLayer<Feature<Geometry>, VectorSource<Feature<Geometry>>>) => {
    return layer.getSource()?.getFeatures().find(feature => feature.getProperties().entity_id === entity_id)
}

// Function to save features to localStorage
export const saveFeatures = (map_id: string) => {
    const features = getDrawingLayerSource(map_id).getFeatures()
    if (features && features.length > 0) {
        const geoJSON = new GeoJSON()
        const featuresJSON = geoJSON.writeFeatures(features)
        localStorage.setItem('savedFeatures', featuresJSON)
    }
}

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

export function rotateProjection(projection: ProjectionLike, angle: number, extent: Extent) {
    function rotateCoordinate(coordinate: Coordinate, angle: number, anchor: Coordinate) {
        const coord = rotate(
            [coordinate[0] - anchor[0], coordinate[1] - anchor[1]],
            angle
        );
        return [coord[0] + anchor[0], coord[1] + anchor[1]];
    }

    function rotateTransform(coordinate: Coordinate) {
        return rotateCoordinate(coordinate, angle, getCenter(extent));
    }

    function normalTransform(coordinate: Coordinate) {
        return rotateCoordinate(coordinate, -angle, getCenter(extent));
    }

    const normalProjection = get(projection);

    if (normalProjection) {
        const rotatedProjection = new Projection({
            code: normalProjection.getCode() + ":" + angle.toString() + ":" + extent.toString(),
            units: normalProjection.getUnits(),
            extent: extent
        });
        addProjection(rotatedProjection);

        addCoordinateTransforms(
            "EPSG:4326",
            rotatedProjection,
            function (coordinate) {
                return rotateTransform(transform(coordinate, "EPSG:4326", projection));
            },
            function (coordinate) {
                return transform(normalTransform(coordinate), projection, "EPSG:4326");
            }
        );

        addCoordinateTransforms(
            "EPSG:3857",
            rotatedProjection,
            function (coordinate) {
                return rotateTransform(transform(coordinate, "EPSG:3857", projection));
            },
            function (coordinate) {
                return transform(normalTransform(coordinate), projection, "EPSG:3857");
            }
        );

        // also set up transforms with any projections defined using proj4
        if (typeof proj4 !== "undefined") {
            const projCodes = Object.keys(proj4.defs);
            projCodes.forEach(function (code) {
                const proj4Projection = get(code) as Projection;
                if (proj4Projection) {
                    if (!getTransform(proj4Projection, rotatedProjection)) {
                        addCoordinateTransforms(
                            proj4Projection,
                            rotatedProjection,
                            function (coordinate) {
                                return rotateTransform(
                                    transform(coordinate, proj4Projection, projection)
                                );
                            },
                            function (coordinate) {
                                return transform(
                                    normalTransform(coordinate),
                                    projection,
                                    proj4Projection
                                );
                            }
                        );
                    }
                }
            });
        }

        return rotatedProjection;
    }
}

export const calculateCentroid = (bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) => {
    const x = (bottomLeft[0] + topLeft[0] + topRight[0] + bottomRight[0]) / 4;
    const y = (bottomLeft[1] + topLeft[1] + topRight[1] + bottomRight[1]) / 4;
    return [x, y];
}

export function calculateRotationAngle(bottomLeft: Coordinate, bottomRight: Coordinate) {
    // Calculate the difference in x and y coordinates between bottom right and bottom left
    const deltaX = bottomRight[0] - bottomLeft[0];
    const deltaY = bottomRight[1] - bottomLeft[1];

    // Calculate the angle using atan2
    const angle = -Math.atan2(deltaY, deltaX);

    return angle;
}

export function calculateExtent(bottomLeft: Coordinate, topLeft: Coordinate, topRight: Coordinate, bottomRight: Coordinate) {
    const width = distance(bottomLeft, bottomRight);
    const height = distance(bottomLeft, topLeft);

    // Calculate the centroid of the polygon
    const [centerX, centerY] = calculateCentroid(bottomLeft, topLeft, topRight, bottomRight);

    // Define the extent based on the center and dimensions
    const extent = [
        centerX - width / 2, // minX
        centerY - height / 2, // minY
        centerX + width / 2, // maxX
        centerY + height / 2  // maxY
    ];

    return extent;
}

export function getTilesPerSide(zoom: number) {
    return Math.pow(2, zoom)
}

export function normalize(value: number, min: number, max: number) {
    return (value - min) / (max - min)
}

export function getTileIndex(normalized: number, tilesPerSide: number) {
    return Math.floor(normalized * tilesPerSide)
}

export function getGridCellPosition(x: number, y: number, extent: Extent, zoom: number) {
    const tilesPerSide = getTilesPerSide(zoom);
    const minX = extent[0]
    const minY = extent[1]
    const maxX = extent[2]
    const maxY = extent[3]

    // Normalize the coordinates
    const xNormalized = normalize(x, minX, maxX);
    const yNormalized = normalize(y, minY, maxY);

    // Get tile indices
    const tileX = getTileIndex(xNormalized, tilesPerSide);
    const tileY = getTileIndex(1 - yNormalized, tilesPerSide);

    return { tileX, tileY };
}

export function calculateTransformations(alignPoints: Coordinate[]) {
    const [P1, P2, P3, P4] = alignPoints;

    // Translation vector (move P1 to P3)
    const translation = [P3[0] - P1[0], P3[1] - P1[1]];

    // Scaling factor (distance between P3 and P4 divided by P1 and P2)
    const distanceLayer = Math.sqrt((P2[0] - P1[0]) ** 2 + (P2[1] - P1[1]) ** 2);
    const distanceMap = Math.sqrt((P4[0] - P3[0]) ** 2 + (P4[1] - P3[1]) ** 2);
    const scale = distanceMap / distanceLayer;

    // Rotation angle (difference in angles between the two lines)
    const angleLayer = Math.atan2(P2[1] - P1[1], P2[0] - P1[0]);
    const angleMap = Math.atan2(P4[1] - P3[1], P4[0] - P3[0]);
    const rotation = angleMap - angleLayer;

    return { translation, scale, rotation };
}

export function calculateCenter(geometry: SimpleGeometry) {
    let center, coordinates, minRadius;
    const type = geometry.getType();
    if (type === 'Polygon') {
        let x = 0;
        let y = 0;
        let i = 0;
        coordinates = (geometry as Polygon).getCoordinates()[0].slice(1);
        coordinates.forEach(function (coordinate) {
            x += coordinate[0];
            y += coordinate[1];
            i++;
        });
        center = [x / i, y / i];
    } else if (type === 'LineString') {
        center = (geometry as LineString).getCoordinateAt(0.5);
        coordinates = geometry.getCoordinates();
    } else {
        center = getCenter(geometry.getExtent());
    }
    let sqDistances;
    if (coordinates) {
        sqDistances = coordinates.map(function (coordinate: Coordinate) {
            const dx = coordinate[0] - center[0];
            const dy = coordinate[1] - center[1];
            return dx * dx + dy * dy;
        });
        minRadius = Math.sqrt(Math.max(...sqDistances)) / 3;
    } else {
        minRadius =
            Math.max(
                getWidth(geometry.getExtent()),
                getHeight(geometry.getExtent()),
            ) / 3;
    }
    return {
        center: center,
        coordinates: coordinates,
        minRadius: minRadius,
        sqDistances: sqDistances,
    };
}

export function applyTransformations(
    layer: VectorLayer,
    transformations: {
        translation: number[];
        scale: number;
        rotation: number;
    },
    origin: Coordinate
) {
    const { translation, scale, rotation } = transformations;

    const source = layer.getSource();
    if (!source) return;

    source.getFeatures().forEach((feature) => {
        const geometry = feature.getGeometry();

        if (geometry) {
            // Translate
            geometry.translate(translation[0], translation[1]);

            // Scale (around the origin)
            geometry.scale(scale, scale, origin);

            // Rotate (around the origin)
            geometry.rotate(rotation, origin);
        }
    });

    console.log("Transformations applied to figuresLayer");
}