from cooptools.geometry_utils import vector_utils as vec
from coopgame.linedrawing import line_draw_utils as line
from shapely.geometry import Polygon
from cooptools.coopEnum import CoopEnum, auto
from dataclasses import dataclass, field
from cooptools.coopDataclass import BaseIdentifiedDataClass, UniqueIdentifier
from typing import List, Dict, Set, Tuple, Self, Iterator
import logging
from cooptools.colors import Color
import uuid

logger = logging.getLogger(__name__)


class TerrainType(CoopEnum):
    DEEPSEAWATER = auto()
    SHALLOWSEAWATER = auto()
    BEACH = auto()
    GRASS = auto()
    GRASSHILLS = auto()
    FOREST = auto()
    PLAINS = auto()
    PLAINSHILLS=auto()
    MOUNTAINS = auto()
    SNOWPEAK = auto()
    UNDEFINED = -1

TERRAIN_COMPATIBLITY_MAP = {
    TerrainType.DEEPSEAWATER: [TerrainType.DEEPSEAWATER, TerrainType.SHALLOWSEAWATER],
    TerrainType.SHALLOWSEAWATER: [TerrainType.DEEPSEAWATER, TerrainType.SHALLOWSEAWATER, TerrainType.BEACH],
    TerrainType.BEACH: [TerrainType.SHALLOWSEAWATER, TerrainType.BEACH, TerrainType.PLAINS, TerrainType.GRASS],
    TerrainType.GRASS: [TerrainType.FOREST, TerrainType.GRASS, TerrainType.GRASSHILLS, TerrainType.PLAINS, TerrainType.PLAINSHILLS, TerrainType.BEACH],
    TerrainType.GRASSHILLS: [TerrainType.GRASS, TerrainType.GRASSHILLS, TerrainType.PLAINS, TerrainType.PLAINSHILLS, TerrainType.MOUNTAINS],
    TerrainType.PLAINS: [TerrainType.GRASS, TerrainType.GRASSHILLS, TerrainType.PLAINS, TerrainType.PLAINSHILLS, TerrainType.BEACH],
    TerrainType.PLAINSHILLS: [TerrainType.GRASS, TerrainType.GRASSHILLS, TerrainType.PLAINS, TerrainType.PLAINSHILLS, TerrainType.MOUNTAINS],
    TerrainType.MOUNTAINS: [TerrainType.PLAINSHILLS, TerrainType.GRASSHILLS, TerrainType.MOUNTAINS, TerrainType.SNOWPEAK],
    TerrainType.SNOWPEAK: [TerrainType.MOUNTAINS, TerrainType.SNOWPEAK],
    TerrainType.FOREST: [TerrainType.GRASS, TerrainType.FOREST],
    TerrainType.UNDEFINED: []
}

TERRAIN_COLOR_MAP = {
    TerrainType.DEEPSEAWATER: Color.REGAL_NAVY,
    TerrainType.SHALLOWSEAWATER: Color.FRESH_SKY,
    TerrainType.BEACH: Color.CHAMPAGNE_MIST,
    TerrainType.GRASS: Color.LIGHT_GREEN,
    TerrainType.GRASSHILLS: Color.LIGHT_GREEN,
    TerrainType.PLAINS: Color.CAMEL,
    TerrainType.PLAINSHILLS: Color.CAMEL,
    TerrainType.MOUNTAINS: Color.ROSY_GRANITE,
    TerrainType.SNOWPEAK: Color.BRIGHT_SNOW,
    TerrainType.FOREST: Color.BLACK_FOREST,
    TerrainType.UNDEFINED: Color.HOT_PINK
}

class VisibilityLevel(CoopEnum):
    HIDDEN = auto()
    REVEALABLE = auto()
    REVEALING = auto()
    REVEALED = auto()

@dataclass(frozen=True)
class NeighborAttrs:
    shared_verts: Dict[int, vec.FloatVec]
    cnxn_node_id: str

    @property
    def Boundary(self)-> line.Line:
        if len(self.shared_verts) >= 2:
            return line.Line(*self.shared_verts.values())
        return None

@dataclass
class Region:
    name: UniqueIdentifier
    pt: vec.FloatVec2D
    terrain: TerrainType
    boundary: Polygon
    neighbors: Dict[int, NeighborAttrs]
    visibility_level: VisibilityLevel = field(init=False, default=VisibilityLevel.HIDDEN)
    action_states: List = field(default_factory=list, init=False)
    id: uuid.UUID = field(init=False, default_factory=uuid.uuid4)
    node_id: str = field(init=False)
    vert_node_ids: List[str]

    def __post_init__(self):
        self.node_id = f"R{self.name}"

    def __hash__(self):
        return hash(self.id)

    def __eq__(self, other):
        if type(other) != Region:
            return False

        return self.__hash__() == other.__hash__()

    def add_action_state(self, state):
        self.action_states.append(state)
        self.action_states = list(set(self.action_states))
        return self

    def remove_action_state(self, state, force: bool = False):
        if state not in self.action_states and not force:
            raise Exception(f"{state} not in region \'{self.name}\'s action_states")

        if state in self.action_states:
            self.action_states.remove(state)

        return self

    def set_visibility(self, requested, force:bool = False):
        dependencies = {
            VisibilityLevel.REVEALABLE: [VisibilityLevel.HIDDEN, VisibilityLevel.REVEALABLE],
            VisibilityLevel.REVEALED: [VisibilityLevel.REVEALABLE, VisibilityLevel.REVEALING, VisibilityLevel.REVEALED],
            VisibilityLevel.HIDDEN: [VisibilityLevel.HIDDEN],
            VisibilityLevel.REVEALING: [VisibilityLevel.REVEALABLE, VisibilityLevel.REVEALING]
        }

        if self.visibility_level not in dependencies[requested] and not force:
            raise Exception(f"Cannot set {requested} a region that was {self.visibility_level}")

        self.visibility_level = requested

        return self

    @property
    def RiverEligible(self):
        if self.terrain not in [
            TerrainType.MOUNTAINS,
            TerrainType.SNOWPEAK,
            TerrainType.DEEPSEAWATER,
            TerrainType.SHALLOWSEAWATER
        ]:
            return True

        return False
