# @Author  : lightXu
# @File    : sheet_server.py
# @Time    : 2018/12/19 0019 下午 14:33
import json
import os
import shutil
import time
import traceback
import uuid
import xml.etree.cElementTree as ET
import cv2

import numpy as np
from PIL import Image
from django.conf import settings
from segment.sheet_resolve.tools.tf_settings import xml_template_path
import segment.logging_config as logging
from segment.sheet_resolve.analysis.anchor.marker_detection import find_anchor
from segment.sheet_resolve.analysis.resolve import choice, choice_m_row_col
from segment.sheet_resolve.analysis.resolve import cloze
from segment.sheet_resolve.analysis.resolve import exam_number_row_col
from segment.sheet_resolve.analysis.resolve import sheet
from segment.sheet_resolve.analysis.resolve import solve, solve_with_number, cloze_with_number
from segment.sheet_resolve.analysis.sheet.analysis_sheet import box_region_format, question_number_format
from segment.sheet_resolve.analysis.sheet.sheet_points import get_sheet_points
from segment.sheet_resolve.analysis.sheet.sheet_points_total import get_sheet_number_total
from segment.sheet_resolve.tools import utils
from segment.sheet_resolve.tools.brain_api import get_ocr_text_and_coordinate, change_format_baidu_to_google

logger = logging.getLogger(settings.LOGGING_TYPE)


def decide_blank_sheet(image):
    if len(image.shape) <= 2:
        gray_image = image
    else:
        gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    height = gray_image.shape[0]
    width = gray_image.shape[1]
    if max(height, width) > 800:
        percent = max(height, width) / 800
        new_x = int(width * percent)
        new_y = int(height * percent)

        gray_image = cv2.resize(gray_image, (new_x, new_y), interpolation=cv2.INTER_AREA)

    if height > width:  # 纵向
        image = gray_image[height//2:, :]
        PIX_VALUE_LOW = 25.0  # 二进制参数
        PIX_VALUE_HIGH = 220  # 原始图像参数

    else:  # 横向
        image = gray_image[:, width // 2:]
        PIX_VALUE_LOW = 15.0
        PIX_VALUE_HIGH = 250

    bin_img = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    bin_img_mean = np.mean(bin_img)
    img_raw_mean = np.mean(image)

    print(bin_img_mean, img_raw_mean)
    blank_cond = bin_img_mean < PIX_VALUE_LOW or img_raw_mean > PIX_VALUE_HIGH
    return blank_cond


def convert_pil_to_jpeg(raw_img):
    if raw_img.mode == 'L':
        channels = raw_img.split()
        img = Image.merge("RGB", (channels[0], channels[0], channels[0]))
    elif raw_img.mode == 'RGB':
        img = raw_img
    elif raw_img.mode == 'RGBA':
        img = Image.new("RGB", raw_img.size, (255, 255, 255))
        img.paste(raw_img, mask=raw_img.split()[3])  # 3 is the alpha channel
    else:
        img = raw_img
    open_cv_image = cv2.cvtColor(np.asarray(img), cv2.COLOR_RGB2BGR)
    return img, open_cv_image


def handle_uploaded_xml_file(f, save_path):
    with open(save_path, 'wb+') as destination:
        for chunk in f.chunks():
            destination.write(chunk)


def generate_serial_number(time_str, sheet_big_boxes):
    if len(sheet_big_boxes.objects.all()) < 1:
        last_number_gen = time_str + '000001'
    else:
        objects = sheet_big_boxes.objects.latest('update_time')
        last_number_in_db = objects.series_number
        if time_str in last_number_in_db[0:9]:
            last_number_gen = str(int(last_number_in_db) + 1)
        else:
            last_number_gen = time_str + '000001'

    return last_number_gen


def save_raw_image_with_paper_id(subject, paper_id, img_file, analysis_type):
    time_str = time.strftime('%Y-%m-%d', time.localtime(time.time()))
    # 随机生成新的图片名，自定义路径。
    ext = img_file.name.split('.')[-1]
    # raw_name = img_file.name[0:-len(ext) - 1]
    # file_name = '{}_{}.{}'.format(raw_name, uuid.uuid4().hex[:10], 'jpg')
    file_name = '{}.{}'.format(paper_id, ext)

    raw_img = Image.open(img_file)  # 读取上传的网络图像
    save_dir = os.path.join(settings.MEDIA_ROOT, analysis_type, subject, time_str)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    save_path = os.path.join(save_dir, file_name)

    pil_img, open_cv_image = convert_pil_to_jpeg(raw_img)

    try:
        pil_img.save(save_path)
    except Exception as e:
        raise e

    url_path = os.path.join(settings.MEDIA_URL, analysis_type, subject, time_str, file_name).replace('\\', '/')
    return save_path, open_cv_image, url_path


def save_raw_image_without_segment(subject, datetime, img_file, analysis_type):
    # 随机生成新的图片名，自定义路径。
    ext = img_file.name.split('.')[-1]
    raw_name = img_file.name[0:-len(ext) - 1]
    file_name = '{}_{}.{}'.format(raw_name, uuid.uuid4().hex[:10], 'jpg')

    raw_img = Image.open(img_file)  # 读取上传的网络图像
    save_dir = os.path.join(settings.MEDIA_ROOT, analysis_type, subject, datetime)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    save_path = os.path.join(save_dir, file_name)

    pil_img, open_cv_image = convert_pil_to_jpeg(raw_img)
    try:
        pil_img.save(save_path)
        shutil.copy(save_path, save_path.replace('.jpg', '_small.jpg'))
    except Exception as e:
        raise e

    url_path = os.path.join(settings.MEDIA_URL, analysis_type, subject, datetime, file_name).replace('\\', '/')
    return save_path, open_cv_image, url_path


def sheet_big_boxes_resolve(series_number, image, saved_path, subject, sheet_sess, ocr=''):
    status = 1
    conf_thresh_0 = 0.7
    mns_thresh_0 = 0.3

    sheets_dict_0 = ''
    xml_save_path = ''
    try:
        sheets_dict_0, xml_save_path = sheet(series_number, saved_path, image,
                                             conf_thresh_0, mns_thresh_0, subject, sheet_sess, ocr)

    except Exception as e:
        status = 0
        logger.info('试卷：{} 答题卡区域解析失败: {}'.format(saved_path, e))

    return status, sheets_dict_0, xml_save_path


def sheet_small_boxes_resolve(raw_img, sheet_dict, choice_sess, cloze_sess, xml_save_path):
    conf_thresh_0 = 0.7
    mns_thresh_0 = 0.3
    regions = sheet_dict['regions']
    classes_name = str([ele['class_name'] for ele in regions])

    sheet_dict.pop('regions')
    json.dumps(sheet_dict, ensure_ascii=False)
    if 'choice' in classes_name:
        try:
            sheet_dict['choice'] = choice(raw_img, regions, xml_save_path, conf_thresh_0, mns_thresh_0, choice_sess)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 选择题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'exam_number' in classes_name:
        try:
            # sheet_dict['exam_number'] = exam_number(raw_img, regions, xml_save_path)
            sheet_dict['exam_number'] = exam_number_row_col(raw_img, regions, xml_save_path)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 考号区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'cloze' in classes_name:
        try:
            sheet_dict['cloze'] = cloze(raw_img, regions, xml_save_path, conf_thresh_0, mns_thresh_0, cloze_sess)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 填空题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'solve' in classes_name:
        try:
            # solve_list, mark_list = solve(raw_img, regions, xml_save_path,)
            # sheet_dict['solve'] = solve_list
            # sheet_dict['mark'] = mark_list
            sheet_dict['solve'] = solve(raw_img, regions, xml_save_path)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 解答题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'qr_code' in classes_name:
        try:
            for ele in regions:
                if 'qr_code' == ele['class_name']:
                    sheet_dict['qr_code'] = ele['bounding_box']
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 二维码区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'bar_code' in classes_name:
        try:
            for ele in regions:
                if 'bar_code' == ele['class_name']:
                    sheet_dict['bar_code'] = ele['bounding_box']
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 条形码区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    return sheet_dict


def sheet_row_col_resolve(raw_img, sheet_dict, choice_sess, cloze_sess, xml_save_path):
    conf_thresh_0 = 0.7
    mns_thresh_0 = 0.3
    regions = sheet_dict['regions']
    classes_name = str([ele['class_name'] for ele in regions])

    region_tmp = regions.copy()
    # json.dumps(sheet_dict, ensure_ascii=False)
    # if 'choice' in classes_name:
    #     try:
    #         # sheet_dict['choice'] = choice(raw_img, regions, xml_save_path, conf_thresh_0, mns_thresh_0, choice_sess)
    #         choice_dict_list = choice_row_col(raw_img, regions, xml_save_path, conf_thresh_0, mns_thresh_0, choice_sess)
    #         if len(choice_dict_list) > 0:
    #             region_tmp.extend(choice_dict_list)
    #     except Exception as e:
    #         traceback.print_exc()
    #         logger.info('试卷：{} 选择题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'choice_m' in classes_name:
        try:
            choice_dict_list = choice_m_row_col(raw_img, regions, xml_save_path)
            if len(choice_dict_list) > 0:
                region_tmp.extend(choice_dict_list)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 选择题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'exam_number' in classes_name:
        try:
            # sheet_dict['exam_number'] = exam_number(raw_img, regions, xml_save_path)
            exam_number_dict_list = exam_number_row_col(raw_img, regions, xml_save_path)
            if len(exam_number_dict_list) > 0:
                region_tmp.extend(exam_number_dict_list)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 考号区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'cloze' in classes_name:
        try:
            cloze_dict_list = cloze(raw_img, regions, xml_save_path, conf_thresh_0, mns_thresh_0, cloze_sess)
            if len(cloze_dict_list) > 0:
                region_tmp.extend(cloze_dict_list)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 填空题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    sheet_dict.update({'regions': region_tmp})
    return sheet_dict


def sheet_detail_resolve(raw_img, sheet_dict, xml_save_path, shrink=True):
    regions = sheet_dict['regions']
    classes_names_list = set([ele['class_name'] for ele in regions])

    region_tmp = regions.copy()
    # json.dumps(sheet_dict, ensure_ascii=False)
    if 'choice_m' in classes_names_list:
        try:
            choice_dict_list = choice_m_row_col(raw_img, regions, xml_save_path)
            if shrink:
                for ele in choice_dict_list:
                    if 'all_small_coordinate' in ele.keys():
                        ele.pop('all_small_coordinate')
            region_tmp = [ele for ele in region_tmp if ele['class_name'] != 'choice_m']  # 重名
            if len(choice_dict_list) > 0:
                region_tmp.extend(choice_dict_list)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 选择题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'exam_number' in classes_names_list:
        try:
            exam_number_dict_list = exam_number_row_col(raw_img, regions, xml_save_path)
            for ele in exam_number_dict_list:
                ele.pop('all_small_coordinate')
            if len(exam_number_dict_list) > 0:
                region_tmp.extend(exam_number_dict_list)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 考号区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'solve' or 'solve0' or 'composition' or 'composition0' in classes_names_list:
        try:
            solve_number = solve_with_number(region_tmp, xml_save_path)
            region_tmp = [ele for ele in region_tmp if 'solve' not in ele['class_name']]  # 重名
            region_tmp = [ele for ele in region_tmp if 'composition' not in ele['class_name']]
            if len(solve_number) > 0:
                region_tmp.extend(solve_number)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 解答题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    if 'cloze' in classes_names_list or 'cloze_s' in classes_names_list:
        try:
            cloze_number = cloze_with_number(region_tmp, xml_save_path)
            region_tmp = [ele for ele in region_tmp if 'cloze' not in ele['class_name']]  # 重名
            if len(cloze_number) > 0:
                region_tmp.extend(cloze_number)
        except Exception as e:
            traceback.print_exc()
            logger.info('试卷：{} 解答题区域解析失败: {}'.format(xml_save_path.replace('xml', '.jpg'), e))

    sheet_dict.update({'regions': region_tmp})
    return sheet_dict


def sheet_points(sheet_dict_list, image_list, ocr_list, if_ocr=False):
    sheet_list = []
    for index, ele in enumerate(sheet_dict_list):
        ocr_res = ocr_list[index]
        h, w = image_list[index].shape[0], image_list[index].shape[1]
        sheet_dict = {'sheet_dict': sheet_dict_list[index], 'ocr': ocr_res, 'shape': (h, w), 'raw_image': image_list[index]}
        sheet_list.append(sheet_dict)

    try:
        res = get_sheet_points(sheet_list)
        sheet_dict_list = [ele['sheet_dict'] for ele in res]
    except Exception as e:
        traceback.print_exc()
        sheet_dict_list = [ele['sheet_dict'] for ele in sheet_list]

    try:
        sheet_total_list = []
        for index, ele in enumerate(sheet_dict_list):
            ocr_res = change_format_baidu_to_google(ocr_list[index])
            sheet_dict = get_sheet_number_total(ele, ocr_res, image_list[index])
            sheet_total_list.append(sheet_dict)
    except Exception as e:
        traceback.print_exc()
        sheet_total_list = sheet_dict_list

    if if_ocr:
        for index, ele in enumerate(sheet_total_list):
            ele.update({'sheet_ocr': ocr_list[index]})
    return sheet_total_list


def sheet_format_output(init_number, crt_numbers, sheet_dict, image, subject, shrink):
    sheet_dict = box_region_format(sheet_dict, image, subject, shrink)
    sheet_dict, init_number, crt_numbers = question_number_format(init_number, crt_numbers, sheet_dict)
    return sheet_dict, init_number, crt_numbers


def sheet_anchor(image):
    anchor_list = find_anchor(image)
    return anchor_list


def gen_xml(sheet_region_dict, xml_path):
    tree = ET.parse(xml_template_path)
    for index_num, box in enumerate(sheet_region_dict):
        if len(box['bounding_box']) > 0:
            abcd = box['bounding_box']
            name = box["class_name"]
            box_tmp = box.copy()
            box_tmp.pop('bounding_box')
            box_tmp.pop('class_name')
            info = str(box_tmp)
            name = '{}_{}'.format(name, info)
            tree = utils.create_xml(name, tree,
                                    abcd['xmin'], abcd['ymin'],
                                    abcd['xmax'], abcd['ymax'])

    tree.write(xml_path)
