from base_resource import BaseResource, SendBaseResource
from mysql_model import DeviceModel, PersonModel
from influx_model import BaseInfluxModel, PunchInfluxModel,LogInfluxModel
# from docker_model import RTMPDockerModel
from line_model import PunchNotifyModel
from flask_restful import request
from datetime import datetime, timedelta
from io import BytesIO
import os, math, calendar, pandas as pd, numpy as np, json, base64, pytz
from broadcast_resource import broadcast_with_temp

import requests
import hashlib
from requests.auth import HTTPDigestAuth

env = os.environ
rtmp_host = env['RTMP_HOST']      if 'RTMP_HOST' in env else "localhost"
rtmp_port = int(env['RTMP_PORT']) if 'RTMP_PORT' in env else 1935

tw = pytz.timezone('Asia/Taipei')

# 設備基礎參數
CM26EWH_username="admin"
CM26EWH_password="admin"
flask_host="192.168.5.185"
flask_port=8001
duration=60

Devices = {}
CM26EWH_subscription = {}
Persons = {}
Influxs = {}
Dockers = {}

# 排程任務 : 持續更新訂閱人臉辨識攝影機
def CM26EWH_subscription_aps(x):
    for CM26EWH_device in CM26EWH_subscription.keys():
        if(CM26EWH_subscription[CM26EWH_device].issubscribe):
            # 已訂閱 -> 更新訂閱
            CM26EWH_subscription[CM26EWH_device].update()
#             print("更新訂閱")
        if(not CM26EWH_subscription[CM26EWH_device].issubscribe):
            # 未訂閱或更新失敗 -> 重新訂閱
            ret=CM26EWH_subscription[CM26EWH_device].subscription(flask_host,flask_port,duration)
#             print("重新訂閱")

# 產生 RFC 2617 response header
def RFC2617_response_header(username,password,h,method,uri):
    realm=h.split("Digest realm=")[1].split(',')[0].strip('"')
    nonce=h.split("nonce=")[1].split(',')[0].strip('"')
    ha1_md5 = hashlib.md5()
    
    ha1_md5.update("{}:{}:{}".format(username,realm,password).encode("utf-8"))
    HA1=ha1_md5.hexdigest().upper()

    ha2_md5 = hashlib.md5()
    ha2_md5.update("{}:{}".format(method,uri).encode("utf-8"))
    HA2=ha2_md5.hexdigest().upper()

    response_md5 = hashlib.md5()
    response_md5.update("{}:{}:{}".format(HA1,nonce,HA2).encode("utf-8"))
    response_digest=response_md5.hexdigest().upper()
    
    headers = {
    "Authorization": '''Digest username="{}", realm="{}", nonce="{}",uri="{}", response="{}", opaque=""'''.format(username,realm,nonce,uri,response_digest),
    "Cache-Control": "no-cache"
    }
    
    return headers

class CM26EWH_Personinfo(BaseResource):
    def __init__(self):
        super().__init__()
    def post(self):
        request_data=request.data
        person_info=json.loads(request_data.decode("utf-8").replace("\r\n",""))
        device_uuid=person_info["DeviceCode"]
        device = Devices[device_uuid] if device_uuid in Devices else DeviceModel.FromDB(device_uuid, self.log.SetMessage)
        if device.isExist:
            Devices[device_uuid] = device
             # Person Data
            for face_info in person_info["PersonEventInfo"]["FaceInfoList"]:
                person_id = face_info["CompareInfo"]["PersonInfo"]["PersonName"]
                person_name = face_info["CompareInfo"]["PersonInfo"]["PersonName"]
                person_temperature = 0.0
                person_image = face_info['CompareInfo']['SnapshotImage']['SmallImage']['Data']
                person_time=datetime.fromtimestamp(face_info["PassingTime"])
                person = Persons[person_id] if person_id in Persons else PersonModel.FromDB(person_id, self.log.SetMessage)
                if not person.isExist:
                    person = PersonModel.FromDB("unknown", self.log.SetMessage)
                    person.SetName("Guest")
                person_id = person.GetID()
                person_name = person.GetName()
                filename = '{0}_{1}'.format(person_id, person_time.strftime('%Y_%m_%d_%H_%M_%S'))
                filename = '{0}.jpg'.format(os.path.join('static', 'face', filename))
                if not os.path.isfile(filename) and len(person_image) > 0:
                    with open(filename, 'wb') as f:
                        f.write(base64.b64decode(person_image))
                person_image = filename
                
                if person.HasToken():
                    token = person.GetToken()
                    notify = PunchNotifyModel(token)
                    self.message = "{0} notify failed".format(token) if not notify.Broadcast(person_time, person_name, person_temperature, filename) else self.message
                if person.isExist: Persons[person_id] = person
                # Record Data
                device_host = device.GetHost()
                device_location = device.GetLocation()
                
                person_similarity=float(face_info["CompareInfo"]["Similarity"])
                person_similarity = person_similarity / 100 if person_similarity > 100 else person_similarity
                person_type = person.GetType()
                record = PunchInfluxModel(device_uuid, device_host, device_location, person_time)
                record.SetID(person_id)
                record.SetName(person_name)
                record.SetType(person_type)
                record.SetSimilarity(person_similarity)
                record.SetTemperature(person_temperature)
        #         record.SetPosition(person_position)
                record.SetImage(person_image)
                influx = Influxs[device_host] if device_host in Influxs else BaseInfluxModel()
                influx.Add(record)
                self.message = "influxdb upload failed" if not influx.Upload() else self.message
                Influxs[device_host] = influx
                
# 訂閱類別
class CM26EWH_Subscription():
    def __init__(self,camera_path,username,password):
        super().__init__()
        self.camera_path=camera_path
        self.username=username
        self.password=password
        self.issubscribe=False
        self.expired=0
        self.now=0
        self.event_id=None
        self.timer=None
        self.duration=30
        self.trytimes=0
        self.ip=None
        self.port=0
    def update(self):
        method="PUT"

        uri="/LAPI/V1.0/System/Event/Subscription/{}".format(self.event_id)

        response=requests.get(self.camera_path+uri)
        json_request={
            "Duration":self.duration ,# 訂閱週期，單位秒，[30,3600]
        }
        if(response.status_code==401):
            # 驗證過期
            # 重新計算憑證
            headers=RFC2617_response_header(self.username,self.password,response.headers['WWW-Authenticate'],method,uri)
            headers['Content-type']='application/json'
            headers['Accept']='text/plain'
            response=requests.put(self.camera_path+uri,headers=headers,json=json_request,auth=HTTPDigestAuth(self.username, self.password))
        # 驗證成功
        response_json=json.loads(response.content.decode("utf-8").replace("\r\n","").replace("\t","").replace("'",'"'))
        if(response_json["Response"]["ResponseString"]=="Succeed"):
            self.expired=response_json["Response"]['Data']['TerminationTime']
            self.now=response_json["Response"]['Data']['CurrentTime']
            self.issubscribe=True
        else:
            self.issubscribe=False
                                 
    def subscription(self,ip,port,duration):
        if(self.issubscribe):
            return False
        
        self.ip=ip
        self.port=port
        method="POST"

        uri="/LAPI/V1.0/System/Event/Subscription"

        response=requests.get(self.camera_path+uri)
        json_request={
            "AddressType": 0, # IP 位置類型，0 -> IPv4， 1 -> IPv6， 2 -> domains， 3 -> Ipv4 與 IPv6，
            "IPAddress": ip, # Ipv4 位置
            "Port": port, # port
            "Duration":duration ,# 訂閱週期，單位秒，[30,3600]
            "Type":16, # 訂閱類型，不帶訂閱所有警告
            "SubscribePersonCondition":{ # 訂閱人臉庫類型
                "LibIDNum":0xFFFF, # 訂閱庫 ID 數量，值為 0xFFFF時，代表全部訂閱
        #         "LibIDList"[ # 訂閱的人臉庫ID，LibIDNum為 0xFFFF時，可不帶入
        #             {
        #                 "LibID":1595305841
        #             }
        #         ]
            }
        }
        if(response.status_code==401):
            # 驗證過期
            # 重新計算憑證
            headers=RFC2617_response_header(self.username,self.password,response.headers['WWW-Authenticate'],method,uri)
            headers['Content-type']='application/json'
            headers['Accept']='text/plain'
            response=requests.post(self.camera_path+uri,headers=headers,json=json_request,auth=HTTPDigestAuth(self.username,self.password))
        # 驗證成功
        response_json=json.loads(response.content.decode("utf-8").replace("\r","").replace("\t","").replace("\n",""))
        self.event_id=response_json["Response"]['Data']['ID']
        self.expired=response_json["Response"]['Data']['TerminationTime']
        self.now=response_json["Response"]['Data']['CurrentTime']
        self.issubscribe=True
        self.duration=duration
        return True
    def loop_update(self):
        if(self.issubscribe):
            self.update()
        if(not self.issubscribe):
            # 錯誤嘗試
#             print("錯誤重試")
            self.issubscribe=self.subscription(self.ip,self.port,self.duration)
            if(not self.issubscribe and self.trytimes <= 3):
                # 嘗試失敗
                # 重試
                # 增加錯誤次數
                self.trytimes+=1
                self.loop_update()
        if(self.issubscribe):
            # 更新成功
            # 持續監控
            self.timer=Timer(self.duration,self.loop_update)
            self.timer.start()
#             print("更新成功")
            
    def cancel(self):
        self.timer.cancel()
        method="DELETE"
        uri="/LAPI/V1.0/System/Event/Subscription/{}".format(self.event_id)
        response=requests.get(self.camera_path+uri)

        if(response.status_code==401):
            # 驗證過期
            # 重新計算憑證
            headers=RFC2617_response_header(self.username,self.password,response.headers['WWW-Authenticate'],method,uri)
            response=requests.delete(camera_path+uri,headers=headers,auth=HTTPDigestAuth(self.username, self.password))
        # 驗證成功
        response_json=json.loads(response.content.decode("utf-8").replace("\r","").replace("\t","").replace("\n",""))
        if(response_json["Response"]["ResponseString"]=="Succeed"):
            self.issubscribe=False
            self.trytimes=4
#             print("取消成功")
        else:
            self.issubscribe=False
            self.trytimes=4
#             print("取消失敗")
            
            
# 初始化現有設備清單
devices=DeviceModel.AllFromDB(LogInfluxModel("初始化人臉辨識攝影機設備清單"))
for device in devices:
    # 判斷是否為"人臉辨識攝影機"
    if(device.GetType()==2):
        Devices[device.GetID()]=device
        CM26EWH_subscription[device.GetID()]=CM26EWH_Subscription("http://{}:{}".format(device.GetHost(),device.GetPort()),CM26EWH_username,CM26EWH_password)