#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved

import copy
import json
import numpy as np
import os
import sys
import pycocotools.mask as mask_utils

from detectron2.utils.env import seed_all_rng
from detectron2.utils.file_io import PathManager


def get_point_annotations(input_filename, output_filename, num_points_per_instance):
    with PathManager.open(input_filename, "r") as f:
        coco_json = json.load(f)

    coco_annos = coco_json.pop("annotations")
    coco_points_json = copy.deepcopy(coco_json)

    imgs = {}
    for img in coco_json["images"]:
        imgs[img["id"]] = img

    new_annos = []
    for ann in coco_annos:
        # convert mask
        t = imgs[ann["image_id"]]
        h, w = t["height"], t["width"]
        segm = ann.pop("segmentation")
        if type(segm) == list:
            # polygon -- a single object might consist of multiple parts
            # we merge all parts into one mask rle code
            rles = mask_utils.frPyObjects(segm, h, w)
            rle = mask_utils.merge(rles)
        elif type(segm["counts"]) == list:
            # uncompressed RLE
            rle = mask_utils.frPyObjects(segm, h, w)
        else:
            # rle
            rle = segm
        mask = mask_utils.decode(rle)
        new_ann = copy.deepcopy(ann)
        # sample points in image coordinates
        box = ann["bbox"]
        point_coords_wrt_image = np.random.rand(num_points_per_instance, 2)
        point_coords_wrt_image[:, 0] = point_coords_wrt_image[:, 0] * box[2]
        point_coords_wrt_image[:, 1] = point_coords_wrt_image[:, 1] * box[3]
        point_coords_wrt_image[:, 0] += box[0]
        point_coords_wrt_image[:, 1] += box[1]
        # round to integer coordinates
        point_coords_wrt_image = np.floor(point_coords_wrt_image).astype(int)
        # get labels
        assert (point_coords_wrt_image >= 0).all(), (point_coords_wrt_image, mask.shape)
        assert (point_coords_wrt_image[:, 0] < w).all(), (point_coords_wrt_image, mask.shape)
        assert (point_coords_wrt_image[:, 1] < h).all(), (point_coords_wrt_image, mask.shape)
        point_labels = mask[point_coords_wrt_image[:, 1], point_coords_wrt_image[:, 0]]
        # store new annotations
        new_ann["point_coords"] = point_coords_wrt_image.tolist()
        new_ann["point_labels"] = point_labels.tolist()
        new_annos.append(new_ann)
    coco_points_json["annotations"] = new_annos

    with PathManager.open(output_filename, "w") as f:
        json.dump(coco_points_json, f)

    print("{} is modified and stored in {}.".format(input_filename, output_filename))


if __name__ == "__main__":
    """
    Generate point-based supervision for COCO dataset.

    Usage:
        python tools/prepare_coco_point_annotations_without_masks.py \
            NUM_POINTS_PER_INSTANCE NUM_VERSIONS_WITH_DIFFERENT_SEED

    Example to generate point-based COCO dataset with 10 points per instance:
        python tools/prepare_coco_point_annotations_without_masks.py 10
    """

    # Fix random seed
    seed_all_rng(12345)

    assert len(sys.argv) >= 2, "Please provide number of points to sample per instance"
    dataset_dir = os.path.join(os.getenv("DETECTRON2_DATASETS", "datasets"), "coco/annotations")
    num_points_per_instance = int(sys.argv[1])
    if len(sys.argv) == 3:
        repeat = int(sys.argv[2])
    else:
        repeat = 1
    s = "instances_train2017"
    for version in range(repeat):
        print(
            "Start sampling {} points per instance for annotations {}.".format(
                num_points_per_instance, s
            )
        )
        get_point_annotations(
            os.path.join(dataset_dir, "{}.json".format(s)),
            os.path.join(
                dataset_dir,
                "{}_n{}_v{}_without_masks.json".format(s, num_points_per_instance, version + 1),
            ),
            num_points_per_instance,
        )
