import tkinter as tk
from tkinter import messagebox
from typing import Optional

# Context objects (set via set_context from FlowSync)
_ribbon = None
_winRoadProxy = None
_logProxy = None


def set_context(ribbon, winRoadProxy, logProxy):
    global _ribbon, _winRoadProxy, _logProxy
    _ribbon = ribbon
    _winRoadProxy = winRoadProxy
    _logProxy = logProxy


class RibbonButtonHandler:
    def SetCOMEventClass(self, events):
        self.events = events

    def OnIsExistEventHandler(self, funcname):
        try:
            getattr(self.events, funcname)
        except AttributeError:
            return False
        return True

    def OnClick(self):
        if _logProxy:
            _logProxy.logger.info("Ribbon button clicked (no-op)")


import win32com.client as com

class CameraButtonHandler(RibbonButtonHandler):
    def __init__(self):
        super().__init__()
        self.camera_mode = 0  # Track current camera mode

    def OnClick(self):
        """Cycle through camera modes."""
        try:
            modes = [
                ("Top View", self._apply_top_view),
                ("Follow Vehicle", self._apply_follow_vehicle),
                ("Orbit View", self._apply_orbit_view),
                ("Bird's Eye", self._apply_birds_eye)
            ]

            mode_name, mode_func = modes[self.camera_mode]
            mode_func()

            if _logProxy:
                _logProxy.logger.info(f"Switched to camera mode: {mode_name}")

            self.camera_mode = (self.camera_mode + 1) % len(modes)

        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Camera mode switch failed: {e}")

    def _apply_top_view_fallback(self):
        try:
            const = _winRoadProxy.const
            mainCam = _winRoadProxy.MainForm.MainCamera
            camState = mainCam.MainCameraState
            view = camState.ViewPoint
            top_eye = com.Dispatch('UCwinRoad.F8COMdVec3')
            top_eye.X, top_eye.Y, top_eye.Z = view.X, view.Y + 120.0, view.Z

            new_state = com.Dispatch('UCwinRoad.F8COMMainCameraStateType')
            new_state.allowUnderTerrain = True
            new_state.cameraMode = const._useTiltAng
            new_state.eye = top_eye
            new_state.ViewPoint = view
            new_state.tiltAngle = 0

            mainCam.MainCameraState = new_state
            if _logProxy:
                _logProxy.logger.info("Applied top view to main window (fallback mode).")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Top view fallback failed: {e}")

    def _apply_top_view(self):
        """Apply top-down view."""
        try:
            const = _winRoadProxy.const
            mainCam = _winRoadProxy.MainForm.MainCamera
            camState = mainCam.MainCameraState
            view = camState.ViewPoint

            # Position camera above the current viewpoint
            top_eye = com.Dispatch('UCwinRoad.F8COMdVec3')
            top_eye.X, top_eye.Y, top_eye.Z = view.X, view.Y + 150.0, view.Z

            new_state = com.Dispatch('UCwinRoad.F8COMMainCameraStateType')
            new_state.allowUnderTerrain = True
            new_state.cameraMode = const._useTiltAng
            new_state.eye = top_eye
            new_state.ViewPoint = view
            new_state.tiltAngle = 0

            mainCam.MainCameraState = new_state
            if _logProxy:
                _logProxy.logger.info("Applied top view.")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Top view failed: {e}")

    def _apply_follow_vehicle(self):
        """Follow a vehicle with the camera."""
        try:
            # Try to find a vehicle to follow
            traffic = _winRoadProxy.MainForm.Traffic
            vehicles = traffic.GetTransientVehiclesArround(1000, _winRoadProxy.MainForm.MainCamera.MainCameraState.eye)

            if vehicles.Count > 0:
                # Follow the first vehicle
                vehicle = vehicles.Item(0)
                const = _winRoadProxy.const
                mainCam = _winRoadProxy.MainForm.MainCamera

                new_state = com.Dispatch('UCwinRoad.F8COMMainCameraStateType')
                new_state.allowUnderTerrain = True
                new_state.cameraMode = const._followVehicle
                new_state.followVehicleID = vehicle.ID

                mainCam.MainCameraState = new_state
                if _logProxy:
                    _logProxy.logger.info(f"Following vehicle ID: {vehicle.ID}")
            else:
                if _logProxy:
                    _logProxy.logger.info("No vehicles found to follow, using top view fallback.")
                self._apply_top_view_fallback()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Follow vehicle failed: {e}")

    def _apply_orbit_view(self):
        """Apply orbital camera view."""
        try:
            const = _winRoadProxy.const
            mainCam = _winRoadProxy.MainForm.MainCamera
            camState = mainCam.MainCameraState
            view = camState.ViewPoint

            # Position camera for orbital view
            orbit_eye = com.Dispatch('UCwinRoad.F8COMdVec3')
            orbit_eye.X, orbit_eye.Y, orbit_eye.Z = view.X + 100.0, view.Y + 100.0, view.Z + 50.0

            new_state = com.Dispatch('UCwinRoad.F8COMMainCameraStateType')
            new_state.allowUnderTerrain = True
            new_state.cameraMode = const._useTiltAng
            new_state.eye = orbit_eye
            new_state.ViewPoint = view
            new_state.tiltAngle = -30  # Slight tilt for orbital view

            mainCam.MainCameraState = new_state
            if _logProxy:
                _logProxy.logger.info("Applied orbital view.")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Orbit view failed: {e}")

    def _apply_birds_eye(self):
        """Apply bird's eye view."""
        try:
            const = _winRoadProxy.const
            mainCam = _winRoadProxy.MainForm.MainCamera
            camState = mainCam.MainCameraState
            view = camState.ViewPoint

            # High altitude bird's eye view
            birds_eye = com.Dispatch('UCwinRoad.F8COMdVec3')
            birds_eye.X, birds_eye.Y, birds_eye.Z = view.X, view.Y + 300.0, view.Z

            new_state = com.Dispatch('UCwinRoad.F8COMMainCameraStateType')
            new_state.allowUnderTerrain = True
            new_state.cameraMode = const._useTiltAng
            new_state.eye = birds_eye
            new_state.ViewPoint = view
            new_state.tiltAngle = 0

            mainCam.MainCameraState = new_state
            if _logProxy:
                _logProxy.logger.info("Applied bird's eye view.")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Bird's eye view failed: {e}")


class PrevRoadHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            OpenRoadSelectionWindowHandler().OnClick()
        except Exception:
            pass


class NextRoadHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            OpenRoadSelectionWindowHandler().OnClick()
        except Exception:
            pass


class AddVehiclesHandler(RibbonButtonHandler):
    def OnClick(self):
        count = min(getattr(_ribbon.scenario, 'vehicle_count', 0) + 10, 200)
        _ribbon.scenario.vehicle_count = count
        if hasattr(_ribbon, 'vehicleLabel'):
            _ribbon.vehicleLabel.Caption = f'{count} vehicles'


class RemoveVehiclesHandler(RibbonButtonHandler):
    def OnClick(self):
        count = max(getattr(_ribbon.scenario, 'vehicle_count', 10) - 10, 10)
        _ribbon.scenario.vehicle_count = count
        if hasattr(_ribbon, 'vehicleLabel'):
            _ribbon.vehicleLabel.Caption = f'{count} vehicles'


class IncSpeedHandler(RibbonButtonHandler):
    def OnClick(self):
        min_speed = min(getattr(_ribbon.scenario, 'min_speed', 20) + 5, 80)
        max_speed = min(getattr(_ribbon.scenario, 'max_speed', 50) + 5, 120)
        _ribbon.scenario.min_speed = min_speed
        _ribbon.scenario.max_speed = max_speed
        if hasattr(_ribbon, 'speedLabel'):
            _ribbon.speedLabel.Caption = f'{min_speed}-{max_speed} km/h'


class DecSpeedHandler(RibbonButtonHandler):
    def OnClick(self):
        min_speed = max(getattr(_ribbon.scenario, 'min_speed', 20) - 5, 10)
        max_speed = max(getattr(_ribbon.scenario, 'max_speed', 50) - 5, 20)
        _ribbon.scenario.min_speed = min_speed
        _ribbon.scenario.max_speed = max_speed
        if hasattr(_ribbon, 'speedLabel'):
            _ribbon.speedLabel.Caption = f'{min_speed}-{max_speed} km/h'


class StartButtonHandler(RibbonButtonHandler):
    def OnClick(self):
        if _logProxy:
            _logProxy.logger.info('Starting traffic simulation')
        try:
            if not getattr(_ribbon.scenario, 'selected_road_names', None):
                prj = _winRoadProxy.Project
                if prj.RoadsCount > 0:
                    _ribbon.scenario.selected_road_names = [prj.Road(0).Name]

            # Spawn asynchronously
            _ribbon.scenario.start_simulation()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to start simulation: {e}")


class StopButtonHandler(RibbonButtonHandler):
    def OnClick(self):
        if _logProxy:
            _logProxy.logger.info('Stopping traffic simulation')
        try:
            _ribbon.scenario.stop_simulation()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to stop simulation: {e}")


def _apply_context_by_name(candidates):
    prj = _winRoadProxy.Project
    context = None
    cand_lower = [c.lower() for c in candidates]
    for i in range(prj.ContextsCount):
        ctx = prj.Context(i)
        name = ctx.Name.lower()
        # match if any candidate appears as substring
        if any(c in name for c in cand_lower):
            context = ctx
            break
    if context:
        _winRoadProxy.SimulationCore.ApplyContext(context)
        if _logProxy:
            _logProxy.logger.info(f"Applied context: {context.Name}")
        return True
    return False


class SetSunnyHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Setting weather to Sunny.")
            if not _apply_context_by_name(["sunny", "day", "clear"]):
                if _logProxy:
                    _logProxy.logger.info("No 'Sunny' context found. Applying weather effects directly.")
            if hasattr(_ribbon.scenario, 'apply_weather_effects'):
                _ribbon.scenario.apply_weather_effects("sunny")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to set sunny weather: {e}")


class SetRainyHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Setting weather to Rainy.")
            if not _apply_context_by_name(["rainy", "rain", "wet"]):
                if _logProxy:
                    _logProxy.logger.info("No 'Rainy' context found. Applying weather effects directly.")
            if hasattr(_ribbon.scenario, 'apply_weather_effects'):
                _ribbon.scenario.apply_weather_effects("rainy")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to set rainy weather: {e}")


class SetFoggyHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Setting weather to Foggy.")
            if not _apply_context_by_name(["foggy", "fog", "mist"]):
                if _logProxy:
                    _logProxy.logger.info("No 'Foggy' context found. Applying weather effects directly.")
            if hasattr(_ribbon.scenario, 'apply_weather_effects'):
                _ribbon.scenario.apply_weather_effects("foggy")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to set foggy weather: {e}")


class SetIcyHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Setting weather to Icy.")
            if not _apply_context_by_name(["icy", "ice", "slippery"]):
                if _logProxy:
                    _logProxy.logger.info("No 'Icy' context found. Applying weather effects directly.")
            if hasattr(_ribbon.scenario, 'apply_weather_effects'):
                _ribbon.scenario.apply_weather_effects("icy")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to set icy weather: {e}")


class SetStormyHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Setting weather to Stormy.")
            if not _apply_context_by_name(["stormy", "storm", "thunder"]):
                if _logProxy:
                    _logProxy.logger.info("No 'Stormy' context found. Applying weather effects directly.")
            if hasattr(_ribbon.scenario, 'apply_weather_effects'):
                _ribbon.scenario.apply_weather_effects("stormy")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to set stormy weather: {e}")


class ListContextsHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if _logProxy:
                _logProxy.logger.info("Listing all available contexts:")
            prj = _winRoadProxy.Project
            if prj.ContextsCount == 0:
                if _logProxy:
                    _logProxy.logger.info("No contexts found in the project.")
            else:
                for i in range(prj.ContextsCount):
                    ctx = prj.Context(i)
                    if _logProxy:
                        _logProxy.logger.info(f"Context {i}: {ctx.Name}")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to list contexts: {e}")


class OpenEnvironmentWindowHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if hasattr(_ribbon, 'env_window') and _ribbon.env_window:
                try:
                    if _ribbon.env_window.winfo_exists():
                        _ribbon.env_window.deiconify()
                        _ribbon.env_window.lift()
                    else:
                        _ribbon.create_environment_window()
                except tk.TclError:
                    _ribbon.create_environment_window()
            else:
                _ribbon.create_environment_window()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to open environment window: {e}")


class OpenTrafficAnalysisWindowHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if hasattr(_ribbon, 'analysis_window') and _ribbon.analysis_window:
                _ribbon.analysis_window.deiconify()
                _ribbon.analysis_window.lift()
            else:
                _ribbon.create_traffic_analysis_window()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to open traffic analysis window: {e}")


class OpenScenariosWindowHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if hasattr(_ribbon, 'scenarios_window') and _ribbon.scenarios_window:
                _ribbon.scenarios_window.deiconify()
                _ribbon.scenarios_window.lift()
            else:
                _ribbon.create_scenarios_window()
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to open scenarios window: {e}")


class OpenRoadSelectionWindowHandler(RibbonButtonHandler):
    def OnClick(self):
        try:
            if hasattr(_ribbon, 'road_selection_window') and _ribbon.road_selection_window:
                if _ribbon.road_selection_window.winfo_exists():
                    if _logProxy:
                        _logProxy.logger.info("Road selection window already exists")
                else:
                    if _logProxy:
                        _logProxy.logger.info("Road selection window not implemented")
            else:
                if _logProxy:
                    _logProxy.logger.info("Road selection window not implemented")
        except Exception as e:
            if _logProxy:
                _logProxy.logger.error(f"Failed to open road selection window: {e}")


