# Copyright 3D Control Systems, Inc. All Rights Reserved 2017-2019.
# Built in San Francisco.

# This software is distributed under a commercial license for personal,
# educational, corporate or any other use.
# The software as a whole or any parts of it is prohibited for distribution or
# use without obtaining a license from 3D Control Systems, Inc.

# All software licenses are subject to the 3DPrinterOS terms of use
# (available at https://www.3dprinteros.com/terms-and-conditions/),
# and privacy policy (available at https://www.3dprinteros.com/privacy-policy/)

import sys
import time
import logging
import hashlib

from io import BytesIO

import config
import http_client
import log
from dual_cam import Camera

class FakeList(list):
    def __getitem__(self, _):
        return 0


class M5thCamera(Camera):
    def __init__(self, parent):
        self.logger = logging.getLogger(__name__)
        self.stop_flag = False
        self.parent = parent
        self.ip = parent.ip
        self.http_client = None
        self.cloud_camera_number = None
        self.init_settings()
        self.init_parameters()
        self.init()
        self.init_user_token()

    def init_parameters(self):
        super().init_parameters()
        self.resized = FakeList()
        self.fails = FakeList()

    def init_user_token(self):
        self.http_client = http_client.HTTPClient(self, keep_connection_flag=True, logging_level=logging.WARNING)
        self.token = config.get_app().user_login.user_token

    def init(self):
        Camera.init(self)
        self.min_loop_time = 1

    @log.log_exception
    def main_loop(self):
        self.init_parameters()
        if not self.ip:
            self.logger.error('No IP. Stopping...')
            return
        self.makerbot = self.parent.makerbot
        settings = config.get_settings()
        old_camera_number = self.get_number_by_ip()
        self.active_camera_number = old_camera_number # in case of old numeration of migration fail
        new_camera_number = int(hashlib.shake_128(self.ip.encode(errors='ignore')).hexdigest(4), 16)
        if settings['camera'].get('old_ip_numeration'):
            self.cloud_camera_number = old_camera_number
            self.camera_name = str(self.cloud_camera_number)
            if settings['camera'].get('migrate_numeration'):
                number_migr_dict = {old_camera_number: new_camera_number}
                resp_json = self.http_client.change_camera_number(self.token, number_migr_dict)
                if resp_json and isinstance(resp_json, dict) and resp_json.get('success'):
                    settings = config.get_settings()
                    settings['cam_number_migration_success'] = True
                    config.Config.instance().save_settings(settings)
                    self.logger.info('Camera number migration success: ' + str(number_migr_dict))
                    self.cloud_camera_number = new_camera_number
                    self.camera_name = str(self.ip)
                else:
                    self.logger.info('Camera number migration fail: ' + str(resp_json))
        else:
            self.cloud_camera_number = new_camera_number
            self.camera_name = str(self.ip)
        time.sleep(self.min_loop_time)
        while not self.stop_flag and not self.parent.stop_flag:
            self.logger.debug("camera %s cycle" % self.cloud_camera_number)
            frame_start_time = time.monotonic()
            frame = self.make_shot()
            if frame:
                # self.logger.debug("Got frame from camera N" + str(number))
                self.send_frame(frame)
            else:
                self.logger.warning("No frame from camera N" + str(self.cloud_camera_number))
            while time.monotonic() < frame_start_time + 0.001 + self.min_loop_time:
                time.sleep(0.01)
        self.http_client.close()
        sys.exit(0)

    def get_number_by_ip(self):
        numbers = [i.zfill(3) for i in self.ip.split(".")]
        try:
            number = int(''.join(numbers))
        except ValueError:
            number = 0
        return number

    def get_camera_number_for_cloud(self):
        return self.cloud_camera_number

    def get_camera_name(self, _):
        return self.camera_name

    def make_shot(self):
        if self.is_same_image_frame():
            return self.SAME_IMAGE
        try:
            _, width, height, t, image_data = self.makerbot._get_raw_camera_image_data()
        except Exception as e:
            self.logger.error("Error in makerbot 5th gen camera: " + str(e))
        else:
            if not image_data:
                self.logger.warning('Failed to get camera frame: %i, %i' % (width, height))
                return None
            if t == 1:
                bgr_rows = self.makerbot.yuv_to_bgr_rows(BytesIO(image_data), width, height)
                frame = self.np.array(bgr_rows).reshape(height, width, 3)
                return self.get_image_from_cv2_frame(self.resize_cv2_frame(frame))
            elif t == 2:
                return self.resize_image(image_data)
            self.logger.warning('Failed to get camera frame (wrong type): %i, %i, %i' % (width, height, t))
