# !/usr/bin/python3
import gc
import json
import os
from aiohttp import web
import aiohttp_cors
from multiprocessing import Process, cpu_count
from socket import SOL_SOCKET, SO_REUSEADDR, socket

import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

import paddlehub as hub
import cv2
import requests
import numpy as np
import time
import signal

from libs.app_tool import AppTool

LISTEN_PORT = 8903

class PaddlleImg:
    def __init__(self):
        self.app_tool = AppTool('pp_orc.log')
        self.ZK_CONF = self.app_tool.zk()

        self.hostname = os.uname()[1]
        #chinese_ocr_db_crnn_server 比较慢，效果比较好
        self.ocr_module = hub.Module(name="chinese_ocr_db_crnn_mobile", enable_mkldnn=True)
        #pyramidbox_face_detection, ultra_light_fast_generic_face_detector_1mb_640 pyramidbox_lite_server
        self.face_module = hub.Module(name="pyramidbox_lite_server")
        self.logger = self.app_tool.logger

    def download_img(self, img_url):
        headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36'}
        response = requests.get(img_url, verify=False, timeout=10, headers=headers)
        arr = np.asarray(bytearray(response.content), dtype=np.uint8)
        img = cv2.imdecode(arr, -1) # 'Load it as it is'
        return img

    def do_orc(self, img_url):
        try:
            img = self.download_img(img_url)
            h, w, c = img.shape
            ##高度太小的时候配置
            time1 = time.time()
            self.logger.info("start do_ocr img [%s] and height is [%s x %s] anc channels %s " % (img_url, h, w, c))
            if h < 100:
                scale_percent = int(100 + ((100 - h) / h) * 100)
                width = int(w * scale_percent / 100)
                height = int(h * scale_percent / 100)
                dim = (width, height)
                img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)

            #convert bgra to bga    
            if c > 3:
                self.logger.info("convert img [%s] COLOR_BGRA2BGR" % img_url)
                img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)

            results = self.ocr_module.recognize_text(
                                images=[img],         # 图片数据，ndarray.shape 为 [H, W, C]，BGR格式；
                                use_gpu=False,            # 是否使用 GPU；若使用GPU，请先设置CUDA_VISIBLE_DEVICES环境变量
                                visualization=False,       # 是否将识别结果保存为图片文件；
                                box_thresh=0.5,           # 检测文本框置信度的阈值；
                                text_thresh=0.5) 
            gc.collect()
            time2 = time.time()
            self.logger.info("end do_ocr img [%s] and exec  [%0.3f] ms" % (img_url, ((time2-time1)*1000.0)))
            for result in results:
                data = result['data']
                return self.app_tool.success_result({'data': data})
            
        except Exception as e:
            return self.app_tool.fail_result("ocr recognize fail")

    def do_face(self, img_url):
        try:
            img = self.download_img(img_url)
            h, w, c = img.shape
            time1 = time.time()
            self.logger.info("start do_face img [%s] and height is [%s x %s] anc channels %s " % (img_url, h, w, c))
            if c > 3:
                self.logger.info("convert img [%s] COLOR_BGRA2BGR" % img_url)
                img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)

            signal.alarm(20)
            results = self.face_module.face_detection(
                            images=[img],         #  图片数据，ndarray.shape 为 [H, W, C]，BGR格式；
                            use_gpu=False,            # 是否使用 GPU；若使用GPU，请先设置CUDA_VISIBLE_DEVICES环境变量
                            visualization=False,       # 是否将识别结果保存为图片文件；
                            confs_threshold=0.6)           #  置信度的阈值。pyramidbox_face_detection 为 score_thresh ultra_light_fast_generic_face_detector_1mb_640 为 confs_threshold
            gc.collect()
            time2 = time.time()
            self.logger.info("end do_face img [%s] and exec  [%0.3f] ms" % (img_url, ((time2-time1)*1000.0)))
            for result in results:
                data = result['data']
                return self.app_tool.success_result({'data': data})
        except Exception as e:
            if str(e) == 'Timeout':
                raise TimeoutError('Execution timed out')
            
            return self.app_tool.fail_result("face detection fail")
        finally:
            signal.alarm(0)

    async def ocr(self, request):
        form = await request.post()
        resp = self.app_tool.fail_result("格式解析失败")
        if request.method == 'POST':
            img_url = None if 'img_url' not in form else form['img_url']
            sign = None if 'sign' not in form else form['sign']
            is_self_ip = self.app_tool.is_self_ip(request)
            try:
                is_vaild = self.app_tool.verify_post_sign(sign, img_url)
                if (is_vaild is False and is_self_ip is False) or img_url is None:
                    return web.Response(text=json.dumps(self.app_tool.fail_result("非法的请求参数！")))
                
                resp = self.do_orc(img_url)
                return web.Response(text=json.dumps(resp))
            except ValueError as e:
                 return web.Response(text=json.dumps(resp))
        else:
             return web.Response(text=json.dumps(resp))

    async def face(self, request):
        form = await request.post()
        resp = self.app_tool.fail_result("格式解析失败")
        if request.method == 'POST':
            img_url = None if 'img_url' not in form else form['img_url']
            sign = None if 'sign' not in form else form['sign']
            is_self_ip = self.app_tool.is_self_ip(request)
            try:
                is_vaild = self.app_tool.verify_post_sign(sign, img_url)
                if (is_vaild is False and is_self_ip is False) or img_url is None:
                    return web.Response(text=json.dumps(self.app_tool.fail_result("非法的请求参数！")))
                
                resp = self.do_face(img_url)
                return web.Response(text=json.dumps(resp))
            except ValueError as e:
                 return web.Response(text=json.dumps(resp))
        else:
             return web.Response(text=json.dumps(resp))

    
    def serve_multiple(self, app, workers):
        sock = socket()
        sock.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
        sock.bind(('0.0.0.0', LISTEN_PORT))
        sock.set_inheritable(True)

        processes = []
        for i in range(workers):
            process = Process(target=web.run_app, name=f'worker-{i}', kwargs=dict(app=app, sock=sock))
            process.daemon = True
            process.start()
            processes.append(process)

        try:
            for process in processes:
                process.join()
        except KeyboardInterrupt:
            pass
        finally:
            for process in processes:
                process.terminate()
            sock.close()

    def main_multi(self, process_cnt):
        app = web.Application()
        app.router.add_get('/ocr', self.ocr, name="PPOcr")
        app.router.add_post('/ocr', self.ocr, name="PPOcr")
        app.router.add_get('/face', self.face, name="PPFace")
        app.router.add_post('/face', self.face, name="PPFace")
        cors = aiohttp_cors.setup(app, defaults={
            "*": aiohttp_cors.ResourceOptions(
                allow_credentials=True,
                expose_headers="*",
                allow_headers="*",
            )
        })
        self.serve_multiple(app, process_cnt)
    def main(self):
        app = web.Application()
        app.router.add_get('/ocr', self.ocr, name="PPOcr")
        app.router.add_post('/ocr', self.ocr, name="PPOcr")
        app.router.add_get('/face', self.face, name="PPFace")
        app.router.add_post('/face', self.face, name="PPFace")
        web.run_app(app, port=LISTEN_PORT)

if __name__ == '__main__':
    process_cnt = 1
    PP = PaddlleImg()
    PP.main_multi(process_cnt)

