package com.zivilon.cinder_loe.carriage;

import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagList;
import net.minecraft.world.World;
import net.minecraft.world.WorldSavedData;
import net.minecraft.world.WorldServer;
import net.minecraft.world.storage.MapStorage;

import java.util.*;
import java.util.function.BiPredicate;
import java.util.stream.Collectors;

public class CarriageDestinationsData extends WorldSavedData {
    public static final String DATA_NAME = "carriage_destinations";
    private final Map<String, CarriageDestination> destinations = new HashMap<>();

    public CarriageDestinationsData() {
        super(DATA_NAME);
    }

    public CarriageDestinationsData(String name) {
        super(name);
    }

    @Override
    public void readFromNBT(NBTTagCompound nbt) {
        destinations.clear();
        NBTTagList list = nbt.getTagList("Destinations", 10);
        for (int i = 0; i < list.tagCount(); i++) {
            NBTTagCompound tag = list.getCompoundTagAt(i);
            String name = tag.getString("Name");
            int x = tag.getInteger("X");
            int y = tag.getInteger("Y");
            int z = tag.getInteger("Z");
            int dim = tag.getInteger("Dim");
            String road = tag.getString("Road");
            destinations.put(name, new CarriageDestination(name, x, y, z, dim, road));

        }
    }

    @Override
    public void writeToNBT(NBTTagCompound nbt) {
        NBTTagList list = new NBTTagList();
        for (CarriageDestination dest : destinations.values()) {
            NBTTagCompound tag = new NBTTagCompound();
            tag.setString("Name", dest.name);
            tag.setInteger("X", dest.x);
            tag.setInteger("Y", dest.y);
            tag.setInteger("Z", dest.z);
            tag.setInteger("Dim", dest.dimension);
            tag.setString("Road", dest.road);
            list.appendTag(tag);
        }
        nbt.setTag("Destinations", list);
    }

    public void addDestination(CarriageDestination dest) {
        destinations.put(dest.name, dest);
        markDirty();
    }

    public Map<String, CarriageDestination> getDestinations() {
        return Collections.unmodifiableMap(destinations);
    }

    public static CarriageDestinationsData get(World world) {
        if (!world.isRemote && world instanceof WorldServer) {
            MapStorage storage = ((WorldServer) world).mapStorage;
            CarriageDestinationsData data = (CarriageDestinationsData) storage.loadData(CarriageDestinationsData.class, DATA_NAME);
            if (data == null) {
                data = new CarriageDestinationsData();
                storage.setData(DATA_NAME, data);
            }
            return data;
        }
        return null;
    }

    /**
     * Return up to four nearest destinations reachable via the
     * same road network as the point (px, pz).  Only destinations whose
     * road is connected (directly or indirectly) to the start road
     * will be considered.  Distances under skipRadius are skipped.
     */

    public List<CarriageDestination> getNearestFour(double px, double pz, String startRoadId, CarriageRoadRegistry network) {
        final double skipRadius = 100;

        // List of roads included, disconnected regions like Gulfen will not magically travel across the desert
        Set<String> allowedRoads = network.reachableRoadIds(startRoadId);

        List<CarriageDestination> pool = destinations.values().stream()
                .filter(d -> allowedRoads.contains(d.road))
                .collect(Collectors.toList());

        // Sort by distance from player
        pool.sort(Comparator.comparingDouble(d -> {
            double dx = d.x - px, dz = d.z - pz;
            return Math.hypot(dx, dz);
        }));

        // Pick up to 4, skip within radius
        List<CarriageDestination> result = new ArrayList<>(4);
        for (CarriageDestination d : pool) {
            double dx = d.x - px, dz = d.z - pz;
            double dist = Math.hypot(dx, dz);
            if (dist < skipRadius) continue;
            result.add(d);
            if (result.size() == 4) break;
        }

        return result;
    }


}

