"""Addon preferences that are baked into Blender preferences."""

import os
import platform
from pathlib import Path

import bpy
from bpy.props import (
    BoolProperty,
    EnumProperty,
    FloatProperty,
    IntProperty,
    StringProperty,
)
from bpy.types import Context

from rizomuvbridge_ultimate import constants
from rizomuvbridge_ultimate.functions.utils.path_utils import (
    get_latest_rizom_location,
    remove_from_path,
    set_rizomlink_availability,
    setup_data_directory,
)
from rizomuvbridge_ultimate.resources.tooltips import TOOLTIPS


def map_res_get(self) -> float:
    """RizomUV map resolution property getter.

    Returns
    -------
        Current value of the map resolution property in Px.

    """
    return self.get("map_res", 2048)


def map_res_set(self, value: int) -> None:
    """RizomUV map resolution property setter.

    Updates the margin and padding as these values are relative to the map resolution

    Args:
    ----
        value: New value of the map resolution property in Px.

    """
    old_value = self.get("map_res", 2048)
    self.margin = self.margin * (1 / old_value) * value
    self.padding = self.padding * (1 / old_value) * value

    self["map_res"] = value


def update_addon_tab(self: "RizomUVBridgePreferences", context: Context) -> None:
    """Update the bl_category field of the addon UI, changes the UI tab it docks to."""
    if not self.addon_tab_name:
        self.addon_tab_name = "RizomUV"

    for p_id in constants.PANEL_IDS:
        cls = getattr(bpy.types, p_id)
        if cls.is_registered:
            bpy.utils.unregister_class(cls)
            cls.bl_category = self.addon_tab_name
            bpy.utils.register_class(cls)


def set_data_path() -> str:
    """Set the data path for the bridge data.

    Crucial that it is in a directory with write/read permissions.
    For Windows it goes in AppData/RizomUV Bridge.
    For macOS it stays inside the addon folder as I am not familiar
    with the file system.

    Don't put it in addon folder for Windows as if the user is using
    a windows store version of Blender it wont have write permission.

    Returns
    -------
        Path to the data directory.

    """
    if platform.system() == "Windows":
        setup_data_directory()
    return str(constants.DATA_PATH)


def default_rizom_path() -> str:
    """Attempt to locate the latest version of RizomUV installed on the system.

    Windows only, macOS users must manually browse for it.

    Returns
    -------
        Path to the RizomUV executable or an empty string if it could not be found

    """
    if platform.system() == "Windows":
        path = get_latest_rizom_location()
        if not path:
            return ""

        rizom_exe = Path(path) / "rizomuv.exe"
        if rizom_exe.is_file():
            return str(rizom_exe)

    return ""


def adjust_rizom_path_for_mac(self, context) -> None:
    """Adjust the RizomUV path for MacOS.

    Needed because of weird macOS filebrowser choice by Blender
    https://projects.blender.org/blender/blender/issues/82510

    Need the path to the actual executable, not just the app.

    """
    if platform.system() == "Darwin" and self["rizom_path"].endswith(".app"):
        filename = os.path.basename(self["rizom_path"])
        basename, _ = os.path.splitext(filename)
        self["rizom_path"] = str(Path(self["rizom_path"]) / "Contents" / "MacOS" / basename)


def set_rizom_path(self: "RizomUVBridgePreferences", value: str):
    """rizom_path setter.

    Clears up old PATH entries when the user changes Rizom path,
    this avoid cases where the user could have the working library
    still on path but using the wrong rizom executable.

    """
    if platform.system() != "Windows":
        self["livelink_allowed"] = False
    else:
        old_path = self.get("rizom_path", "")
        old_link_dir = Path(old_path).parent / "RizomUVLink"
        remove_from_path(str(old_link_dir))
        set_rizomlink_availability(self, Path(value))

    self["rizom_path"] = value


def get_rizom_path(self: "RizomUVBridgePreferences"):
    """rizom_path getter."""
    return self.get("rizom_path", default_rizom_path())


def set_texture_path(self: "RizomUVBridgePreferences", value: str):
    """texture_path setter."""
    _, extension = os.path.splitext(value)

    if extension.lower() not in constants.TEXTURE_FORMATS:
        value = ""

    self["texture_path"] = value


def get_texture_path(self: "RizomUVBridgePreferences"):
    """texture_path getter."""
    return self.get("texture_path", "")


def livelink_allowed_callback(self: "RizomUVBridgePreferences", context):
    """livelink_allowed update callback.

    Used to sync the user visible preference with the internal one.
    This stops the user from overriding and trying to enable the link
    when it is not possible to do so.

    Livelink only available when my checks say it is.

    Allow the option for user to disable the livelink if they
    dont want to use it.

    """
    if not self.livelink_allowed:
        self["attempt_livelink"] = False


def update_livelink_callback(self: "RizomUVBridgePreferences", context):
    """livelink_allowed update callback."""
    if self.attempt_livelink:
        # This is hidden when using livelink so set option false
        self["use_seams"] = False


class RizomUVBridgePreferences(bpy.types.AddonPreferences):
    """Addon preferences."""

    bl_idname = constants.PACKAGE_NAME

    rizom_path: StringProperty(
        name="Rizom Executable",
        subtype="FILE_PATH",
        default=default_rizom_path(),
        get=get_rizom_path,
        set=set_rizom_path,
        update=adjust_rizom_path_for_mac,
        description=TOOLTIPS["rizom_path"],
    )
    data_path: StringProperty(
        name="Data Path",
        subtype="DIR_PATH",
        default=set_data_path(),
        description=TOOLTIPS["data_path"],
    )

    addon_tab_name: StringProperty(
        default="RizomUV",
        name="Addon Tab",
        update=update_addon_tab,
        description=TOOLTIPS["addon_tab_name"],
    )
    livelink_allowed: BoolProperty(
        name="Allow Livelink",
        update=livelink_allowed_callback,
        default=True,
        description=TOOLTIPS["livelink_allowed"],
    )
    attempt_livelink: BoolProperty(
        name="Attempt Livelink",
        default=True,
        description=TOOLTIPS["attempt_livelink"],
        update=update_livelink_callback,
    )

    remote_actions: EnumProperty(
        name="Remote Actions",
        description=TOOLTIPS["remote_actions"],
        items=list(constants.CUSTOM_ACTION_ENUMS.values())
        + list(constants.AUTOSEAMS_ENUMS.values()),
    )

    export_actions: EnumProperty(
        name="Export Actions",
        description=TOOLTIPS["export_actions"],
        items=[
            *list(constants.EXPORT_ENUMS.values()),
            constants.CUSTOM_ACTION_ENUMS["RESET_UVS"],
            *list(constants.AUTOSEAMS_ENUMS.values()),
        ],
    )
    confirm_export: BoolProperty(
        name="Confirm Export", default=False, description=TOOLTIPS["confirm_export"]
    )
    apply_modifiers: BoolProperty(name="Apply Modifiers", description=TOOLTIPS["apply_modifiers"])
    apply_to_all_uvmaps_action: BoolProperty(
        name="Apply To All UVMaps", description=TOOLTIPS["apply_to_all_uvmaps_action"]
    )
    exclude_clones: BoolProperty(
        name="Exclude Clones", default=True, description=TOOLTIPS["exclude_clones"]
    )
    export_format: EnumProperty(
        name="Export Format",
        items=(
            ("FBX", "FBX File", ""),
            ("OBJ", "OBJ File", ""),
            ("USD", "USD File", ""),
        ),
        description=TOOLTIPS["export_format"],
        default="FBX",
    )
    apply_rizom_settings: BoolProperty(
        name="Apply Rizom Settings", description=TOOLTIPS["apply_rizom_settings"]
    )
    mark_seams: BoolProperty(name="Mark Seams", description=TOOLTIPS["mark_seams"])
    mark_sharp: BoolProperty(name="Mark Sharp", description=TOOLTIPS["mark_sharp"])
    replace_objects: BoolProperty(name="Replace Objects", description=TOOLTIPS["replace_objects"])
    reveal_hidden: BoolProperty(name="Reveal Hidden", description=TOOLTIPS["reveal_hidden"])
    delete_uvsets: BoolProperty(
        name="Delete Extra UVSets", default=False, description=TOOLTIPS["delete_uvsets"]
    )
    add_uvsets: BoolProperty(
        name="Add Missing UVSets", default=True, description=TOOLTIPS["add_uvsets"]
    )
    triangulate: BoolProperty(name="Triangulate", description=TOOLTIPS["triangulate"])
    use_seams: BoolProperty(name="Use Seams", description=TOOLTIPS["use_seams"])
    fix_names: BoolProperty(name="Fix Names", description=TOOLTIPS["fix_names"])

    # Rizom settings
    margin: FloatProperty(
        name="Margin",
        default=8.0,
        subtype="PIXEL",
        soft_max=32.0,
        min=0.0,
        step=1.0,
        description=TOOLTIPS["margin"],
    )
    padding: FloatProperty(
        name="Padding",
        default=16.0,
        subtype="PIXEL",
        soft_max=32.0,
        min=0.0,
        step=1.0,
        description=TOOLTIPS["padding"],
    )

    map_res: IntProperty(
        name="Map Resolution",
        min=1,
        default=2048,
        subtype="PIXEL",
        get=map_res_get,
        set=map_res_set,
        description=TOOLTIPS["map_res"],
    )
    td_unit: EnumProperty(
        name="Texel Density Unit",
        description=TOOLTIPS["td_unit"],
        items=(
            ("km", "tx/km", ""),
            ("m", "tx/m", ""),
            ("dm", "tx/dm", ""),
            ("cm", "tx/cm", ""),
            ("mm", "tx/mm", ""),
            ("in", "tx/in", ""),
            ("ft", "tx/ft", ""),
            ("yd", "tx/yd", ""),
            ("mi", "tx/mi", ""),
        ),
        default="cm",
    )
    td_target: FloatProperty(
        name="Texel Density Target",
        description=TOOLTIPS["td_target"],
        default=10.24,
        min=0.0,
        subtype="PIXEL",
    )

    prevent_flips: BoolProperty(
        name="Prevent Flips",
        default=True,
        description=TOOLTIPS["prevent_flips"],
    )

    prevent_overlaps: BoolProperty(
        name="Prevent Overlaps",
        default=True,
        description=TOOLTIPS["prevent_overlaps"],
    )

    fill_holes: BoolProperty(
        name="Fill Holes",
        default=False,
        description=TOOLTIPS["fill_holes"],
    )

    accuracy: IntProperty(
        name="Packing Accuracy",
        default=256,
        max=2048,
        min=128,
        description=TOOLTIPS["accuracy"],
    )
    iterations: IntProperty(
        name="Iterations",
        default=1,
        min=1,
        max=128,
        description=TOOLTIPS["iterations"],
    )

    orientation: EnumProperty(
        name="Orientation",
        description=TOOLTIPS["orientation"],
        items=(
            ("0", "No pre-orientation", "Don't pre-orient"),
            ("1", "Horizontal pre-orientation", "Pre-orient islands horizontally"),
            ("2", "Vertical pre-orientation", "Pre-orient islands vertically"),
            (
                "3",
                "X Axis pre-orientation",
                "Pre-orient islands so 3D X axis aligns with UV V axis",
            ),
            (
                "4",
                "Y Axis pre-orientation",
                "Pre-orient islands so 3D Y axis aligns with UV V axis",
            ),
            (
                "5",
                "Y Axis pre-orientation",
                "Pre-orient islands so 3D Z axis aligns with UV V axis",
            ),
        ),
        default="0",
    )
    step_angle: IntProperty(
        name="Step Angle",
        description=TOOLTIPS["step_angle"],
        subtype="ANGLE",
        min=0,
        max=180,
    )
    # endregion

    # region Actions settings.

    weld_before_autoseams: BoolProperty(
        name="Weld Before Autoseams",
        default=True,
        description=TOOLTIPS["weld_before_autoseams"],
    )
    stretch_quality: FloatProperty(
        name="Stretch Limiter",
        default=0.25,
        min=0,
        max=0.99,
        step=0.1,
        description=TOOLTIPS["stretch_quality"],
    )
    link_gaps: BoolProperty(name="Link Gaps", description=TOOLTIPS["link_gaps"])
    open_cylinders: BoolProperty(name="Open Cylinders", description=TOOLTIPS["open_cylinders"])
    cut_handles: BoolProperty(name="Cut Handles", description=TOOLTIPS["cut_handles"])
    sharp_angle: IntProperty(
        name="Edge Angle",
        default=70,
        min=0,
        max=180,
        description=TOOLTIPS["sharp_angle"],
    )
    mosaic_force: FloatProperty(
        name="Force",
        default=0.5,
        step=0.05,
        min=0.01,
        max=0.99,
        description=TOOLTIPS["mosaic_force"],
    )
    pelt_trunk: BoolProperty(name="Tree", description=TOOLTIPS["pelt_trunk"])
    pelt_branch: BoolProperty(name="Branch", description=TOOLTIPS["pelt_branch"])
    pelt_leaf: BoolProperty(name="Leaf", description=TOOLTIPS["pelt_leaf"])

    texture_path: StringProperty(
        name="Texture Path",
        subtype="FILE_PATH",
        default="",
        get=get_texture_path,
        set=set_texture_path,
        description=TOOLTIPS["texture_path"],
    )
