# Copyright (c) 2021 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.
"""
This code is based on https://github.com/niecongchong/RS-building-regularization
Ths copyright of niecongchong/RS-building-regularization is as follows:
Apache License [see LICENSE for details]
"""

import cv2
import matplotlib.pyplot as plt
import numpy as np
from .rdp_alg import rdp
from .cal_point import cal_ang, cal_dist, cal_azimuth
from .rotate_ang import Nrotation_angle_get_coor_coordinates, Srotation_angle_get_coor_coordinates
from .cal_line import line, intersection, par_line_dist, point_in_line


def boundary_regularization(contours, img_shape, epsilon=6):
    h, w = img_shape[0:2]
    # 轮廓定位
    contours = np.squeeze(contours)
    # 轮廓精简DP
    contours = rdp(contours, epsilon=epsilon)
    contours[:, 1] = h - contours[:, 1]
    # 轮廓规则化
    dists = []
    azis = []
    azis_index = []
    # 获取每条边的长度和方位角
    for i in range(contours.shape[0]):
        cur_index = i
        next_index = i + 1 if i < contours.shape[0] - 1 else 0
        prev_index = i - 1
        cur_point = contours[cur_index]
        nest_point = contours[next_index]
        prev_point = contours[prev_index]
        dist = cal_dist(cur_point, nest_point)
        azi = cal_azimuth(cur_point, nest_point)
        dists.append(dist)
        azis.append(azi)
        azis_index.append([cur_index, next_index])
    # 以最长的边的方向作为主方向
    longest_edge_idex = np.argmax(dists)
    main_direction = azis[longest_edge_idex]
    # 方向纠正，绕中心点旋转到与主方向垂直或者平行
    correct_points = []
    para_vetr_idxs = []  # 0平行 1垂直
    for i, (azi, (point_0_index,
                  point_1_index)) in enumerate(zip(azis, azis_index)):
        if i == longest_edge_idex:
            correct_points.append(
                [contours[point_0_index], contours[point_1_index]])
            para_vetr_idxs.append(0)
        else:
            # 确定旋转角度
            rotate_ang = main_direction - azi
            if np.abs(rotate_ang) < 180 / 4:
                rotate_ang = rotate_ang
                para_vetr_idxs.append(0)
            elif np.abs(rotate_ang) >= 90 - 180 / 4:
                rotate_ang = rotate_ang + 90
                para_vetr_idxs.append(1)
            # 执行旋转任务
            point_0 = contours[point_0_index]
            point_1 = contours[point_1_index]
            point_middle = (point_0 + point_1) / 2
            if rotate_ang > 0:
                rotate_point_0 = Srotation_angle_get_coor_coordinates(
                    point_0, point_middle, np.abs(rotate_ang))
                rotate_point_1 = Srotation_angle_get_coor_coordinates(
                    point_1, point_middle, np.abs(rotate_ang))
            elif rotate_ang < 0:
                rotate_point_0 = Nrotation_angle_get_coor_coordinates(
                    point_0, point_middle, np.abs(rotate_ang))
                rotate_point_1 = Nrotation_angle_get_coor_coordinates(
                    point_1, point_middle, np.abs(rotate_ang))
            else:
                rotate_point_0 = point_0
                rotate_point_1 = point_1
            correct_points.append([rotate_point_0, rotate_point_1])
    correct_points = np.array(correct_points)
    # 相邻边校正，垂直取交点，平行平移短边或者加线
    final_points = []
    final_points.append(correct_points[0][0])
    for i in range(correct_points.shape[0] - 1):
        cur_index = i
        next_index = i + 1 if i < correct_points.shape[0] - 1 else 0
        cur_edge_point_0 = correct_points[cur_index][0]
        cur_edge_point_1 = correct_points[cur_index][1]
        next_edge_point_0 = correct_points[next_index][0]
        next_edge_point_1 = correct_points[next_index][1]
        cur_para_vetr_idx = para_vetr_idxs[cur_index]
        next_para_vetr_idx = para_vetr_idxs[next_index]
        if cur_para_vetr_idx != next_para_vetr_idx:
            # 垂直取交点
            L1 = line(cur_edge_point_0, cur_edge_point_1)
            L2 = line(next_edge_point_0, next_edge_point_1)
            point_intersection = intersection(L1, L2)
            final_points.append(point_intersection)
        elif cur_para_vetr_idx == next_para_vetr_idx:
            # 平行分两种，一种加短线，一种平移，取决于距离阈值
            L1 = line(cur_edge_point_0, cur_edge_point_1)
            L2 = line(next_edge_point_0, next_edge_point_1)
            marg = par_line_dist(L1, L2)
            if marg < 3:
                # 平移
                point_move = point_in_line(
                    next_edge_point_0[0], next_edge_point_0[1],
                    cur_edge_point_0[0], cur_edge_point_0[1],
                    cur_edge_point_1[0], cur_edge_point_1[1])
                final_points.append(point_move)
                # 更新平移之后的下一条边
                correct_points[next_index][0] = point_move
                correct_points[next_index][1] = point_in_line(
                    next_edge_point_1[0], next_edge_point_1[1],
                    cur_edge_point_0[0], cur_edge_point_0[1],
                    cur_edge_point_1[0], cur_edge_point_1[1])
            else:
                # 加线
                add_mid_point = (cur_edge_point_1 + next_edge_point_0) / 2
                add_point_1 = point_in_line(
                    add_mid_point[0], add_mid_point[1], cur_edge_point_0[0],
                    cur_edge_point_0[1], cur_edge_point_1[0],
                    cur_edge_point_1[1])
                add_point_2 = point_in_line(
                    add_mid_point[0], add_mid_point[1], next_edge_point_0[0],
                    next_edge_point_0[1], next_edge_point_1[0],
                    next_edge_point_1[1])
                final_points.append(add_point_1)
                final_points.append(add_point_2)
    final_points.append(final_points[0])
    final_points = np.array(final_points)
    final_points[:, 1] = h - final_points[:, 1]
    final_points = final_points[np.newaxis, :].transpose((1, 0, 2))
    return final_points


# def rs_build_re(mask):
#     # 中值滤波，去噪
#     ori_img = cv2.medianBlur(mask, 5)
#     ori_img = cv2.cvtColor(ori_img, cv2.COLOR_BGR2GRAY)
#     ret, ori_img = cv2.threshold(ori_img, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
#     # 连通域分析
#     num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(ori_img, connectivity=8)
#     # 遍历联通域
#     for i in range(1, num_labels):
#         img = np.zeros_like(labels)
#         index = np.where(labels==i)
#         img[index] = 255
#         img = np.array(img, dtype=np.uint8)
#         regularization_contour = boundary_regularization(img).astype(np.int32)
#         cv2.polylines(img=mask, pts=[regularization_contour], isClosed=True, color=(255, 0, 0), thickness=5)
#         single_out = np.zeros_like(mask)
#         cv2.polylines(img=single_out, pts=[regularization_contour], isClosed=True, color=(255, 0, 0), thickness=5)
#         cv2.imwrite('single_out_{}.jpg'.format(i), single_out)
