from __future__ import annotations
import placement
import services
import sims4
from element_utils import build_critical_section_with_finally
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from typing import *
    from objects.game_object import GameObject
logger = sims4.log.Logger('Interaction Utils')

class InteractionRetargetHandler:

    def __init__(self, interaction, target):
        self._target = target
        self._old_target = interaction.target
        self._interaction = interaction

    def begin(self, _):
        self._interaction.set_target(self._target)

    def end(self, _):
        self._interaction.set_target(self._old_target)


def retarget_interaction(interaction, target, *args):
    if interaction is not None:
        interaction_retarget_handler = InteractionRetargetHandler(interaction, target)
        return build_critical_section_with_finally(interaction_retarget_handler.begin, args, interaction_retarget_handler.end)
    return args


class InteractionUtils:

    @staticmethod
    def do_put_near(subject:'GameObject', target:'GameObject', fallback_to_spawn_point:'bool', use_fgl:'bool', offset:'sims4.math.Transform'=None) -> 'None':
        if subject is None or target is None:
            logger.error('Trying to run a PutNear basic extra with a None Subject and/or Target. subject:{}, target:{}', subject, target, owner='trevor')
            return
        _target = target
        if _target.is_in_inventory():
            _target = _target.inventoryitem_component.get_root_owner()[0]
        target_location = _target.location
        target_surface = _target.routing_surface
        if offset is not None:
            target_location = sims4.math.Location(sims4.math.Transform.concatenate(offset, target_location.transform), target_surface)
        if use_fgl:
            starting_location = placement.create_starting_location(location=target_location)
            if subject.is_sim:
                fgl_context = placement.create_fgl_context_for_sim(starting_location, subject)
            else:
                fgl_context = placement.create_fgl_context_for_object(starting_location, subject)
            (translation, orientation, _) = fgl_context.find_good_location()
            if fallback_to_spawn_point:
                zone = services.current_zone()
                fallback_point = zone.get_spawn_point(lot_id=zone.lot.lot_id)
                (translation, orientation) = fallback_point.next_spawn_spot()
                target_surface = fallback_point.routing_surface
        else:
            transform = target_location.transform
            translation = transform.translation
            orientation = transform.orientation
        if translation is None:
            return
        inventoryitem_component = subject.inventoryitem_component
        if inventoryitem_component is not None and subject.is_in_inventory():
            inventory_owner = inventoryitem_component.last_inventory_owner
            if not inventory_owner.inventory_component.try_remove_object_by_id(subject.id):
                logger.error("Failed to remove object from {}'s inventory when running PutNear basic extra on {}", inventory_owner, subject, owner='skorman')
                return
        subject.move_to(translation=translation, orientation=orientation or subject.orientation, routing_surface=target_surface, parent=None, joint_name_or_hash=None, slot_hash=0)

