from copy import deepcopy
import numpy as np

import pygarment as pyg

from assets.garment_programs.base_classes import BaseBodicePanel
from assets.garment_programs import sleeves
from assets.garment_programs import collars
from assets.garment_programs import tee
from scipy.spatial.transform import Rotation as R

class BodiceFrontHalf(BaseBodicePanel):
    def __init__(self, name, body, design) -> None:
        super().__init__(name, body, design)

        m_bust = body['bust']
        m_waist = body['waist']

        # sizes   
        bust_point = body['bust_points'] / 2
        front_frac = (body['bust'] - body['back_width']) / 2 / body['bust'] 

        self.width = front_frac * m_bust
        waist = (m_waist - body['waist_back_width']) / 2
        sh_tan = np.tan(np.deg2rad(body['_shoulder_incl']))
        shoulder_incl = sh_tan * self.width
        bottom_d_width = (self.width - waist) * 2 / 3

        adjustment = sh_tan * (self.width - body['shoulder_w'] / 2)
        max_len = body['waist_over_bust_line'] - adjustment

        # side length is adjusted due to shoulder inclination
        # for the correct sleeve fitting
        fb_diff = (front_frac - (0.5 - front_frac)) * body['bust']
        back_adjustment = sh_tan * (body['back_width'] / 2 - body['shoulder_w'] / 2)
        side_len = body['waist_line'] - back_adjustment - sh_tan * fb_diff 

        self.edges.append(pyg.EdgeSeqFactory.from_verts(
            [0, 0], 
            [-self.width, 0],
            [-self.width, max_len], 
            [0, max_len + shoulder_incl]
        ))
        self.edges.close_loop()

        # Side dart
        bust_line = body['waist_line'] - body['_bust_line']
        side_d_depth = 0.75 * (self.width - bust_point)    # NOTE: calculated value 
        side_d_width = max_len - side_len
        s_edge, side_interface = self.add_dart(
            pyg.EdgeSeqFactory.dart_shape(side_d_width, side_d_depth),
            self.edges[1], 
            offset=bust_line + side_d_width / 2)
        self.edges.substitute(1, s_edge)

        # Take some fabric from the top to match the shoulder width
        s_edge[-1].end[0] += (x_upd:=self.width - body['shoulder_w'] / 2)
        s_edge[-1].end[1] += (sh_tan * x_upd)

        # Bottom dart
        b_edge, b_interface = self.add_dart(
            pyg.EdgeSeqFactory.dart_shape(bottom_d_width, 0.9 * bust_line),
            self.edges[0], 
            offset=bust_point + bottom_d_width / 2
        )
        self.edges.substitute(0, b_edge)
        # Take some fabric from side in the bottom (!: after side dart insertion)
        b_edge[-1].end[0] = - (waist + bottom_d_width) 

        # Interfaces
        self.interfaces = {
            'outside':  pyg.Interface(self, side_interface),   # side_interface,    # pyp.Interface(self, [side_interface]),  #, self.edges[-3]]),
            'inside': pyg.Interface(self, self.edges[-1]),
            'shoulder': pyg.Interface(self, self.edges[-2]),
            'bottom': pyg.Interface(self, b_interface),

            # Reference to the corner for sleeve and collar projections
            'shoulder_corner': pyg.Interface(
                self, [self.edges[-3], self.edges[-2]]),
            'collar_corner': pyg.Interface(
                self, [self.edges[-2], self.edges[-1]])
        }
  
        # default placement
        self.translate_by([0, body['height'] - body['head_l'] - max_len - shoulder_incl, 0])


class BodiceBackHalf(BaseBodicePanel):
    """Panel for the back of basic fitted bodice block"""

    def __init__(self, name, body, design) -> None:
        super().__init__(name, body, design)

        # Overall measurements        
        self.width = body['back_width'] / 2
        waist = body['waist_back_width'] / 2  
        # NOTE: no inclination on the side, since there is not much to begin with
        waist_width = self.width if waist < self.width else waist  
        shoulder_incl = (sh_tan:=np.tan(np.deg2rad(body['_shoulder_incl']))) * self.width

        # Adjust to make sure length is measured from the shoulder
        # and not the de-fact side of the garment
        back_adjustment = sh_tan * (self.width - body['shoulder_w'] / 2)
        length = body['waist_line'] - back_adjustment

        # Base edge loop
        edge_0 = pyg.CurveEdgeFactory.curve_from_tangents(
            start=[0, shoulder_incl / 4],  # back a little shorter 
            end=[-waist_width, 0],
            target_tan0=[-1, 0]
        )
        self.edges.append(edge_0)
        self.edges.append(pyg.EdgeSeqFactory.from_verts(
            edge_0.end,
            [-self.width, body['waist_line'] - body['_bust_line']],  # from the bottom
            [-self.width, length],   
            [0, length + shoulder_incl],   # Add some fabric for the neck (inclination of shoulders)
        ))
        self.edges.close_loop()
        
        # Take some fabric from the top to match the shoulder width
        self.interfaces = {
            'outside': pyg.Interface(
                self, [self.edges[1], self.edges[2]]),
            'inside': pyg.Interface(self, self.edges[-1]),
            'shoulder': pyg.Interface(self, self.edges[-2]),
            'bottom': pyg.Interface(self, self.edges[0]),
            # Reference to the corners for sleeve and collar projections
            'shoulder_corner': pyg.Interface(
                self, pyg.EdgeSequence(self.edges[-3], self.edges[-2])),
            'collar_corner': pyg.Interface(
                self, pyg.EdgeSequence(self.edges[-2], self.edges[-1]))
        }

        # Bottom dart as cutout -- for straight line
        if waist < self.get_width(self.edges[2].end[1] - self.edges[2].start[1]):
            w_diff = waist_width - waist
            side_adj = 0  if w_diff < 4 else w_diff / 6  # NOTE: don't take from sides if the difference is too small
            bottom_d_width = w_diff - side_adj
            bottom_d_width /= 2   # double darts
            bottom_d_depth = 1. * (length - body['_bust_line'])  # calculated value
            bottom_d_position = body['bum_points'] / 2

            # TODOLOW Avoid hardcoding for matching with the bottoms?
            dist = bottom_d_position * 0.5  # Dist between darts -> dist between centers
            b_edge, b_interface = self.add_dart(
                pyg.EdgeSeqFactory.dart_shape(bottom_d_width, 0.9 * bottom_d_depth),
                self.edges[0], 
                offset=bottom_d_position + dist / 2 + bottom_d_width + bottom_d_width / 2,
            )
            b_edge, b_interface = self.add_dart(
                pyg.EdgeSeqFactory.dart_shape(bottom_d_width, bottom_d_depth),
                b_edge[0], 
                offset=bottom_d_position - dist / 2 + bottom_d_width / 2,
                edge_seq=b_edge,
                int_edge_seq=b_interface,
            )

            self.edges.substitute(0, b_edge)
            self.interfaces['bottom'] = pyg.Interface(self, b_interface)

            # Remove fabric from the sides if the diff is big enough
            b_edge[-1].end[0] += side_adj

        # default placement
        self.translate_by([0, body['height'] - body['head_l'] - length - shoulder_incl, 0])

    def get_width(self, level):
        return self.width

class BodiceHalf(pyg.Component):
    """Definition of a half of an upper garment with sleeves and collars"""

    def __init__(self, name, body, design, fitted=True) -> None:
        super().__init__(name)

        design = deepcopy(design)   # Recalculate freely!

        # Torso
        if fitted:
            self.ftorso = BodiceFrontHalf(
                f'{name}_ftorso', body, design).translate_by([0, 0, 30])
            self.btorso = BodiceBackHalf(
                f'{name}_btorso', body, design).translate_by([0, 0, -25])
        else:
            self.ftorso = tee.TorsoFrontHalfPanel(
                f'{name}_ftorso', body, design).translate_by([0, 0, 30])
            self.btorso = tee.TorsoBackHalfPanel(
                f'{name}_btorso', body, design).translate_by([0, 0, -25])

        # Interfaces
        self.interfaces.update({
            'f_bottom': self.ftorso.interfaces['bottom'],
            'b_bottom': self.btorso.interfaces['bottom'],
            'front_in': self.ftorso.interfaces['inside'],
            'back_in': self.btorso.interfaces['inside']
        })

        # Sleeves/collar cuts
        self.sleeve = None
        self.collar_comp = None
        self.eval_dep_params(body, design)

        if design['fitted_shirt']['strapless']['v'] and fitted:  # NOTE: Strapless design only for fitted tops
            self.make_strapless(body, design)
        else:
            # Sleeves and collars 
            self.add_sleeves(name, body, design)
            self.add_collars(name, body, design)
            self.stitching_rules.append((
                self.ftorso.interfaces['shoulder'], 
                self.btorso.interfaces['shoulder']
            ))  # tops

        # Main connectivity
        self.stitching_rules.append((
            self.ftorso.interfaces['outside'], self.btorso.interfaces['outside']))   # sides

    def eval_dep_params(self, body, design):

        # Sleeves
        # NOTE assuming the vertical side is the first argument
        max_cwidth = self.ftorso.interfaces['shoulder_corner'].edges[0].length() - 1  # cm
        min_cwidth = body['_armscye_depth']
        v = design['sleeve']['connecting_width']['v']
        design['sleeve']['connecting_width']['v'] = min(min_cwidth + min_cwidth * v, max_cwidth)

        # Collars
        # NOTE: Assuming the first is the top edge
        # Width
        # TODOLOW What if sleeve inclination is variable?
        # NOTE: Back panel is more narrow, so using it
        max_w = body['_base_sleeve_balance'] - 2  # 1 cm from default sleeve
        min_w = body['neck_w']

        if design['collar']['width']['v'] >= 0:
            design['collar']['width']['v'] = width = pyg.utils.lin_interpolation(min_w, max_w, design['collar']['width']['v'])
        else:
            design['collar']['width']['v'] = width = pyg.utils.lin_interpolation(0, min_w, 1 + design['collar']['width']['v'])

        # Depth
        # Collar depth is given w.r.t. length.
        # adjust for the shoulder inclination
        tg = np.tan(np.deg2rad(body['_shoulder_incl']))
        f_depth_adj = tg * (self.ftorso.get_width(0) - width / 2)
        b_depth_adj = tg * (self.btorso.get_width(0) - width / 2)

        max_f_len = self.ftorso.interfaces['collar_corner'].edges[1].length() - tg * self.ftorso.get_width(0) - 1  # cm
        max_b_len = self.btorso.interfaces['collar_corner'].edges[1].length() - tg * self.btorso.get_width(0) - 1  # cm

        design['collar']['f_strapless_depth'] = {}
        design['collar']['f_strapless_depth']['v'] = min(
            design['collar']['fc_depth']['v'] * body['_bust_line'], 
            max_f_len)
        design['collar']['fc_depth']['v'] = design['collar']['f_strapless_depth']['v'] + f_depth_adj
        

        design['collar']['b_strapless_depth'] = {}
        design['collar']['b_strapless_depth']['v'] =  min(
            design['collar']['bc_depth']['v'] * body['_bust_line'], 
            max_b_len)
        design['collar']['bc_depth']['v'] = design['collar']['b_strapless_depth']['v'] + b_depth_adj 

    def add_sleeves(self, name, body, design):
        self.sleeve = sleeves.Sleeve(
            name, body, design, 
            front_w=self.ftorso.get_width,
            back_w=self.btorso.get_width
        )
        # self.sleeve = sleeves.OnePieceSleeve(
        #     name, body, design,
        #     front_w=self.ftorso.get_width,
        #     back_w=self.btorso.get_width
        # )

        _, f_sleeve_int = pyg.ops.cut_corner(
            self.sleeve.interfaces['in_front_shape'].edges, 
            self.ftorso.interfaces['shoulder_corner'], 
            verbose=self.verbose
        )
        _, b_sleeve_int = pyg.ops.cut_corner(
            self.sleeve.interfaces['in_back_shape'].edges, 
            self.btorso.interfaces['shoulder_corner'],
            verbose=self.verbose
        )

        if not design['sleeve']['sleeveless']['v']:  
            # Ordering
            bodice_sleeve_int = pyg.Interface.from_multiple(
                f_sleeve_int.reverse(with_edge_dir_reverse=True),
                b_sleeve_int.reverse(),
            )    
            self.stitching_rules.append((
                self.sleeve.interfaces['in'], 
                bodice_sleeve_int
            ))

            # NOTE: This is a heuristic tuned for arm poses 30 deg-60 deg 
            # used in the dataset
            # FIXME Needs a better general solution
            gap = -1 - body['arm_pose_angle'] / 10
            self.sleeve.place_by_interface(
                self.sleeve.interfaces['in'], 
                bodice_sleeve_int, 
                gap=gap,   
                alignment='top',
            )

        # Add edge labels
        f_sleeve_int.edges.propagate_label(f'{self.name}_armhole')
        b_sleeve_int.edges.propagate_label(f'{self.name}_armhole')
    
    def add_collars(self, name, body, design):
        # Front
        collar_type = getattr(
            collars, 
            str(design['collar']['component']['style']['v']), 
            collars.NoPanelsCollar
            )
        
        self.collar_comp = collar_type(name, body, design)
        
        # Project shape
        _, fc_interface = pyg.ops.cut_corner(
            self.collar_comp.interfaces['front_proj'].edges, 
            self.ftorso.interfaces['collar_corner'],
            verbose=self.verbose
        )
        _, bc_interface = pyg.ops.cut_corner(
            self.collar_comp.interfaces['back_proj'].edges, 
            self.btorso.interfaces['collar_corner'],
            verbose=self.verbose
        )

        # Add stitches/interfaces
        if 'bottom' in self.collar_comp.interfaces:
            self.stitching_rules.append((
                pyg.Interface.from_multiple(fc_interface, bc_interface), 
                self.collar_comp.interfaces['bottom']
            ))

        # Upd front interfaces accordingly
        if 'front' in self.collar_comp.interfaces:
            self.interfaces['front_collar'] = self.collar_comp.interfaces['front']
            self.interfaces['front_in'] = pyg.Interface.from_multiple(
                self.ftorso.interfaces['inside'], self.interfaces['front_collar']
            )
        if 'back' in self.collar_comp.interfaces:
            self.interfaces['back_collar'] = self.collar_comp.interfaces['back']
            self.interfaces['back_in'] = pyg.Interface.from_multiple(
                self.btorso.interfaces['inside'], self.interfaces['back_collar']
            )
        
        # Add edge labels
        fc_interface.edges.propagate_label(f'{self.name}_collar')
        bc_interface.edges.propagate_label(f'{self.name}_collar')

    def make_strapless(self, body, design):

        out_depth = design['sleeve']['connecting_width']['v']
        f_in_depth = design['collar']['f_strapless_depth']['v']
        b_in_depth = design['collar']['b_strapless_depth']['v']

        # Shoulder adjustment for the back
        # TODOLOW Shoulder adj evaluation should be a function
        shoulder_angle = np.deg2rad(body['_shoulder_incl'])
        sleeve_balance = body['_base_sleeve_balance'] / 2
        back_w = self.btorso.get_width(0)
        shoulder_adj = np.tan(shoulder_angle) * (back_w - sleeve_balance)
        out_depth -= shoulder_adj

        # Upd back
        self._adjust_top_level(self.btorso, out_depth, b_in_depth)

        # Front depth determined by ~compensating for lenght difference
        len_back = self.btorso.interfaces['outside'].edges.length()
        len_front = self.ftorso.interfaces['outside'].edges.length()
        self._adjust_top_level(self.ftorso, out_depth, f_in_depth, target_remove=(len_front - len_back))
        
        # Placement
        # NOTE: The commented line places the top a bit higher, increasing the chanced of correct drape
        # Surcumvented by attachment constraint, so removed for nicer alignment in asymmetric garments
        # self.translate_by([0, out_depth - body['_armscye_depth'] * 0.75, 0])   # adjust for better localisation

        # Add a label
        self.ftorso.interfaces['shoulder'].edges.propagate_label('strapless_top')
        self.btorso.interfaces['shoulder'].edges.propagate_label('strapless_top')


    def _adjust_top_level(self, panel, out_level, in_level, target_remove=None):
        """Crops the top of the bodice front/back panel for strapless style

            * out_length_diff -- if set, determined the length difference that should be compensates
            after cutting the depth
        """
        # TODOLOW Should this be the panel's function?

        panel_top = panel.interfaces['shoulder'].edges[0]
        min_y = min(panel_top.start[1], panel_top.end[1])  

        # Order vertices
        ins, out = panel_top.start, panel_top.end
        if panel_top.start[1] < panel_top.end[1]:
            ins, out = out, ins
    
        # Inside is a simple vertical line and can be adjusted by chaning Y value
        ins[1] = min_y - in_level

        # Outside could be inclined, so needs further calculations
        outside_edge = panel.interfaces['outside'].edges[-1]
        bot, top = outside_edge.start, outside_edge.end
        if bot is out:
            bot, top = top, bot
        
        if target_remove is not None:
            # Adjust the depth to remove this length exactly
            angle_sin = abs(out[1] - bot[1]) / outside_edge.length()   
            curr_remove = out_level / angle_sin
            length_diff = target_remove - curr_remove
            adjustment = length_diff * angle_sin
            out_level += adjustment
        
        angle_cotan = abs(out[0] - bot[0]) / abs(out[1] - bot[1])
        out[0] -= out_level * angle_cotan  
        out[1] = min_y - out_level


    def length(self):
        return self.btorso.length()

class Shirt(pyg.Component):
    """Panel for the front of upper garments with darts to properly fit it to
    the shape"""

    def __init__(self, body, design, fitted=False) -> None:
        name_with_params = f"{self.__class__.__name__}"
        super().__init__(name_with_params)

        design = self.eval_dep_params(design)

        self.right = BodiceHalf(f'right', body, design, fitted=fitted)
        self.left = BodiceHalf(
            f'left', body, 
            design['left'] if design['left']['enable_asym']['v'] else design, 
            fitted=fitted).mirror()

        self.stitching_rules.append((self.right.interfaces['front_in'],
                                     self.left.interfaces['front_in']))
        self.stitching_rules.append((self.right.interfaces['back_in'],
                                     self.left.interfaces['back_in']))

        # Adjust interface ordering for correct connectivity
        self.interfaces = {   # Bottom connection
            'bottom': pyg.Interface.from_multiple(
                self.right.interfaces['f_bottom'].reverse(),
                self.left.interfaces['f_bottom'],
                self.left.interfaces['b_bottom'].reverse(),
                self.right.interfaces['b_bottom'],)
        }

    def eval_dep_params(self, design):
        # NOTE: Support for full collars with partially strapless top
        # or combination of paneled collar styles
        # requres further development
        # TODOLOW enable this one to work
        if design['left']['enable_asym']['v']:
            # Force no collars since they are not compatible with each other
            design = deepcopy(design)
            design['collar']['component']['style']['v'] = None
            design['left']['collar']['component'] = dict(style=dict(v=None))
            
            # Left-right design compatibility 
            design['left']['shirt'].update(length={})
            design['left']['shirt']['length']['v'] = design['shirt']['length']['v']
            
            design['left']['collar'].update(fc_depth={}, bc_depth={})
            design['left']['collar']['fc_depth']['v'] = design['collar']['fc_depth']['v']
            design['left']['collar']['bc_depth']['v'] = design['collar']['bc_depth']['v']

        return design

    def length(self):
        return self.right.length()

class FittedShirt(Shirt):
    """Creates fitted shirt
    
        NOTE: Separate class is used for selection convenience.
        Even though most of the processing is the same 
        (hence implemented with the same components except for panels), 
        design parametrization differs significantly. 
        With that, we decided to separate the top level names
    """
    def __init__(self, body, design) -> None:
        super().__init__(body, design, fitted=True)


class PrincessSeamShirt(pyg.Component):
    """Class representing a shirt with princess seams."""

    def __init__(self, body, design):
        # Initialize the base Component class
        super().__init__("PrincessSeamShirt")

        # Evaluate design parameters
        self.eval_dep_params(design)

        # Create front and back panels
        self.front_panel = self.create_front_panel(body, design)
        self.back_panel = self.create_back_panel(body, design)

        # Add panels as components
        self.add_component(self.front_panel)
        self.add_component(self.back_panel)

        # Define stitching rules
        self.stitching_rules = [
            # Side seams
            (self.front_panel.interfaces['side_seam'], self.back_panel.interfaces['side_seam']),
            # Shoulder seams
            (self.front_panel.interfaces['shoulder_seam'], self.back_panel.interfaces['shoulder_seam']),
            # Front princess seams
            (self.front_panel.interfaces['princess_seam_left'], self.front_panel.left_princess_panel.interfaces['princess_seam']),
            (self.front_panel.interfaces['princess_seam_right'], self.front_panel.right_princess_panel.interfaces['princess_seam']),
            # Back princess seams
            (self.back_panel.interfaces['princess_seam_left'], self.back_panel.left_princess_panel.interfaces['princess_seam']),
            (self.back_panel.interfaces['princess_seam_right'], self.back_panel.right_princess_panel.interfaces['princess_seam']),
        ]

    def eval_dep_params(self, design):
        # Placeholder for evaluating dependent design parameters
        pass

    def create_front_panel(self, body, design):
        # Create the front panel
        front_panel = pyg.Panel("FrontPanel")

        # Body measurements
        bust = body['bust']
        waist = body['waist']
        hips = body['hips']
        shoulder_width = body['shoulder_width']
        neck_width = body['neck_width']
        neck_depth = body['front_neck_depth']

        # Calculate panel dimensions
        panel_width = bust / 2
        princess_seam_pos = bust / 8

        # Define key points
        points = [
            [0, 0],  # Bottom left
            [0, hips],  # Hip point
            [0, waist],  # Waist point
            [princess_seam_pos, bust],  # Left bust point
            [shoulder_width / 2, bust + neck_depth],  # Left shoulder
            [panel_width - shoulder_width / 2, bust + neck_depth],  # Right shoulder
            [panel_width - princess_seam_pos, bust],  # Right bust point
            [panel_width, waist],  # Right waist point
            [panel_width, hips],  # Right hip point
            [panel_width, 0],  # Bottom right
        ]

        # Create edges
        edges = pyg.EdgeSequence()
        for i in range(len(points) - 1):
            edges.append(pyg.Edge(points[i], points[i + 1]))
        edges.append(pyg.Edge(points[-1], points[0]))  # Close shape

        # Assign edges to panel
        front_panel.edges = edges

        # Define interfaces
        front_panel.interfaces = {
            'side_seam': pyg.Interface(front_panel, [edges[0], edges[8]]),
            'shoulder_seam': pyg.Interface(front_panel, [edges[4], edges[5]]),
            'princess_seam_left': pyg.Interface(front_panel, [edges[2]]),
            'princess_seam_right': pyg.Interface(front_panel, [edges[7]]),
        }

        # Create princess panels
        front_panel.left_princess_panel = self.create_princess_panel(front_panel, edges[2], "FrontLeftPrincessPanel")
        front_panel.right_princess_panel = self.create_princess_panel(front_panel, edges[7], "FrontRightPrincessPanel")

        return front_panel

    def create_back_panel(self, body, design):
        # Create the back panel
        back_panel = pyg.Panel("BackPanel")

        # Body measurements
        bust = body['bust']
        waist = body['waist']
        hips = body['hips']
        shoulder_width = body['shoulder_width']
        neck_width = body['neck_width']
        neck_depth = body['back_neck_depth']

        # Calculate panel dimensions
        panel_width = bust / 2
        princess_seam_pos = bust / 8

        # Define key points
        points = [
            [0, 0],  # Bottom left
            [0, hips],  # Hip point
            [0, waist],  # Waist point
            [princess_seam_pos, bust],  # Left bust point
            [shoulder_width / 2, bust + neck_depth],  # Left shoulder
            [panel_width - shoulder_width / 2, bust + neck_depth],  # Right shoulder
            [panel_width - princess_seam_pos, bust],  # Right bust point
            [panel_width, waist],  # Right waist point
            [panel_width, hips],  # Right hip point
            [panel_width, 0],  # Bottom right
        ]

        # Create edges
        edges = pyg.EdgeSequence()
        for i in range(len(points) - 1):
            edges.append(pyg.Edge(points[i], points[i + 1]))
        edges.append(pyg.Edge(points[-1], points[0]))  # Close shape

        # Assign edges to panel
        back_panel.edges = edges

        # Define interfaces
        back_panel.interfaces = {
            'side_seam': pyg.Interface(back_panel, [edges[0], edges[8]]),
            'shoulder_seam': pyg.Interface(back_panel, [edges[4], edges[5]]),
            'princess_seam_left': pyg.Interface(back_panel, [edges[2]]),
            'princess_seam_right': pyg.Interface(back_panel, [edges[7]]),
        }

        # Create princess panels
        back_panel.left_princess_panel = self.create_princess_panel(back_panel, edges[2], "BackLeftPrincessPanel")
        back_panel.right_princess_panel = self.create_princess_panel(back_panel, edges[7], "BackRightPrincessPanel")

        return back_panel

    def create_princess_panel(self, parent_panel, seam_edge, panel_name):
        # Create a princess panel along the seam
        princess_panel = pyg.Panel(panel_name)

        # Seam edge points
        start_point = seam_edge.start
        end_point = seam_edge.end

        # Panel offset
        offset = 5  # Width of the princess panel

        # Calculate new points
        offset_start = [start_point[0] + offset, start_point[1]]
        offset_end = [end_point[0] + offset, end_point[1]]

        # Create edges
        edges = pyg.EdgeSequence()
        edges.append(pyg.Edge(start_point, end_point))  # Seam edge
        edges.append(pyg.Edge(end_point, offset_end))   # Top edge
        edges.append(pyg.Edge(offset_end, offset_start))  # Outer edge
        edges.append(pyg.Edge(offset_start, start_point))  # Bottom edge

        # Close shape
        edges.close_loop()

        # Assign edges to panel
        princess_panel.edges = edges

        # Define interfaces
        princess_panel.interfaces = {
            'princess_seam': pyg.Interface(princess_panel, [edges[0]]),
            'outer_seam': pyg.Interface(princess_panel, [edges[2]]),
        }

        return princess_panel