# Copyright (c) OpenMMLab. All rights reserved.
import logging
import time

import cv2
from argparse import ArgumentParser

from mmengine.logging import print_log
from mmpose.apis import inference_topdown, init_model
from mmpose.registry import VISUALIZERS
from mmpose.structures import merge_data_samples
import numpy as np

class Position:
    def __init__(self, person_kpts):
        if len(person_kpts) == 8:
            self.left_elbow_angle = person_kpts[0]
            self.right_elbow_angle = person_kpts[1]
            self.left_shoulder_angle = person_kpts[2]
            self.right_shoulder_angle = person_kpts[3]
            self.left_leg_angle = person_kpts[4]
            self.right_leg_angle = person_kpts[5]
            self.left_hip_angle = person_kpts[6]
            self.right_hip_angle = person_kpts[7]
        else:
            self.left_hip = person_kpts[11]
            self.left_knee = person_kpts[13]
            self.left_ankle = person_kpts[15]

            self.right_hip = person_kpts[12]
            self.right_knee = person_kpts[14]
            self.right_ankle = person_kpts[16]

            self.left_shoulder = person_kpts[5]
            self.left_elbow = person_kpts[7]
            self.left_wrist = person_kpts[9]

            self.right_shoulder = person_kpts[6]
            self.right_elbow = person_kpts[8]
            self.right_wrist = person_kpts[10]

            self.left_elbow_angle = angle_between(self.left_shoulder - self.left_elbow, self.left_wrist - self.left_elbow)
            self.right_elbow_angle = angle_between(self.right_shoulder - self.right_elbow, self.right_wrist - self.right_elbow)

            self.left_shoulder_angle = angle_between(self.right_shoulder - self.left_shoulder, self.left_elbow - self.left_shoulder)
            self.right_shoulder_angle = angle_between(self.left_shoulder - self.right_shoulder, self.right_elbow - self.right_shoulder)

            self.left_leg_angle = angle_between(self.left_hip - self.left_knee, self.left_ankle - self.left_knee)
            self.right_leg_angle = angle_between(self.right_hip - self.right_knee, self.right_ankle - self.right_knee)

            self.left_hip_angle = angle_between(self.left_shoulder - self.left_hip, self.left_knee - self.left_hip)
            self.right_hip_angle = angle_between(self.right_shoulder - self.right_hip, self.right_knee - self.right_hip)

    def to_array(self):
        return [
            self.left_elbow_angle,
            self.right_elbow_angle,
            self.left_shoulder_angle,
            self.right_shoulder_angle,
            self.left_leg_angle,
            self.right_leg_angle,
            self.left_hip_angle,
            self.right_hip_angle
        ]

    def distance_to(self, position):
        error = 0
        success = True

        for i, x in enumerate(self.to_array()):
            if 3 < i < 6:
                continue

            y = position.to_array()[i]

            if abs(y) > 165:
                x = abs(x)
                y = abs(y)

            dist = abs(y - x)

            if dist > 20:
                success = False
                # print(f"{i} nie jest ok: moje: {x}, cel: {y}")
                error += abs(y - x)
            else:
                pass
                # print(f"{i} jest ok: moje: {x}, cel: {position.to_array()[i]}")

        return (success, error)

def parse_args():
    parser = ArgumentParser()
    # parser.add_argument('checkpoint', help='Checkpoint file')
    parser.add_argument(
        '--device', default='cuda:0', help='Device used for inference')
    parser.add_argument(
        '--draw-heatmap',
        action='store_true',
        help='Visualize the predicted heatmap')
    parser.add_argument(
        '--show-kpt-idx',
        action='store_true',
        default=False,
        help='Whether to show the index of keypoints')
    parser.add_argument(
        '--skeleton-style',
        default='mmpose',
        type=str,
        choices=['mmpose', 'openpose'],
        help='Skeleton style selection')
    parser.add_argument(
        '--kpt-thr',
        type=float,
        default=0.3,
        help='Visualizing keypoint thresholds')
    parser.add_argument(
        '--radius',
        type=int,
        default=3,
        help='Keypoint radius for visualization')
    parser.add_argument(
        '--thickness',
        type=int,
        default=1,
        help='Link thickness for visualization')
    parser.add_argument(
        '--alpha', type=float, default=0.8, help='The transparency of bboxes')
    parser.add_argument(
        '--camera-id', type=int, default=0, help='Camera device ID')
    args = parser.parse_args()
    return args


def angle_between(v1, v2):
    """
    Liczy kąt między wektorami v1 i v2 w stopniach.
    Znak kąta zależy od kierunku (przeciwnie do ruchu wskazówek zegara jest dodatni).
    """
    # kąt w radianach
    angle = np.arccos(np.clip(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)), -1.0, 1.0))

    # obliczamy znak kąta w 2D: + jeśli v2 jest "po lewej" od v1
    sign = np.sign(v1[0] * v2[1] - v1[1] * v2[0])

    return np.degrees(angle) * sign

def is_visible(kpt, threshold=0.3):
    # kpt = [x, y, score]
    return kpt[2] > threshold

def main():
    args = parse_args()

    # build the model from a config file and a checkpoint file
    if args.draw_heatmap:
        cfg_options = dict(model=dict(test_cfg=dict(output_heatmaps=True)))
    else:
        cfg_options = None

    model = init_model(
        "mmpose/configs/body_2d_keypoint/topdown_heatmap/coco/td-hm_hrnet-w48_8xb32-210e_coco-256x192.py",
        "hrnet_w48_coco_256x192-b9e0b3ab_20200708.pth",
        device=args.device,
        cfg_options=cfg_options)

    # init visualizer
    model.cfg.visualizer.radius = args.radius
    model.cfg.visualizer.alpha = args.alpha
    model.cfg.visualizer.line_width = args.thickness

    visualizer = VISUALIZERS.build(model.cfg.visualizer)
    visualizer.set_dataset_meta(
        model.dataset_meta, skeleton_style=args.skeleton_style)

    # start capturing video from camera
    cap = cv2.VideoCapture(args.camera_id)
    if not cap.isOpened():
        print("Error: Cannot open camera")
        return

    lastDegs = []
    lastMove = 0
    visible = 0
    maha = 0
    start = False

    oldpos = None
    waiting = 100

    positions = []
    numPositions = 0
    tPose = Position(
        [
            167.5568,
            -161.67027,
            -166.49443,
            168.22028,
            110.21745,
            166.41733,
            167.57822,
            -176.08066
        ]
    )

    krakowiak = Position(
        [np.float32(-98.226715), np.float32(106.39389), np.float32(-135.05656), np.float32(139.48904), np.float32(-149.9036), np.float32(8.216028), np.float32(174.70923), np.float32(-176.893)]
    )

    krakowiakRight = Position(
        [np.float32(-110.61421), np.float32(-174.2424), np.float32(-127.05916), np.float32(174.9463),
         np.float32(175.62007), np.float32(166.89127), np.float32(168.8219), np.float32(-178.02744)]
    )

    while True:
        ret, frame = cap.read()
        if not ret:
            print("Error: Cannot read frame from camera")
            break

        # convert BGR to RGB
        img = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

        # inference
        batch_results = inference_topdown(model, img)
        results = merge_data_samples(batch_results)

        person_kpts = results.pred_instances.keypoints[0]
        person_visible = results.pred_instances.keypoints_visible[0]

        left_hand_visible = person_visible[5] > 0.75 and person_visible[5] > 0.75 and person_visible[9] > 0.75
        right_hand_visible = person_visible[6] > 0.75 and person_visible[8] > 0.75 and person_visible[10] > 0.75

        left_leg_visible = person_visible[11] > 0.75 and person_visible[13] > 0.75 and person_visible[15] > 0.75
        right_leg_visible = person_visible[12] > 0.75 and person_visible[14] > 0.75 and person_visible[16] > 0.75

        position = Position(person_kpts)
        #
        if position.distance_to(tPose)[0]:
            print("\rT POSE!", end="")
        elif position.distance_to(krakowiak)[0]:
            print("\rKrakowiak", end="")
        elif position.distance_to(krakowiakRight)[0]:
            print("\rKrakowiak right", end="")
        else:
            print("\rNIC!", end="")

        # print(position.left_elbow_angle)
        # if oldpos is None:
        #     oldpos = position
        #
        # if oldpos.distance_to(position) < 7.5:
        #     print(f"\r{goalPosition.distance_to(position)}", end="")
        #
        # oldpos = position

        # if error > 200:

        # if start:
        #     if numPositions > 100:
        #         avgPosition = [0, 0, 0, 0, 0, 0, 0, 0]
        #
        #         for element in positions:
        #             for i, data in enumerate(element):
        #                 avgPosition[i] += data
        #
        #         for i, element in enumerate(avgPosition):
        #             avgPosition[i] = avgPosition[i] / numPositions
        #
        #         print(avgPosition)
        #
        #         break
        #     else:
        #         print(f"\r{numPositions}", end="")
        #         if oldpos is None:
        #             oldpos = position
        #
        #         if oldpos.distance_to(position)[0]:
        #             positions.insert(0, position.to_array())
        #             numPositions += 1
        #
        #         oldpos = position
        #
        # # else:
        # #     print(f"\rOK!", end="")
        #
        # if waiting != 0 and not start:
        #     print(f"\r{waiting}", end="")
        #     waiting -= 1
        # else:
        #     start = True

        # lastDegs.insert(0, (left_elbow_angle, right_elbow_angle))
        #
        # last = 0
        # lastCount = 0
        #
        # for element in lastDegs:
        #     last += element[1]
        #     lastCount += 1
        #
        # last = last / lastCount
        # dist = right_elbow_angle - last
        #
        # if not right_visible:
        #     visible = 0
        #     print("\rNie widać prawej ręki!!!!!!!!", end="")
        # else:
        #     if maha == 0:
        #         print("\rWidać rękę, nie maha!", end="")
        #     else:
        #         maha -= 1
        #
        #     visible += 1
        #
        # if 15 < abs(dist) < 60 and visible > 5:
        #     if lastMove != dist > 0:
        #         maha = 10
        #         print("\rmaha!", end="")
        #
        #     lastMove = dist > 0
        #
        # if len(lastDegs) > 5:
        #     lastDegs.pop()

        # visualize
        vis_img = visualizer.add_datasample(
            'result',
            img,
            data_sample=results,
            draw_gt=False,
            draw_bbox=True,
            kpt_thr=args.kpt_thr,
            draw_heatmap=args.draw_heatmap,
            show_kpt_idx=args.show_kpt_idx,
            skeleton_style=args.skeleton_style,
            show=False,
            out_file=None)

        # convert RGB back to BGR for OpenCV
        vis_img = cv2.cvtColor(vis_img, cv2.COLOR_RGB2BGR)
        cv2.imshow('Live Pose Estimation', vis_img)

        # press 'q' to quit
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()


if __name__ == '__main__':
    main()
