"""Built-in "Rotate" action."""

import math

import gi

gi.require_version('Gimp', '3.0')
from gi.repository import Gimp

from src import utils
from src.procedure_groups import *

from . import _utils as builtin_actions_utils


__all__ = [
  'Angles',
  'AngleUnits',
  'UNIT_DEGREE',
  'UNIT_RADIAN',
  'UNITS',
  'rotate',
  'on_after_add_rotate_action',
]


class Angles:
  ANGLES = (
    DEGREES_90,
    DEGREES_180,
    DEGREES_270,
    CUSTOM,
  ) = (
    'degrees_90',
    'degrees_180',
    'degrees_270',
    'custom',
  )


class AngleUnits:
  ANGLE_UNITS = (
    DEGREE,
    RADIAN
  ) = (
    'degree',
    'radian'
  )


class _AngleUnit:

  def __init__(self, name, display_name, scaling_factor):
    self._name = name
    self._display_name = display_name
    self._scaling_factor = scaling_factor

  @property
  def name(self):
    return self._name

  @property
  def display_name(self):
    return self._display_name

  @property
  def scaling_factor(self):
    return self._scaling_factor


UNIT_DEGREE = _AngleUnit('degree', _('degrees'), 180 / math.pi)
UNIT_RADIAN = _AngleUnit('radian', _('radians'), 1.0)

UNITS = {
  UNIT_DEGREE.name: UNIT_DEGREE,
  UNIT_RADIAN.name: UNIT_RADIAN,
}


def rotate(
      batcher,
      object_to_rotate,
      angle,
      custom_angle,
      transform_resize,
      interpolation,
      rotate_around_center,
      center_x,
      center_y,
):
  Gimp.context_push()
  Gimp.context_set_transform_resize(transform_resize)
  Gimp.context_set_interpolation(interpolation)
  Gimp.context_set_transform_direction(Gimp.TransformDirection.FORWARD)

  center_x_pixels = builtin_actions_utils.unit_to_pixels(
    batcher,
    center_x,
    'x',
  )
  center_y_pixels = builtin_actions_utils.unit_to_pixels(
    batcher,
    center_y,
    'y',
  )

  if isinstance(object_to_rotate, Gimp.Image):
    if angle == Angles.DEGREES_90:
      object_to_rotate.rotate(Gimp.RotationType.DEGREES90)
    elif angle == Angles.DEGREES_180:
      object_to_rotate.rotate(Gimp.RotationType.DEGREES180)
    elif angle == Angles.DEGREES_270:
      object_to_rotate.rotate(Gimp.RotationType.DEGREES270)
    elif angle == Angles.CUSTOM:
      image_center_x = object_to_rotate.get_width() / 2
      image_center_y = object_to_rotate.get_width() / 2

      for layer in object_to_rotate.get_layers():
        layer.transform_rotate(
          _angle_to_radians(custom_angle),
          False,
          image_center_x,
          image_center_y,
        )
  else:
    if angle == Angles.DEGREES_90:
      object_to_rotate.transform_rotate_simple(
        Gimp.RotationType.DEGREES90,
        rotate_around_center,
        center_x_pixels,
        center_y_pixels,
      )
    elif angle == Angles.DEGREES_180:
      object_to_rotate.transform_rotate_simple(
        Gimp.RotationType.DEGREES180,
        rotate_around_center,
        center_x_pixels,
        center_y_pixels,
      )
    elif angle == Angles.DEGREES_270:
      object_to_rotate.transform_rotate_simple(
        Gimp.RotationType.DEGREES270,
        rotate_around_center,
        center_x_pixels,
        center_y_pixels,
      )
    elif angle == Angles.CUSTOM:
      object_to_rotate.transform_rotate(
        _angle_to_radians(custom_angle),
        rotate_around_center,
        center_x_pixels,
        center_y_pixels,
      )

  Gimp.context_pop()


def _angle_to_radians(angle):
  scaling_factor = UNITS[angle['unit']].scaling_factor

  if scaling_factor != 0.0:
    return angle['value'] / scaling_factor
  else:
    return angle['value']


def on_after_add_rotate_action(_actions, action, _orig_action_dict):
  if action['orig_name'].value.startswith('rotate_for_'):
    _set_visible_for_custom_angle(
      action['arguments/angle'],
      action['arguments/custom_angle'],
    )

    action['arguments/angle'].connect_event(
      'value-changed',
      _set_visible_for_custom_angle,
      action['arguments/custom_angle'],
    )

    _set_value_and_visible_for_rotation_center_settings(
      action['arguments/object_to_rotate'],
      action['arguments/rotate_around_center'],
      action['arguments/center_x'],
      action['arguments/center_y'],
    )

    action['arguments/object_to_rotate'].connect_event(
      'value-changed',
      _set_value_and_visible_for_rotation_center_settings,
      action['arguments/rotate_around_center'],
      action['arguments/center_x'],
      action['arguments/center_y'],
    )

    _set_visible_for_center_x_y(
      action['arguments/rotate_around_center'],
      action['arguments/object_to_rotate'],
      action['arguments/center_x'],
      action['arguments/center_y'],
    )

    action['arguments/rotate_around_center'].connect_event(
      'value-changed',
      _set_visible_for_center_x_y,
      action['arguments/object_to_rotate'],
      action['arguments/center_x'],
      action['arguments/center_y'],
    )


def _set_visible_for_custom_angle(angle_setting, custom_angle_setting):
  custom_angle_setting.gui.set_visible(angle_setting.value == 'custom')


def _set_value_and_visible_for_rotation_center_settings(
      object_to_rotate_setting,
      rotate_around_center_setting,
      center_x_setting,
      center_y_setting,
):
  is_image = object_to_rotate_setting.value == 'current_image'

  rotate_around_center_setting.gui.set_visible(not is_image)
  center_x_setting.gui.set_visible(not (is_image or rotate_around_center_setting.value))
  center_y_setting.gui.set_visible(not (is_image or rotate_around_center_setting.value))


def _set_visible_for_center_x_y(
      rotate_around_center_setting,
      object_to_rotate_setting,
      center_x_setting,
      center_y_setting,
):
  is_image = object_to_rotate_setting.value == 'current_image'

  center_x_setting.gui.set_visible(not (is_image or rotate_around_center_setting.value))
  center_y_setting.gui.set_visible(not (is_image or rotate_around_center_setting.value))


ROTATE_FOR_IMAGES_DICT = {
  'name': 'rotate_for_images',
  'function': rotate,
  'display_name': _('Rotate'),
  'menu_path': _('Resize and Transform'),
  'display_options_on_create': True,
  'additional_tags': [CONVERT_GROUP, EDIT_AND_SAVE_IMAGES_GROUP, EXPORT_IMAGES_GROUP],
  'arguments': [
    {
      'type': 'placeholder_image_or_layer',
      'name': 'object_to_rotate',
      'default_value': 'current_image',
      'display_name': _('Apply to (image or layer):'),
    },
    {
      'type': 'choice',
      'name': 'angle',
      'default_value': Angles.DEGREES_90,
      'items': [
        (Angles.DEGREES_90, _('90° (clockwise)')),
        (Angles.DEGREES_180, _('180°')),
        (Angles.DEGREES_270, _('270° (counter-clockwise)')),
        (Angles.CUSTOM, _('Custom')),
      ],
      'display_name': _('Angle'),
    },
    {
      'type': 'angle',
      'name': 'custom_angle',
      'default_value': {
        'value': 0.0,
        'unit': 'degree',
      },
      'display_name': _('Custom angle'),
    },
    {
      'type': 'enum',
      'enum_type': Gimp.TransformResize,
      'name': 'transform_resize',
      'default_value': Gimp.TransformResize.ADJUST,
      'display_name': _('How to handle boundaries'),
    },
    {
      'type': 'enum',
      'enum_type': Gimp.InterpolationType,
      'name': 'interpolation',
      'default_value': Gimp.InterpolationType.NONE,
      'display_name': _('Interpolation'),
    },
    {
      'type': 'bool',
      'name': 'rotate_around_center',
      'default_value': True,
      'display_name': _('Rotate around the center'),
    },
    {
      'type': 'dimension',
      'name': 'center_x',
      'default_value': {
        'pixel_value': 0.0,
        'percent_value': 0.0,
        'other_value': 0.0,
        'unit': Gimp.Unit.pixel(),
        'percent_object': 'current_image',
        'percent_property': {
          ('current_image',): 'width',
          ('current_layer', 'background_layer', 'foreground_layer'): 'width',
        },
      },
      'percent_placeholder_names': [
        'current_image', 'current_layer', 'background_layer', 'foreground_layer'],
      'display_name': _('Horizontal position of center'),
    },
    {
      'type': 'dimension',
      'name': 'center_y',
      'default_value': {
        'pixel_value': 0.0,
        'percent_value': 0.0,
        'other_value': 0.0,
        'unit': Gimp.Unit.pixel(),
        'percent_object': 'current_image',
        'percent_property': {
          ('current_image',): 'height',
          ('current_layer', 'background_layer', 'foreground_layer'): 'height',
        },
      },
      'percent_placeholder_names': [
        'current_image', 'current_layer', 'background_layer', 'foreground_layer'],
      'display_name': _('Vertical position of center'),
    },
  ]
}

ROTATE_FOR_LAYERS_DICT = utils.semi_deep_copy(ROTATE_FOR_IMAGES_DICT)
ROTATE_FOR_LAYERS_DICT.update({
  'name': 'rotate_for_layers',
  'additional_tags': [EXPORT_LAYERS_GROUP, EDIT_LAYERS_GROUP],
})
ROTATE_FOR_LAYERS_DICT['arguments'][0]['default_value'] = 'current_layer'
