import json
import os
import random
from pycocotools.coco import COCO
import argparse
import shutil


def ensure_coco_save_dir(output):
    train_dir = os.path.join(output, 'train')
    if not os.path.exists(train_dir):
        os.makedirs(train_dir)

    train_image_dir = os.path.join(output, 'train', 'images')
    if not os.path.exists(train_image_dir):
        os.makedirs(train_image_dir)

    val_dir = os.path.join(output, 'val')
    if not os.path.exists(val_dir):
        os.makedirs(val_dir)

    val_image_dir = os.path.join(output, 'val', 'images')
    if not os.path.exists(val_image_dir):
        os.makedirs(val_image_dir)


def ensure_yolo_save_dir(output):
    train_dir = os.path.join(output, 'train')
    if not os.path.exists(train_dir):
        os.makedirs(train_dir)

    train_image_dir = os.path.join(output, 'train', 'images')
    if not os.path.exists(train_image_dir):
        os.makedirs(train_image_dir)

    train_label_dir = os.path.join(output, 'train', 'labels')
    if not os.path.exists(train_label_dir):
        os.makedirs(train_label_dir)

    test_dir = os.path.join(output, 'val')
    if not os.path.exists(test_dir):
        os.makedirs(test_dir)

    test_image_dir = os.path.join(output, 'val', 'images')
    if not os.path.exists(test_image_dir):
        os.makedirs(test_image_dir)

    test_label_dir = os.path.join(output, 'val', 'labels')
    if not os.path.exists(test_label_dir):
        os.makedirs(test_label_dir)


def process_coco(coco_json, image_dir, train_ratio, output):
    # 加载 COCO 数据集
    coco = COCO(coco_json)

    # 获取所有图像的 ID
    image_ids = coco.getImgIds()

    # 随机划分训练集和测试集（例如，80% 为训练集，20% 为测试集）
    train_image_ids = random.sample(image_ids, int(len(image_ids) * train_ratio))
    val_image_ids = list(set(image_ids) - set(train_image_ids))

    # 为训练集和测试集创建新的 JSON 文件
    train_json = {'images': [], 'annotations': [], 'categories': coco.dataset['categories'], 'info': coco.dataset['info']}
    val_json = {'images': [], 'annotations': [], 'categories': coco.dataset['categories'], 'info': coco.dataset['info']}

    train_dir = os.path.join(output, 'train')
    val_dir = os.path.join(output, 'val')
    # 遍历图像 ID，并将它们分配到训练集或测试集
    for image_id in image_ids:
        image_info = coco.loadImgs(image_id)[0]
        file_name = os.path.join(image_dir, image_info['file_name'])

        # 检查图像文件是否存在（可选）
        if os.path.exists(file_name):
            if image_id in train_image_ids:
                train_json['images'].append(image_info)
                train_image_path = os.path.join(train_dir, image_info['file_name'])

                # 还需要获取并添加相关的标注信息到 train_json['annotations']
                annot_list = [annotation for annotation in coco.dataset['annotations'] if
                              annotation['image_id'] == image_id]
                train_json['annotations'].extend(annot_list)
                shutil.copy2(file_name, train_image_path)
            else:
                val_json['images'].append(image_info)
                val_image_path = os.path.join(val_dir, image_info['file_name'])

                # 还需要获取并添加相关的标注信息到 val_json['annotations']
                annot_list = [annotation for annotation in coco.dataset['annotations'] if
                              annotation['image_id'] == image_id]
                val_json['annotations'].extend(annot_list)
                shutil.copy2(file_name, val_image_path)

    # 保存新的 JSON 文件（这里省略了保存 annotations 的部分）
    train_json_path = os.path.join(output, 'train', 'coco_annotations.json')
    val_json_path = os.path.join(output, 'val', 'coco_annotations.json')

    with open(train_json_path, 'w') as f:
        json.dump(train_json, f, indent=2)
    with open(val_json_path, 'w') as f:
        json.dump(val_json, f, indent=2)


def process_yolo(image_dir, train_ratio, output):
    images_dir = os.path.join(image_dir, 'images')
    labels_dir = os.path.join(image_dir, 'labels')
    image_files = os.listdir(images_dir)
    train_files = random.sample(image_files, int(len(image_files) * train_ratio))
    val_files = list(set(image_files) - set(train_files))

    train_images_dir = os.path.join(output, 'train', 'images')
    train_labels_dir = os.path.join(output, 'train', 'labels')
    val_images_dir = os.path.join(output, 'val', 'images')
    val_labels_dir = os.path.join(output, 'val', 'labels')

    for file_name in train_files:
        shutil.copy2(os.path.join(images_dir, file_name), train_images_dir)
        # label_file_name = file_name.replace('.jpg', '.txt')
        prefix = file_name[:-4]
        label_file_name = prefix + ".txt"
        shutil.copy2(os.path.join(labels_dir, label_file_name), train_labels_dir)

    for file_name in val_files:
        shutil.copy2(os.path.join(images_dir, file_name), val_images_dir)
        # label_file_name = file_name.replace('.jpg', '.txt')
        prefix = file_name[:-4]
        label_file_name = prefix + ".txt"
        shutil.copy2(os.path.join(labels_dir, label_file_name), val_labels_dir)

    # 复制 image_dir 下的其他文件到 output
    for item in os.listdir(image_dir):
        item_path = os.path.join(image_dir, item)
        if os.path.isfile(item_path) and item not in ['images', 'labels']:
            shutil.copy2(item_path, output)


# 主程序入口，处理命令行参数
if __name__ == '__main__':
    parser = argparse.ArgumentParser("处理数据集\n")
    parser.add_argument("--coco_json", help="COCO 格式的 JSON 文件路径")
    parser.add_argument("image_dir", help="图片文件夹路径")
    parser.add_argument("type", help="数据标注格式类型，支持 'coco' 或 'yolo'")
    parser.add_argument("--ratio", default=0.8, type=float, help="训练集的比例，默认是 0.8")
    parser.add_argument("output", help="输出路径")

    args = parser.parse_args()

    # 根据数据标注格式类型调用不同的处理函数
    if args.type == 'coco':
        ensure_coco_save_dir(args.output)
        process_coco(args.coco_json, args.image_dir, args.ratio, args.output)
    elif args.type == 'yolo':
        ensure_yolo_save_dir(args.output)
        process_yolo(args.image_dir, args.ratio, args.output)
