package zenith.zov.client.hud.elements.component;

import java.util.HashMap;
import java.util.Map;
import zenith.zov.base.font.Font;
import zenith.zov.base.font.Fonts;
import zenith.zov.client.hud.elements.draggable.DraggableHudElement;
import zenith.zov.client.modules.api.setting.impl.BooleanSetting;
import zenith.zov.client.modules.api.setting.impl.ColorSetting;
import zenith.zov.client.modules.api.setting.impl.NumberSetting;
import zenith.zov.utility.render.display.base.BorderRadius;
import zenith.zov.utility.render.display.base.CustomDrawContext;
import zenith.zov.utility.render.display.base.color.ColorRGBA;

public class RegionMapComponent extends DraggableHudElement {
    private final NumberSetting mapPosX = new NumberSetting("Position X", 15.0F, 0.0F, 1920.0F, 1.0F, "Horizontal position of the region map");
    private final NumberSetting mapPosY = new NumberSetting("Position Y", 15.0F, 0.0F, 1080.0F, 1.0F, "Vertical position of the region map");
    private final NumberSetting cellDimension = new NumberSetting("Cell Size", 22.0F, 12.0F, 50.0F, 1.0F, "Size of each map cell");
    private final BooleanSetting enableCoordinates = new BooleanSetting("Show Coordinates", "Display player coordinates below map", true);
    private final BooleanSetting enableRegionLabels = new BooleanSetting("Show Region Labels", "Show region type labels with colors", true);
    private final BooleanSetting enableMapGrid = new BooleanSetting("Show Grid", "Draw grid lines between regions", true);
    private final BooleanSetting enablePlayerIndicator = new BooleanSetting("Show Player Indicator", "Show player position and direction on map", true);
    private final NumberSetting mapTransparency = new NumberSetting("Transparency", 0.75F, 0.1F, 1.0F, 0.05F, "Map background transparency level");
    private final NumberSetting labelTextSize = new NumberSetting("Label Size", 0.9F, 0.4F, 2.5F, 0.1F, "Size of region number labels");
    private final ColorSetting mapBackgroundColor = new ColorSetting("Background Color", new ColorRGBA(25, 25, 25, 180));
    private final ColorSetting playerIndicatorColor = new ColorSetting("Player Color", new ColorRGBA(255, 50, 50, 255));
    private final ColorSetting gridLineColor = new ColorSetting("Grid Color", new ColorRGBA(15, 15, 15, 255));
    private final RegionMapComponent.MapDataManager mapData = new RegionMapComponent.MapDataManager();
    private final RegionMapComponent.RegionRenderer regionRenderer = new RegionMapComponent.RegionRenderer();
    private final RegionMapComponent.PlayerTracker playerTracker = new RegionMapComponent.PlayerTracker();

    public RegionMapComponent(
        String name, float initialX, float initialY, float windowWidth, float windowHeight, float offsetX, float offsetY, DraggableHudElement.Align align
    ) {
        super(name, initialX, initialY, windowWidth, windowHeight, offsetX, offsetY, align);
        this.width = 200.0F;
        this.height = 200.0F;
    }

    @Override
    public void render(CustomDrawContext ctx) {
        if (this.isValidRenderState()) {
            int cellSize = (int)this.cellDimension.getCurrent();
            this.width = (float)(this.mapData.getMapSize() * cellSize + 20);
            this.height = (float)(this.mapData.getMapSize() * cellSize + 20);
            int mapX = (int)(this.x + this.mapPosX.getCurrent());
            int mapY = (int)(this.y + this.mapPosY.getCurrent());
            RegionMapComponent.MapRenderContext mapCtx = new RegionMapComponent.MapRenderContext(
                mapX, mapY, cellSize, (double)this.mapTransparency.getCurrent()
            );
            this.regionRenderer.renderMapBackground(ctx, mapCtx, this.mapBackgroundColor.getColor());
            this.regionRenderer.renderRegionCells(ctx, mapCtx, this.mapData);
            if (this.enableMapGrid.isEnabled()) {
                this.regionRenderer.renderGridLines(ctx, mapCtx, this.gridLineColor.getColor());
            }

            this.regionRenderer.renderRegionNumbers(ctx, mapCtx, this.mapData, (double)this.labelTextSize.getCurrent());
            if (this.enablePlayerIndicator.isEnabled() && mc.player != null) {
                this.playerTracker
                    .renderPlayerPosition(
                        ctx,
                        mapCtx,
                        mc.player.getX(),
                        mc.player.getZ(),
                        mc.player.getYaw(),
                        this.playerIndicatorColor.getColor()
                    );
            }

            if (this.enableCoordinates.isEnabled() && mc.player != null) {
                this.renderPlayerInfo(ctx, mapCtx, mc.player.getX(), mc.player.getZ());
            }

            if (this.enableRegionLabels.isEnabled()) {
                this.renderRegionLegend(ctx, mapCtx);
            }
        }
    }

    private boolean isValidRenderState() {
        return mc.player != null && mc.world != null;
    }

    private void renderPlayerInfo(CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, double playerX, double playerZ) {
        int infoY = mapCtx.mapY + mapCtx.getMapHeight() + 8;
        Font font = Fonts.MEDIUM.getFont(12.0F);
        String coordsText = String.format("Position: X: %d, Z: %d", (int)playerX, (int)playerZ);
        ctx.drawText(font, coordsText, (float)mapCtx.mapX, (float)infoY, ColorRGBA.WHITE);
        int currentRegionId = this.mapData.getRegionAt(playerX, playerZ);
        if (currentRegionId != -1) {
            String regionInfo = String.format("Current Region: %d (%s)", currentRegionId, this.mapData.getRegionTypeName(playerX, playerZ));
            ctx.drawText(font, regionInfo, (float)mapCtx.mapX, (float)(infoY + 15), ColorRGBA.WHITE);
        }
    }

    private void renderRegionLegend(CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx) {
        int legendStartY = mapCtx.mapY + mapCtx.getMapHeight() + (this.enableCoordinates.isEnabled() ? 45 : 15);
        String[] regionTypes = this.mapData.getRegionTypeNames();
        ColorRGBA[] regionTypeColors = this.mapData.getRegionTypeColors();
        Font font = Fonts.MEDIUM.getFont(10.0F);

        for (int i = 0; i < regionTypes.length; i++) {
            int legendY = legendStartY + i * 16;
            ctx.drawRoundedRect((float)mapCtx.mapX, (float)legendY, 14.0F, 14.0F, BorderRadius.all(2.0F), regionTypeColors[i]);
        }

        for (int i = 0; i < regionTypes.length; i++) {
            int legendY = legendStartY + i * 16 + 3;
            ctx.drawText(font, regionTypes[i], (float)(mapCtx.mapX + 18), (float)legendY, ColorRGBA.WHITE);
        }
    }

    private static class MapDataManager {
        private static final int MAP_SIZE = 9;
        private static final double REGION_SIZE = 50000.0;
        private static final double MAP_OFFSET = 225000.0;
        private final Map<Integer, RegionMapComponent.RegionInfo> regionMap = new HashMap<>();
        private final String[] regionTypeNames = new String[]{"EU Central", "EU West", "NA East", "NA West", "Asia", "Oceania"};
        private final ColorRGBA[] regionTypeColors = new ColorRGBA[]{
            new ColorRGBA(159, 206, 99, 255),
            new ColorRGBA(0, 166, 99, 255),
            new ColorRGBA(79, 173, 234, 255),
            new ColorRGBA(47, 110, 186, 255),
            new ColorRGBA(245, 194, 66, 255),
            new ColorRGBA(252, 136, 3, 255)
        };

        public MapDataManager() {
            this.initializeRegionData();
        }

        private void initializeRegionData() {
            int[][] regionLayout = new int[][]{
                {82, 5},
                {100, 3},
                {101, 3},
                {102, 3},
                {103, 2},
                {104, 2},
                {105, 2},
                {106, 2},
                {91, 2},
                {83, 5},
                {44, 3},
                {75, 3},
                {42, 3},
                {41, 2},
                {40, 2},
                {39, 2},
                {38, 2},
                {92, 2},
                {84, 5},
                {45, 3},
                {14, 3},
                {13, 3},
                {12, 2},
                {11, 2},
                {10, 2},
                {37, 2},
                {93, 2},
                {85, 5},
                {46, 5},
                {74, 5},
                {3, 3},
                {2, 2},
                {1, 2},
                {25, 2},
                {36, 2},
                {94, 2},
                {86, 4},
                {47, 4},
                {72, 4},
                {71, 4},
                {5, 2},
                {4, 2},
                {24, 2},
                {35, 2},
                {95, 2},
                {87, 4},
                {51, 1},
                {17, 1},
                {9, 0},
                {8, 0},
                {7, 0},
                {23, 0},
                {34, 0},
                {96, 2},
                {88, 4},
                {54, 1},
                {18, 1},
                {61, 0},
                {62, 0},
                {21, 0},
                {22, 0},
                {33, 0},
                {97, 0},
                {89, 0},
                {26, 1},
                {27, 0},
                {28, 0},
                {29, 0},
                {30, 0},
                {59, 0},
                {32, 0},
                {98, 0},
                {90, 0},
                {107, 1},
                {108, 1},
                {109, 1},
                {110, 1},
                {111, 1},
                {112, 1},
                {113, 1},
                {99, 0}
            };

            for (int i = 0; i < regionLayout.length; i++) {
                int row = i / 9;
                int col = i % 9;
                int regionId = regionLayout[i][0];
                int regionType = regionLayout[i][1];
                this.regionMap.put(i, new RegionMapComponent.RegionInfo(regionId, regionType, row, col));
            }
        }

        public RegionMapComponent.RegionInfo getRegionInfo(int gridIndex) {
            return this.regionMap.get(gridIndex);
        }

        public int getRegionAt(double worldX, double worldZ) {
            int[] gridPos = this.worldToGrid(worldX, worldZ);
            if (this.isValidGridPosition(gridPos[0], gridPos[1])) {
                int index = gridPos[1] * 9 + gridPos[0];
                RegionMapComponent.RegionInfo info = this.regionMap.get(index);
                return info != null ? info.regionId : -1;
            } else {
                return -1;
            }
        }

        public String getRegionTypeName(double worldX, double worldZ) {
            int[] gridPos = this.worldToGrid(worldX, worldZ);
            if (this.isValidGridPosition(gridPos[0], gridPos[1])) {
                int index = gridPos[1] * 9 + gridPos[0];
                RegionMapComponent.RegionInfo info = this.regionMap.get(index);
                return info != null ? this.regionTypeNames[info.regionType] : "Unknown";
            } else {
                return "Unknown";
            }
        }

        public ColorRGBA getRegionColor(int regionType) {
            return regionType >= 0 && regionType < this.regionTypeColors.length ? this.regionTypeColors[regionType] : ColorRGBA.WHITE;
        }

        public String[] getRegionTypeNames() {
            return (String[])this.regionTypeNames.clone();
        }

        public ColorRGBA[] getRegionTypeColors() {
            return (ColorRGBA[])this.regionTypeColors.clone();
        }

        public int[] worldToGrid(double worldX, double worldZ) {
            int gridX = (int)((worldX + 225000.0) / 50000.0);
            int gridZ = (int)((worldZ + 225000.0) / 50000.0);
            return new int[]{gridX, gridZ};
        }

        public double[] worldToCellPosition(double worldX, double worldZ) {
            double cellX = (worldX + 225000.0) % 50000.0 / 50000.0;
            double cellZ = (worldZ + 225000.0) % 50000.0 / 50000.0;
            return new double[]{cellX, cellZ};
        }

        private boolean isValidGridPosition(int gridX, int gridZ) {
            return gridX >= 0 && gridX < 9 && gridZ >= 0 && gridZ < 9;
        }

        public int getMapSize() {
            return 9;
        }
    }

    private static class MapRenderContext {
        final int mapX;
        final int mapY;
        final int cellSize;
        final double transparency;

        MapRenderContext(int x, int y, int cellSize, double transparency) {
            this.mapX = x;
            this.mapY = y;
            this.cellSize = cellSize;
            this.transparency = transparency;
        }

        int getMapWidth() {
            return 9 * this.cellSize;
        }

        int getMapHeight() {
            return 9 * this.cellSize;
        }
    }

    private class PlayerTracker {
        void renderPlayerPosition(
            CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, double playerX, double playerZ, float yaw, ColorRGBA indicatorColor
        ) {
            int[] gridPos = RegionMapComponent.this.mapData.worldToGrid(playerX, playerZ);
            if (gridPos[0] >= 0
                && gridPos[0] < RegionMapComponent.this.mapData.getMapSize()
                && gridPos[1] >= 0
                && gridPos[1] < RegionMapComponent.this.mapData.getMapSize()) {
                double[] cellPos = RegionMapComponent.this.mapData.worldToCellPosition(playerX, playerZ);
                int indicatorX = mapCtx.mapX + gridPos[0] * mapCtx.cellSize + (int)(cellPos[0] * (double)mapCtx.cellSize);
                int indicatorY = mapCtx.mapY + gridPos[1] * mapCtx.cellSize + (int)(cellPos[1] * (double)mapCtx.cellSize);
                double rotationAngle = Math.toRadians((double)(-yaw) - 90.0);
                this.renderDirectionalIndicator(ctx, indicatorX, indicatorY, rotationAngle, indicatorColor);
            }
        }

        private void renderDirectionalIndicator(CustomDrawContext ctx, int centerX, int centerY, double angle, ColorRGBA color) {
            int arrowSize = 9;
            int tipX = centerX + (int)(Math.cos(angle) * (double)arrowSize);
            int tipY = centerY - (int)(Math.sin(angle) * (double)arrowSize);
            double leftBaseAngle = angle + Math.toRadians(135.0);
            double rightBaseAngle = angle - Math.toRadians(135.0);
            int leftBaseX = centerX + (int)(Math.cos(leftBaseAngle) * (double)arrowSize);
            int leftBaseY = centerY - (int)(Math.sin(leftBaseAngle) * (double)arrowSize);
            int rightBaseX = centerX + (int)(Math.cos(rightBaseAngle) * (double)arrowSize);
            int rightBaseY = centerY - (int)(Math.sin(rightBaseAngle) * (double)arrowSize);
            this.drawTriangleFilled(ctx, tipX, tipY, leftBaseX, leftBaseY, rightBaseX, rightBaseY, color);
        }

        private void drawTriangleFilled(CustomDrawContext ctx, int x1, int y1, int x2, int y2, int x3, int y3, ColorRGBA color) {
            int minY = Math.min(y1, Math.min(y2, y3));
            int maxY = Math.max(y1, Math.max(y2, y3));

            for (int scanY = minY; scanY <= maxY; scanY++) {
                int leftX = Integer.MAX_VALUE;
                int rightX = Integer.MIN_VALUE;
                int[] intersections = new int[]{
                    this.getEdgeIntersection(x1, y1, x2, y2, scanY),
                    this.getEdgeIntersection(x2, y2, x3, y3, scanY),
                    this.getEdgeIntersection(x3, y3, x1, y1, scanY)
                };

                for (int intersection : intersections) {
                    if (intersection != Integer.MAX_VALUE) {
                        leftX = Math.min(leftX, intersection);
                        rightX = Math.max(rightX, intersection);
                    }
                }

                if (leftX <= rightX) {
                    ctx.drawRoundedRect((float)leftX, (float)scanY, (float)(rightX - leftX + 1), 1.0F, BorderRadius.ZERO, color);
                }
            }
        }

        private int getEdgeIntersection(int x1, int y1, int x2, int y2, int scanY) {
            if (scanY < Math.min(y1, y2) || scanY > Math.max(y1, y2)) {
                return Integer.MAX_VALUE;
            } else {
                return y1 == y2 ? (x1 + x2) / 2 : x1 + (x2 - x1) * (scanY - y1) / (y2 - y1);
            }
        }
    }

    private static class RegionInfo {
        final int regionId;
        final int regionType;
        final int gridRow;
        final int gridCol;

        RegionInfo(int regionId, int regionType, int gridRow, int gridCol) {
            this.regionId = regionId;
            this.regionType = regionType;
            this.gridRow = gridRow;
            this.gridCol = gridCol;
        }
    }

    private class RegionRenderer {
        void renderMapBackground(CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, ColorRGBA bgColor) {
            ColorRGBA adjustedColor = bgColor.mulAlpha((float)mapCtx.transparency);
            ctx.drawRoundedRect(
                (float)mapCtx.mapX, (float)mapCtx.mapY, (float)mapCtx.getMapWidth(), (float)mapCtx.getMapHeight(), BorderRadius.all(4.0F), adjustedColor
            );
        }

        void renderRegionCells(CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, RegionMapComponent.MapDataManager dataManager) {
            int mapSize = dataManager.getMapSize();

            for (int row = 0; row < mapSize; row++) {
                for (int col = 0; col < mapSize; col++) {
                    int index = row * mapSize + col;
                    RegionMapComponent.RegionInfo regionInfo = dataManager.getRegionInfo(index);
                    if (regionInfo != null) {
                        int cellX = mapCtx.mapX + col * mapCtx.cellSize;
                        int cellY = mapCtx.mapY + row * mapCtx.cellSize;
                        ColorRGBA regionColor = dataManager.getRegionColor(regionInfo.regionType);
                        regionColor = regionColor.mulAlpha((float)mapCtx.transparency);
                        ctx.drawRoundedRect(
                            (float)(cellX + 1),
                            (float)(cellY + 1),
                            (float)(mapCtx.cellSize - 2),
                            (float)(mapCtx.cellSize - 2),
                            BorderRadius.all(2.0F),
                            regionColor
                        );
                    }
                }
            }
        }

        void renderGridLines(CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, ColorRGBA gridColor) {
            int mapSize = 9;

            for (int i = 0; i <= mapSize; i++) {
                int lineX = mapCtx.mapX + i * mapCtx.cellSize;
                ctx.drawRoundedRect((float)lineX, (float)mapCtx.mapY, 1.0F, (float)mapCtx.getMapHeight(), BorderRadius.ZERO, gridColor);
            }

            for (int i = 0; i <= mapSize; i++) {
                int lineY = mapCtx.mapY + i * mapCtx.cellSize;
                ctx.drawRoundedRect((float)mapCtx.mapX, (float)lineY, (float)mapCtx.getMapWidth(), 1.0F, BorderRadius.ZERO, gridColor);
            }
        }

        void renderRegionNumbers(
            CustomDrawContext ctx, RegionMapComponent.MapRenderContext mapCtx, RegionMapComponent.MapDataManager dataManager, double textScale
        ) {
            Font font = Fonts.MEDIUM.getFont((float)((int)(12.0 * textScale)));
            int mapSize = dataManager.getMapSize();

            for (int row = 0; row < mapSize; row++) {
                for (int col = 0; col < mapSize; col++) {
                    int index = row * mapSize + col;
                    RegionMapComponent.RegionInfo regionInfo = dataManager.getRegionInfo(index);
                    if (regionInfo != null) {
                        int cellX = mapCtx.mapX + col * mapCtx.cellSize;
                        int cellY = mapCtx.mapY + row * mapCtx.cellSize;
                        String numberText = String.valueOf(regionInfo.regionId);
                        float textWidth = font.width(numberText);
                        float textHeight = font.height();
                        float centeredX = (float)cellX + ((float)mapCtx.cellSize - textWidth) / 2.0F;
                        float centeredY = (float)cellY + ((float)mapCtx.cellSize - textHeight) / 2.0F;
                        ctx.drawText(font, numberText, centeredX, centeredY, ColorRGBA.WHITE);
                    }
                }
            }
        }
    }
}
