# !/usr/bin/env python
# -*- coding:utf-8 -*-
import ctypes
from ctypes import *
import time
import configparser
from multiprocessing import Process, Queue, Manager
import queue
import numpy as np
import sys
sys.path.append('..')

import acl
from ..constants import *
from ..lib.atlasutil_so import libatlas

from .presenter_datatype import *
from . import presenter_agent as agent
from . import presenter_message as pm


class DataBufC(Structure):
    _fields_ = [
        ('size',        c_int),
        ('data',        POINTER(c_ubyte))
    ]

class DataBuf():
    def __init__(self, data, data_size):
        self.data = data
        self.size = data_size
        self.nparray = None

    def copy_to_local(self):
        src_data = DataBufC()
        src_data.data = cast(self.data, POINTER(c_ubyte))
        src_data.size = self.size
        dest_data = DataBufC()
        ret = libatlas.CopyDataToLocal(byref(dest_data), byref(src_data))
        if ret:
            print("Copy data to local failed")
            return None

        return DataBuf(dest_data.data, dest_data.size)
    
    def tobytes(self):
        self.nparray = np.frombuffer((ctypes.c_ubyte * self.size).from_address(ctypes.addressof(self.data.contents)), dtype=np.uint8)
        return self.nparray.tobytes()

    def destroy(self):
        data_buf = DataBufC()
        data_buf.data = cast(self.data, POINTER(c_ubyte))
        data_buf.size = self.size
        libatlas.ReleaseDataBuf(byref(data_buf))
        self.data = None
        self.size = 0
            

class PresenterChannel():
    def __init__(self, server_ip, port, name='video', type=CONTENT_TYPE_VIDEO):
        self._server_ip = server_ip
        self._port = port
        self._type = type
        self._name = name
        self.agent_msg_queue = Queue()
        self.open_status = Manager().Value('i', STATUS_DISCONNECT)
        self.data_respone_counter =  Manager().Value('i', 0)
        self._send_counter = 0
        self._send_buffer = queue.Queue(64)
        self.send_cnt = 0
        self.relase_cnt = 0

    def startup(self):
        agent_process = Process(target=agent.StartPresenterAgent,
                                args=(self.agent_msg_queue, self._server_ip,self._port, 
                                      self.open_status, self.data_respone_counter))
        agent_process.start()
        time.sleep(0.5)

        self._send_open_channel_request(self._name, self._type)

        return self._wait_open_status(STATUS_OPENED)

    def _wait_open_status(self, listen_status):
        ret = STATUS_ERROR
        for i in range(0, 100):
            time.sleep(0.1)
            if self.open_status.value == listen_status:
                print("Open status is %d now"%(listen_status))
                ret = STATUS_OK
                break

        return ret

    def send_message(self, data):
        self.agent_msg_queue.put(data)
        self._send_counter += 1

    def _send_open_channel_request(self, channel_name, content_type):
        request_msg = pm.open_channel_request(channel_name, content_type)
        self.send_message(request_msg)

    def _release_send_success_data(self):
        release_num = self._send_buffer.qsize() - \
                      (self._send_counter - self.data_respone_counter.value)
        if release_num > 0:
            for i in range(0, release_num):                
                data = self._send_buffer.get_nowait()
                data.destroy()
                data = None
                self.relase_cnt += 1
        #print("Released send success images ", self.relase_cnt)

    def send_detection_data(self, image_width, image_height,
                            image_data, detection_result):
        if self._send_buffer.full() is True:
            print("ERROR:Send detection data failed for buffer is full")
            return False

        image_buf = DataBuf(image_data.data(), image_data.size).copy_to_local()
        request_msg = pm.image_frame_request(image_width, image_height,
                                             image_buf.tobytes(), detection_result)
        self.send_message(request_msg)
        self._send_buffer.put(image_buf)
        self._release_send_success_data()

        return True

    def _send_heart_beat_message(self):
        msg = pm.heartbeat_message()
        self.send_message(msg)

    def __del__(self):
        self.open_status.value = STATUS_EXITING
        print("Presenter channel close...")
        self._send_heart_beat_message()
        if STATUS_OK == self._wait_open_status(STATUS_EXITTED):
            print("Presenter channel closed")
        else:
            print("Presenter channel close failed for presenter agent no response")


def get_presenter_server_addr(config_file):
    config = configparser.ConfigParser()
    config.read(config_file)
    presenter_server_ip = config['baseconf']['presenter_server_ip']
    port = int(config['baseconf']['presenter_server_port'])
	
    print("presenter server ip %s, port %d"%(presenter_server_ip, port))
    return presenter_server_ip, port

def open_channel(config_file, channel_name='video', channel_type = CONTENT_TYPE_VIDEO):
    server_ip, port = get_presenter_server_addr(config_file)
    channel = PresenterChannel(server_ip, port, channel_name, channel_type)
    ret = channel.startup()
    if ret:
        print("ERROR:Open channel failed")
        return None
    return channel
