import json
import struct
import time
import urllib

import cv2
import numpy as np

import paths
from cv_cam import OpenCVFFMPEGCaptureWrapper, CvUrlListDetector


class BirdwingCaptureWrapper(OpenCVFFMPEGCaptureWrapper):

    ID = 'BIRDWING'
    ANTI_FLOOD_SLEEP = 0.01

    def get_auth_from_urls_file(self, cap_id):
        for line_words in BirdwingListDetector.load_camera_urls_file(full_string=True):
            if len(line_words) > 4:
                line_cap_id, _, client_id, client_secret, auth_code = line_words
                if line_cap_id == cap_id:
                    self.host = cap_id
                    self.client_id = client_id
                    self.client_secret = client_secret
                    self.auth_code = auth_code
                    self.access_token = self.request_access_token()
                    break
        else:
            self.access_token = None

    def request_access_token(self):
        args = {'response_type': 'token',
                  'client_id': self.client_id,
                  'client_secret': self.client_secret,
                  'auth_code': self.auth_code,
                  'context': 'camera'}
        url = 'http://' + self.cap_id + '/auth/?' + urllib.parse.urlencode(args)
        try:
            response = urllib.request.urlopen(url).read()
            return json.load(response).get('access_token')
        except urllib.error.HTTPError as e: 
            self.logger.info('HTTPError on getting access_token: ' + str(e))
        except:
            self.logger.exception('Exception on getting access_token: ' + str(e))

    def frame_loop(self):
        while not self.stop_flag and self.capture:
            if self.fails > self.MAX_FAILS:
                self.logger.error(f"Reached max failed count for {self.cap_id}. Stopping")
                self.fails = 0
                break
            if self.frame_consumed:
                self.update_frame()
                self.frame_comsumed = False
            else:
                time.sleep(self.ANTI_FLOOD_SLEEP)
            self.fails += 1
            with self.frame_lock:
                self.frame = None
            time.sleep(self.RETRY_SLEEP)
        with self.frame_lock:
            self.frame = None
        self.operational = False
        self.active = False
        if not self.stop_flag:
            self.logger.warning('Frame loop crash ' + self.log_id)

    def update_frame(self):
        try:
            url = f'http://{self.host}/camera?token=' + self.access_token
            frame_data = urllib.request.urlopen(url).read()
            width, height, image_format = struct.unpack("!III", frame_data[4:16])
            #image = frame_data[16:]
        except Exception as e:
            self.fails += 1
            self.access_token = self.request_access_token()
            self.logger.error("Frame request or parsing error: " + str(e))
        else:
            if width and height and image_format:
                try:
                    self.operational = True
                    self.fails += 0
                    if image_format == 1:
                        image = np.asarray(frame_data[16:], copy=None, dtype=np.uint8).reshape((height, width*2))
                        image = cv2.cvtColor(image, cv2.COLOR_YUV2BGR_YUY2) 
                        # image = cv2.cvtColor(image, cv2.COLOR_YUV2BGR_YUYV)
                        with self.frame_lock:
                            self.frame = image
                    elif image_format == 2:
                        with self.frame_lock:
                            self.frame = np.asarray(frame_data[16:], copy=None, dtype=np.uint8)
                    self.logger.warning('Failed to get camera frame (wrong type):' + str(image_format))
                except Exception as e:
                    self.logger.exception(str(e))
            self.logger.warning('Received an empty camera frame')


class BirdwingListDetector(CvUrlListDetector):

    CAPTURE_WRAPPER_CLASS = BirdwingCaptureWrapper
    IS_DEFAULT_FOR_EMPTY_CLASS = False
    FILES = (paths.CAMERA_URLS_FILE, paths.USER_CAMERA_URLS_FILE)
