import json
import os

import utils_pipeline
from skelda import evals
from skelda.writers import json_writer

# ==================================================================================================

whole_body = {
    "foots": False,
    "face": False,
    "hands": False,
}

dataset_use = "human36m"
# dataset_use = "panoptic"
# dataset_use = "mvor"
# dataset_use = "shelf"
# dataset_use = "campus"
# dataset_use = "ikeaasm"
# dataset_use = "chi3d"
# dataset_use = "tsinghua"
# dataset_use = "human36m_wb"
# dataset_use = "egohumans_tagging"
# dataset_use = "egohumans_legoassemble"
# dataset_use = "egohumans_fencing"
# dataset_use = "egohumans_basketball"
# dataset_use = "egohumans_volleyball"
# dataset_use = "egohumans_badminton"
# dataset_use = "egohumans_tennis"


# Describes the minimum area as fraction of the image size for a 2D bounding box to be considered
# If the persons are small in the image, use a lower value
default_min_bbox_area = 0.1 * 0.1

# Describes how confident a 2D bounding box needs to be to be considered
# If the persons are small in the image, or poorly recognizable, use a lower value
default_min_bbox_score = 0.3

# Describes how good two 2D poses need to match each other to create a valid triangulation
# If the quality of the 2D detections is poor, use a lower value
default_min_match_score = 0.94

# Describes the minimum number of camera pairs that need to detect the same person
# If the number of cameras is high, and the views are not occluded, use a higher value
default_min_group_size = 1

# Batch poses per image for faster processing
# If most of the time only one person is in a image, disable it, because it is slightly slower then
default_batch_poses = True

datasets = {
    "human36m": {
        "path": "/datasets/human36m/skelda/pose_test.json",
        "take_interval": 5,
        "min_match_score": 0.95,
        "min_group_size": 1,
        "min_bbox_score": 0.4,
        "min_bbox_area": 0.1 * 0.1,
        "batch_poses": False,
    },
    "panoptic": {
        "path": "/datasets/panoptic/skelda/test.json",
        "cams": ["00_03", "00_06", "00_12", "00_13", "00_23"],
        # "cams": ["00_03", "00_06", "00_12"],
        # "cams": ["00_03", "00_06", "00_12", "00_13", "00_23", "00_15", "00_10"],
        # "cams": ["00_03", "00_06", "00_12", "00_13", "00_23", "00_15", "00_10", "00_21", "00_09", "00_01"],
        # "cams": [],
        "take_interval": 3,
        "min_match_score": 0.95,
        "use_scenes": ["160906_pizza1", "160422_haggling1", "160906_ian5"],
        "min_group_size": 1,
        # "min_group_size": 1,
        # "min_group_size": 1,
        # "min_group_size": 2,
        # "min_group_size": 11,
        "min_bbox_area": 0.05 * 0.05,
    },
    "mvor": {
        "path": "/datasets/mvor/skelda/all.json",
        "take_interval": 1,
        "min_match_score": 0.81,
        "min_bbox_score": 0.25,
    },
    "campus": {
        "path": "/datasets/campus/skelda/test.json",
        "take_interval": 1,
        "min_match_score": 0.91,
        "min_bbox_score": 0.5,
    },
    "shelf": {
        "path": "/datasets/shelf/skelda/test.json",
        "take_interval": 1,
        "min_match_score": 0.95,
        "min_group_size": 2,
    },
    "ikeaasm": {
        "path": "/datasets/ikeaasm/skelda/test.json",
        "take_interval": 2,
        "min_match_score": 0.81,
        "min_bbox_score": 0.20,
    },
    "chi3d": {
        "path": "/datasets/chi3d/skelda/all.json",
        "take_interval": 5,
    },
    "tsinghua": {
        "path": "/datasets/tsinghua/skelda/test.json",
        "take_interval": 3,
        "min_match_score": 0.95,
        "min_group_size": 2,
    },
    "human36m_wb": {
        "path": "/datasets/human36m/skelda/wb/test.json",
        "take_interval": 100,
        "min_bbox_score": 0.4,
        "batch_poses": False,
    },
    "egohumans_tagging": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "tagging",
        "min_match_score": 0.89,
        "min_group_size": 1,
        "min_bbox_score": 0.2,
        "min_bbox_area": 0.05 * 0.05,
    },
    "egohumans_legoassemble": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "legoassemble",
        "min_group_size": 2,
    },
    "egohumans_fencing": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "fencing",
        "min_group_size": 7,
        "min_bbox_score": 0.5,
        "min_bbox_area": 0.05 * 0.05,
    },
    "egohumans_basketball": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "basketball",
        "min_group_size": 4,
        "min_bbox_score": 0.25,
        "min_bbox_area": 0.025 * 0.025,
    },
    "egohumans_volleyball": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "volleyball",
        "min_match_score": 0.95,
        "min_group_size": 7,
        "min_bbox_score": 0.25,
        "min_bbox_area": 0.05 * 0.05,
    },
    "egohumans_badminton": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "badminton",
        "min_group_size": 7,
        "min_bbox_score": 0.25,
        "min_bbox_area": 0.05 * 0.05,
    },
    "egohumans_tennis": {
        "path": "/datasets/egohumans/skelda/all.json",
        "take_interval": 2,
        "subset": "tennis",
        "min_group_size": 11,
        "min_bbox_area": 0.025 * 0.025,
    },
}

joint_names_2d = utils_pipeline.get_joint_names(whole_body)
joint_names_3d = list(joint_names_2d)
eval_joints = [
    "head",
    "shoulder_left",
    "shoulder_right",
    "elbow_left",
    "elbow_right",
    "wrist_left",
    "wrist_right",
    "hip_left",
    "hip_right",
    "knee_left",
    "knee_right",
    "ankle_left",
    "ankle_right",
]
if dataset_use == "human36m":
    eval_joints[eval_joints.index("head")] = "nose"
if dataset_use == "panoptic":
    eval_joints[eval_joints.index("head")] = "nose"
if dataset_use == "human36m_wb":
    if utils_pipeline.use_whole_body(whole_body):
        eval_joints = list(joint_names_2d)
    else:
        eval_joints[eval_joints.index("head")] = "nose"

# output_dir = "/RapidPoseTriangulation/data/testoutput/"
output_dir = ""

# ==================================================================================================


def load_labels(dataset: dict):
    """Load labels by dataset description"""

    if "panoptic" in dataset:
        labels = utils_pipeline.load_json(dataset["panoptic"]["path"])
        labels = [lb for i, lb in enumerate(labels) if i % 1500 < 90]

        # Filter by maximum number of persons
        labels = [l for l in labels if len(l["bodies3D"]) <= 10]

        # Filter scenes
        if "use_scenes" in dataset["panoptic"]:
            labels = [
                l for l in labels if l["scene"] in dataset["panoptic"]["use_scenes"]
            ]

        # Filter cameras
        if not "cameras_depth" in labels[0] and len(dataset["panoptic"]["cams"]) > 0:
            for label in labels:
                for i, cam in reversed(list(enumerate(label["cameras"]))):
                    if cam["name"] not in dataset["panoptic"]["cams"]:
                        label["cameras"].pop(i)
                        label["imgpaths"].pop(i)

    elif "human36m" in dataset:
        labels = utils_pipeline.load_json(dataset["human36m"]["path"])
        labels = [lb for lb in labels if lb["subject"] == "S9"]
        labels = [lb for i, lb in enumerate(labels) if i % 4000 < 150]

        for label in labels:
            label.pop("action")
            label.pop("frame")

    elif "mvor" in dataset:
        labels = utils_pipeline.load_json(dataset["mvor"]["path"])

        # Rename keys
        for label in labels:
            label["cameras_color"] = label["cameras"]
            label["imgpaths_color"] = label["imgpaths"]

    elif "ikeaasm" in dataset:
        labels = utils_pipeline.load_json(dataset["ikeaasm"]["path"])
        cams0 = str(labels[0]["cameras"])
        labels = [lb for lb in labels if str(lb["cameras"]) == cams0]

    elif "shelf" in dataset:
        labels = utils_pipeline.load_json(dataset["shelf"]["path"])
        labels = [lb for lb in labels if "test" in lb["splits"]]

    elif "campus" in dataset:
        labels = utils_pipeline.load_json(dataset["campus"]["path"])
        labels = [lb for lb in labels if "test" in lb["splits"]]

    elif "tsinghua" in dataset:
        labels = utils_pipeline.load_json(dataset["tsinghua"]["path"])
        labels = [lb for lb in labels if "test" in lb["splits"]]
        labels = [lb for lb in labels if lb["seq"] == "seq_1"]
        labels = [lb for i, lb in enumerate(labels) if i % 300 < 90]

        for label in labels:
            label["bodyids"] = list(range(len(label["bodies3D"])))

    elif "chi3d" in dataset:
        labels = utils_pipeline.load_json(dataset["chi3d"]["path"])
        labels = [lb for lb in labels if lb["setup"] == "s03"]
        labels = [lb for i, lb in enumerate(labels) if i % 2000 < 150]

    elif "human36m_wb" in dataset:
        labels = utils_pipeline.load_json(dataset["human36m_wb"]["path"])

    elif any(("egohumans" in key for key in dataset)):
        labels = utils_pipeline.load_json(dataset[dataset_use]["path"])
        labels = [lb for lb in labels if "test" in lb["splits"]]
        labels = [lb for lb in labels if dataset[dataset_use]["subset"] in lb["seq"]]
        if dataset[dataset_use]["subset"] in ["volleyball", "tennis"]:
            labels = [lb for i, lb in enumerate(labels) if i % 150 < 60]

    else:
        raise ValueError("Dataset not available")

    # Optionally drop samples to speed up train/eval
    if "take_interval" in dataset:
        take_interval = dataset["take_interval"]
        if take_interval > 1:
            labels = [l for i, l in enumerate(labels) if i % take_interval == 0]

    # Add default values
    for label in labels:
        if "scene" not in label:
            label["scene"] = "default"
        for cam in label["cameras"]:
            if not "type" in cam:
                cam["type"] = "pinhole"

    return labels


# ==================================================================================================


def main():
    global joint_names_3d, eval_joints

    print("Loading dataset ...")
    labels = load_labels(
        {
            dataset_use: datasets[dataset_use],
            "take_interval": datasets[dataset_use]["take_interval"],
        }
    )

    # Print a dataset sample for debugging
    print(labels[0])

    # Save dataset
    tmp_export_dir = "/tmp/rpt/"
    for label in labels:
        if "splits" in label:
            label.pop("splits")
    json_writer.save_dataset(labels, tmp_export_dir)

    # Load dataset specific parameters
    min_match_score = datasets[dataset_use].get(
        "min_match_score", default_min_match_score
    )
    min_group_size = datasets[dataset_use].get("min_group_size", default_min_group_size)
    min_bbox_score = datasets[dataset_use].get("min_bbox_score", default_min_bbox_score)
    min_bbox_area = datasets[dataset_use].get("min_bbox_area", default_min_bbox_area)
    batch_poses = datasets[dataset_use].get("batch_poses", default_batch_poses)

    # Save config
    config_path = tmp_export_dir + "config.json"
    config = {
        "min_match_score": min_match_score,
        "min_group_size": min_group_size,
        "min_bbox_score": min_bbox_score,
        "min_bbox_area": min_bbox_area,
        "batch_poses": batch_poses,
        "whole_body": whole_body,
        "take_interval": datasets[dataset_use]["take_interval"],
    }
    utils_pipeline.save_json(config, config_path)

    # Call the CPP binary
    os.system("/RapidPoseTriangulation/scripts/test_skelda_dataset.bin")

    # Load the results
    print("Loading exports ...")
    res_path = tmp_export_dir + "results.json"
    results = utils_pipeline.load_json(res_path)
    all_poses_3d = results["all_poses_3d"]
    all_poses_2d = results["all_poses_2d"]
    all_ids = results["all_ids"]
    joint_names_3d = results["joint_names_3d"]

    # Run evaluation
    _ = evals.mpjpe.run_eval(
        labels,
        all_poses_3d,
        all_ids,
        joint_names_net=joint_names_3d,
        joint_names_use=eval_joints,
        save_error_imgs=output_dir,
        debug_2D_preds=all_poses_2d,
    )
    _ = evals.pcp.run_eval(
        labels,
        all_poses_3d,
        all_ids,
        joint_names_net=joint_names_3d,
        joint_names_use=eval_joints,
        replace_head_with_nose=True,
    )

    if dataset_use == "shelf":
        # Also run old-style evaluation for shelf dataset
        odir = os.path.join(output_dir, "pcp/") if output_dir != "" else ""
        _ = evals.campus_shelf.run_eval(
            labels,
            all_poses_3d,
            all_ids,
            joint_names_net=joint_names_3d,
            save_error_imgs=odir,
            debug_2D_preds=all_poses_2d,
        )


# ==================================================================================================

if __name__ == "__main__":
    main()
