from agent.Base_Agent import Base_Agent
from behaviors.custom.Step.Step_Generator import Step_Generator
from math_ops.Math_Ops import Math_Ops as M


class Basic_Kick():

    def __init__(self, base_agent : Base_Agent) -> None:
        self.behavior = base_agent.behavior
        self.path_manager = base_agent.path_manager
        self.world = base_agent.world
        self.description = "Walk to ball and perform a basic kick"
        self.auto_head = True

        r_type = self.world.robot.type
        self.bias_dir = [22,29,26,29,22][self.world.robot.type]
        self.ball_x_limits = ((0.19,0.215), (0.2,0.22), (0.19,0.22), (0.2,0.215), (0.2,0.215))[r_type]
        self.ball_y_limits = ((-0.115,-0.1), (-0.125,-0.095), (-0.12,-0.1), (-0.13,-0.105), (-0.09,-0.06))[r_type]
        self.ball_x_center = (self.ball_x_limits[0] + self.ball_x_limits[1])/2
        self.ball_y_center = (self.ball_y_limits[0] + self.ball_y_limits[1])/2
      
    def execute(self,reset, direction, abort=False) -> bool: # You can add more arguments 
        '''
        Parameters
        ----------
        direction : float
            kick direction relative to field, in degrees
        abort : bool
            True to abort.
            The method returns True upon successful abortion, which is immediate while the robot is aligning itself. 
            However, if the abortion is requested during the kick, it is delayed until the kick is completed.
        '''

        w = self.world
        r = self.world.robot
        b = w.ball_rel_torso_cart_pos
        t = w.time_local_ms
        gait : Step_Generator = self.behavior.get_custom_behavior_object("Walk").env.step_generator

        if reset:
            self.phase = 0
            self.reset_time = t

        if self.phase == 0: 
            biased_dir = M.normalize_deg(direction + self.bias_dir) # add bias to rectify direction
            ang_diff = abs(M.normalize_deg( biased_dir - r.loc_torso_orientation )) # the reset was learned with loc, not IMU

            next_pos, next_ori, dist_to_final_target = self.path_manager.get_path_to_ball(
                x_ori=biased_dir, x_dev=-self.ball_x_center, y_dev=-self.ball_y_center, torso_ori=biased_dir)
            
            if (w.ball_last_seen > t - w.VISUALSTEP_MS and ang_diff < 5 and       # ball is visible & aligned
                self.ball_x_limits[0] < b[0] < self.ball_x_limits[1] and          # ball is in kick area (x)
                self.ball_y_limits[0] < b[1] < self.ball_y_limits[1] and          # ball is in kick area (y)
                t - w.ball_abs_pos_last_update < 100 and                          # ball absolute location is recent
                dist_to_final_target < 0.03 and                                   # if absolute ball position is updated
                not gait.state_is_left_active and gait.state_current_ts == 2 and  # walk gait phase is adequate
                t - self.reset_time > 500): # to avoid kicking immediately without preparation & stability

                self.phase += 1
                return self.behavior.execute_sub_behavior("Kick_Motion", True)
            else:
                dist = max(0.07, dist_to_final_target)
                reset_walk = reset and self.behavior.previous_behavior != "Walk" # reset walk if it wasn't the previous behavior
                self.behavior.execute_sub_behavior("Walk", reset_walk, next_pos, True, next_ori, True, dist) # target, is_target_abs, ori, is_ori_abs, distance
                return abort # abort only if self.phase == 0

        else: # define kick parameters and execute 
            return self.behavior.execute_sub_behavior("Kick_Motion", False)

      
    def is_ready(self) -> any: # You can add more arguments 
        ''' Returns True if this behavior is ready to start/continue under current game/robot conditions '''
        return True
