# SPDX-License-Identifier: GPL-2.0-or-later


"""
Pose Library - usage functions.
"""

from typing import Set
import re

from bpy.types import (
    Action,
    Object,
)


#def select_bones(arm_object: Object, action: Action, *, select: bool, flipped: bool, both=False) -> None:
def select_bones(arm_object: Object, action: Action, *, selected_side, toggle=True):
    pose_bone_re = re.compile(r'pose.bones\["([^"]+)"\]')
    pose = arm_object.pose

    seen_bone_names: Set[str] = set()
    bones_to_select = set()

    for fcurve in action.fcurves:
        data_path: str = fcurve.data_path
        match = pose_bone_re.match(data_path)
        if not match:
            continue

        bone_name = match.group(1)
        bone_name_flip = flip_side_name(bone_name)

        if bone_name in seen_bone_names:
            continue
        seen_bone_names.add(bone_name)
        
        if selected_side == 'FLIPPED':
            bones_to_select.add(bone_name_flip)
        elif selected_side == 'BOTH':
            bones_to_select.add(bone_name_flip)
            bones_to_select.add(bone_name)
        elif selected_side == 'CURRENT':
            bones_to_select.add(bone_name)
        

    for bone in bones_to_select:
        pose_bone = pose.bones.get(bone)
        if pose_bone:
            if toggle:
                pose_bone.bone.select = not pose_bone.bone.select
            else:
                pose_bone.bone.select = True


_FLIP_SEPARATORS = set(". -_")

# These are single-character replacements, others are handled differently.
_FLIP_REPLACEMENTS = {
    "l": "r",
    "L": "R",
    "r": "l",
    "R": "L",
}


def flip_side_name(to_flip: str) -> str:
    """Flip left and right indicators in the name.

    Basically a Python implementation of BLI_string_flip_side_name.

    >>> flip_side_name('bone_L.004')
    'bone_R.004'
    >>> flip_side_name('left_bone')
    'right_bone'
    >>> flip_side_name('Left_bone')
    'Right_bone'
    >>> flip_side_name('LEFT_bone')
    'RIGHT_bone'
    >>> flip_side_name('some.bone-RIGHT.004')
    'some.bone-LEFT.004'
    >>> flip_side_name('some.bone-right.004')
    'some.bone-left.004'
    >>> flip_side_name('some.bone-Right.004')
    'some.bone-Left.004'
    >>> flip_side_name('some.bone-LEFT.004')
    'some.bone-RIGHT.004'
    >>> flip_side_name('some.bone-left.004')
    'some.bone-right.004'
    >>> flip_side_name('some.bone-Left.004')
    'some.bone-Right.004'
    >>> flip_side_name('.004')
    '.004'
    >>> flip_side_name('L.004')
    'R.004'
    """
    import string

    if len(to_flip) < 3:
        # we don't flip names like .R or .L
        return to_flip

    # We first check the case with a .### extension, let's find the last period.
    number = ""
    replace = to_flip
    if to_flip[-1] in string.digits:
        try:
            index = to_flip.rindex(".")
        except ValueError:
            pass
        else:
            if to_flip[index + 1] in string.digits:
                # TODO(Sybren): this doesn't handle "bone.1abc2" correctly.
                number = to_flip[index:]
                replace = to_flip[:index]

    if not replace:
        # Nothing left after the number, so no flips necessary.
        return replace + number

    if len(replace) == 1:
        replace = _FLIP_REPLACEMENTS.get(replace, replace)
        return replace + number

    # First case; separator . - _ with extensions r R l L.
    if replace[-2] in _FLIP_SEPARATORS and replace[-1] in _FLIP_REPLACEMENTS:
        replace = replace[:-1] + _FLIP_REPLACEMENTS[replace[-1]]
        return replace + number

    # Second case; beginning with r R l L, with separator after it.
    if replace[1] in _FLIP_SEPARATORS and replace[0] in _FLIP_REPLACEMENTS:
        replace = _FLIP_REPLACEMENTS[replace[0]] + replace[1:]
        return replace + number

    lower = replace.lower()
    prefix = suffix = ""
    if lower.startswith("right"):
        bit = replace[0:2]
        if bit == "Ri":
            prefix = "Left"
        elif bit == "RI":
            prefix = "LEFT"
        else:
            prefix = "left"
        replace = replace[5:]
    elif lower.startswith("left"):
        bit = replace[0:2]
        if bit == "Le":
            prefix = "Right"
        elif bit == "LE":
            prefix = "RIGHT"
        else:
            prefix = "right"
        replace = replace[4:]
    elif lower.endswith("right"):
        bit = replace[-5:-3]
        if bit == "Ri":
            suffix = "Left"
        elif bit == "RI":
            suffix = "LEFT"
        else:
            suffix = "left"
        replace = replace[:-5]
    elif lower.endswith("left"):
        bit = replace[-4:-2]
        if bit == "Le":
            suffix = "Right"
        elif bit == "LE":
            suffix = "RIGHT"
        else:
            suffix = "right"
        replace = replace[:-4]

    return prefix + replace + suffix + number


if __name__ == '__main__':
    import doctest

    print(f"Test result: {doctest.testmod()}")
