try:
    import Xlib.X
    import Xlib.protocol.event
except ImportError:
    pass


class TargetEngine:
    Primary = -1
    All = -3


class EngineAction:
    def __init__(self, target_engine_index):
        self._target_engine_index = target_engine_index

    def execute_on_window(self, extended_controls, xkeyevent, engine_window):
        raise Exception("abstract method")

    def execute(self, extended_controls, xkeyevent):
        if self._target_engine_index == TargetEngine.Primary:
            target_windows = [extended_controls.primary_engine_window]
        elif self._target_engine_index == TargetEngine.All:
            target_windows = extended_controls.engine_windows
        else:
            target_windows = extended_controls.engine_windows_by_target_index.get(
                self._target_engine_index, [],
            )
        for target_window in target_windows:
            self.execute_on_window(extended_controls, xkeyevent, target_window)


class CenterClickAction(EngineAction):
    def __init__(self, target_engine_index, factor_x, factor_y):
        super().__init__(target_engine_index=target_engine_index,)
        self._button = Xlib.X.Button1
        self._factor_x = factor_x
        self._factor_y = factor_y

    def execute_on_window(self, extended_controls, xkeyevent, engine_window):
        engine_geometry = engine_window.get_geometry()
        smaller_dimension = min(engine_geometry.width, engine_geometry.height)
        attr = dict(
            window=engine_window,
            detail=self._button,
            state=xkeyevent.state,
            event_x=int(engine_geometry.width / 2 + smaller_dimension * self._factor_x),
            event_y=int(
                engine_geometry.height / 2 + smaller_dimension * self._factor_y
            ),
            # apparently root_x & root_y do not need to correspond with event_x/y.
            # attributes are still required to be set.
            root_x=0,  # xkeyevent.root_x,
            root_y=0,  # xkeyevent.root_y,
            child=xkeyevent.child,
            root=xkeyevent.root,
            time=xkeyevent.time,  # X.CurrentTime
            same_screen=xkeyevent.same_screen,
        )
        if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
            e = Xlib.protocol.event.ButtonPress(**attr)
        else:
            e = Xlib.protocol.event.ButtonRelease(**attr)
        engine_window.send_event(e)


class SelectGagAction(CenterClickAction):

    X_OFFSET = -0.286
    X_FACTOR = 0.081

    def __init__(self, target_engine_index, column_index, factor_y):
        super().__init__(
            target_engine_index=target_engine_index,
            factor_x=self.X_OFFSET + self.X_FACTOR * column_index,
            factor_y=factor_y,
        )


class RewriteKeyEventAction(EngineAction):
    def __init__(self, target_engine_index, keysym=None):
        super().__init__(target_engine_index=target_engine_index,)
        self._keysym = keysym

    def execute_on_window(self, extended_controls, xkeyevent, engine_window):
        engine_window.send_event(
            type(xkeyevent)(
                window=engine_window,
                detail=extended_controls.xdisplay.keysym_to_keycode(self._keysym)
                if self._keysym
                else xkeyevent.detail,
                state=xkeyevent.state,
                root_x=xkeyevent.root_x,
                root_y=xkeyevent.root_y,
                event_x=xkeyevent.event_x,
                event_y=xkeyevent.event_y,
                child=xkeyevent.child,
                root=xkeyevent.root,
                time=xkeyevent.time,  # X.CurrentTime
                same_screen=xkeyevent.same_screen,
            )
        )


class ForwardKeyEventAction(RewriteKeyEventAction):
    def __init__(self):
        super().__init__(
            keysym=None, target_engine_index=TargetEngine.Primary,
        )


class ToggleExtendedControlsAction:
    def execute(self, extended_controls, xkeyevent):
        if isinstance(xkeyevent, Xlib.protocol.event.KeyPress):
            extended_controls.toggle()
