# Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import copy
import collections
import math


def eval_detection(
    model,
    data_dir,
    ann_file,
    conf_threshold=None,
    nms_iou_threshold=None,
    plot=False,
    batch_size=1,
):
    from .utils import CocoDetection
    from .utils import COCOMetric
    import cv2
    from tqdm import trange
    import time

    if conf_threshold is not None or nms_iou_threshold is not None:
        assert (
            conf_threshold is not None and nms_iou_threshold is not None
        ), "The conf_threshold and nms_iou_threshold should be set at the same time"
        assert isinstance(
            conf_threshold, (float, int)
        ), "The conf_threshold:{} need to be int or float".format(conf_threshold)
        assert isinstance(
            nms_iou_threshold, (float, int)
        ), "The nms_iou_threshold:{} need to be int or float".format(nms_iou_threshold)
    eval_dataset = CocoDetection(data_dir=data_dir, ann_file=ann_file, shuffle=False)
    all_image_info = eval_dataset.file_list
    image_num = eval_dataset.num_samples
    eval_dataset.data_fields = {
        "im_id",
        "image_shape",
        "image",
        "gt_bbox",
        "gt_class",
        "is_crowd",
    }
    eval_metric = COCOMetric(
        coco_gt=copy.deepcopy(eval_dataset.coco_gt), classwise=False
    )
    scores = collections.OrderedDict()
    twenty_percent_image_num = math.ceil(image_num * 0.2)
    start_time = 0
    end_time = 0
    average_inference_time = 0
    im_list = list()
    im_id_list = list()
    for image_info, i in zip(
        all_image_info, trange(image_num, desc="Inference Progress")
    ):
        if i == twenty_percent_image_num:
            start_time = time.time()
        im = cv2.imread(image_info["image"])
        im_id = image_info["im_id"]
        if batch_size == 1:
            if conf_threshold is None and nms_iou_threshold is None:
                result = model.predict(im.copy())
            else:
                result = model.predict(im, conf_threshold, nms_iou_threshold)
            pred = {
                "bbox": [
                    [c] + [s] + b
                    for b, s, c in zip(result.boxes, result.scores, result.label_ids)
                ],
                "bbox_num": len(result.boxes),
                "im_id": im_id,
            }
            eval_metric.update(im_id, pred)
        else:
            im_list.append(im)
            im_id_list.append(im_id)
            # If the batch_size is not satisfied, the remaining pictures are formed into a batch
            if (i + 1) % batch_size != 0 and i != image_num - 1:
                continue
            if conf_threshold is None and nms_iou_threshold is None:
                results = model.batch_predict(im_list)
            else:
                model.postprocessor.conf_threshold = conf_threshold
                model.postprocessor.nms_threshold = nms_iou_threshold
                results = model.batch_predict(im_list)
            for k in range(len(im_list)):
                pred = {
                    "bbox": [
                        [c] + [s] + b
                        for b, s, c in zip(
                            results[k].boxes, results[k].scores, results[k].label_ids
                        )
                    ],
                    "bbox_num": len(results[k].boxes),
                    "im_id": im_id_list[k],
                }
                eval_metric.update(im_id_list[k], pred)
            im_list.clear()
            im_id_list.clear()

        if i == image_num - 1:
            end_time = time.time()
    average_inference_time = round(
        (end_time - start_time) / (image_num - twenty_percent_image_num), 4
    )
    eval_metric.accumulate()
    eval_details = eval_metric.details
    scores.update(eval_metric.get())
    scores.update({"average_inference_time(s)": average_inference_time})
    eval_metric.reset()
    return scores
