import threading
import time
import string
from threading import Thread
import menu_utils as utils
import paho.mqtt.client as mqtt
from print_color import *


#mqtt 消息处理基类, mqtt消息处理
class class_comm_mqtt_interface :
    def __init__(self, name = "mqtt handler"):
        self.name = name

    def on_message(self, mqtt_thread, topic, message) :
        return
    
    def on_connect(self, mqtt_thread, userdata, flags, rc) :
        return

#mqtt服务器连接处理
def mqtt_on_connect(client, userdata, flags, rc):
    if rc == 0:
        # 连接成功
        print_normal_msg("Mqtt 服务器连接成功")
    elif rc == 1:
        # 协议版本错误
        print_error_msg("协议版本错误")
    elif rc == 2:
        # 无效的客户端标识
        print_error_msg("无效的客户端标识")
    elif rc == 3:
        # 服务器无法使用
        print_error_msg("Mqtt 服务器无法使用")
    elif rc == 4:
        # 错误的用户名或密码
        print_error_msg("错误的用户名或密码")
    elif rc == 5:
        # 未经授权
        print_error_msg("未经授权")

    #获取线程类 （class_comm_mqtt_thread)
    mqtt_thread : class_comm_mqtt_thread= userdata
    unique_object : class_comm_mqtt_interface = None

    #遍历所有unique_object, 通知其 on_connect  事件
    for unique_object in mqtt_thread.unique_object_dict.values() :
        if isinstance(unique_object, list) :  #unique_object 可能是列表, 所以需要遍历
            for each_object in unique_object:
                if hasattr(each_object, "on_connect") :
                    each_object.on_connect(mqtt_thread, userdata, flags, rc)     
        elif hasattr(unique_object, "on_connect") :
            unique_object.on_connect(mqtt_thread, userdata, flags, rc)

#mqtt接收到主题, 通过主题中包含的 unique_name, 定位到 uniuqe_object, 并调用 unique_object.on_message 函数
def mqtt_on_message(client, userdata, message):
    #print("mqtt message:", message.topic, message.payload.decode("utf-8"))

    #获取线程类 （class_comm_mqtt_thread)
    mqtt_thread : class_comm_mqtt_thread= userdata

    unique_object : class_comm_mqtt_interface = None
    for unique_name, search_unique_object in mqtt_thread.unique_object_dict.items() :
        if unique_name in message.topic :
            unique_object = search_unique_object
            break

    #mqtt线程本身无法处理对应的主题消息, 由mqtt  所对应的unique_object处理
    if unique_object != None :
        if isinstance(unique_object, list) :  #unique_object 可能是列表, 所以需要遍历
            for each_object in unique_object:
                if hasattr(each_object, "on_message") :
                    each_object.on_message(mqtt_thread, message.topic, message.payload)
        elif hasattr(unique_object, "on_message") :
            unique_object.on_message(mqtt_thread, message.topic, message.payload)

class class_comm_mqtt_thread(Thread):
    def __init__(self, user_name = "admin", password = "admin") : 
        Thread.__init__(self)
        self.client = None
        
        self.subscribe_list = []
        self.publish_list = []
        self.publish_list_lock = threading.Lock()
        self.condition = threading.Condition()
        self.stop_request = False

        self.server = "127.0.0.1"
        self.port = 1883
        self.user_name = user_name
        self.password = password
        self.keepalive = 60
        
        self.unique_object_dict = {}  #key = unique_name,  value = device_object
        return

    #unique_object: 为unique_object_subscribe_name对应的object,  在回调
    def add_unique_object(self, unique_name : string, unique_object : class_comm_mqtt_interface) :
        self.unique_object_dict[unique_name] = unique_object

    #topic 中 需要包含
    def subscribe(self, topic : string) :
        if self.is_connect() and self.client:
            if topic not in self.subscribe_list :
                self.subscribe_list.append(topic)
                self.client.subscribe(topic)
    
    def set_mqtt_server(self, server = "127.0.0.1", port = 1883, keep_alive = 60.0, user_name = "admin", password = "admin") :
        self.server = server
        self.port = port
        self.user_name = user_name
        self.password = password
        self.keepalive = keep_alive
    
    #用于判断 mqtt_thread是否已经连接
    def is_connect(self) :
        if self.client == None :
            return False
        return self.client.is_connected()

    def open(self):
        try :    
            self.client = mqtt.Client()
            self.client.user_data_set(self)
            self.client.on_connect = mqtt_on_connect
            self.client.on_message = mqtt_on_message
            self.client.username_pw_set(self.user_name, self.password)
            self.client.connect(self.server, self.port, self.keepalive)
        except Exception as e :
            print_error_msg(str(e) + "mqtt_server: ip=%s, port = %d"%(self.server, self.port))
            if self.client:  # 如果self.client已经被初始化了，则断开连接
                self.client.disconnect()
            self.client = None
            return None  # 添加返回语句，告知调用者连接失败
        return self.client

    def close(self):
        self.stop_request = True
        
    def publish(self, topic, message) :
        if self.is_connect :
            self.publish_list_lock.acquire()
            self.publish_list.append([topic, message])
            self.publish_list_lock.release()
            
            self.condition.acquire()
            self.condition.notify()
            self.condition.release()
        return

    def publish_wait(self, topic, message, timeout) :
        if self.is_connect :
            self.publish_list_lock.acquire()
            self.publish_list.insert(0, [topic, message])
            self.publish_list_lock.release()
            self.condition.acquire()
            self.condition.notify()
            self.condition.release()
        return
    
    def run(self): 
        while self.stop_request == False :
            print_warning_msg("mqtt 线程启动, 服务器端口:server = %s:%d"%(self.server, self.port))

            wait_open_timeout = 0
            self.subscribe_list = []   #取消订阅列表, 连接后重新订阅

            while self.client == None and self.stop_request == False:
                if wait_open_timeout == 0 :
                    print_warning_msg("mqtt连接中, 服务器端口:server = %s:%d"%(self.server, self.port))
                    self.open()

                if self.client == None :
                    time.sleep(0.1)
                    wait_open_timeout += 0.1
                    if wait_open_timeout >= 5.0 :
                        wait_open_timeout = 0
            
            time.sleep(0.2)
            
            if self.client :
                self.client.loop_start()
                time.sleep(0.2)

                while self.stop_request == False and self.client.is_connected() :
                    topic = None
                    topic_message = None
                    
                    self.publish_list_lock.acquire()
                    if len(self.publish_list) > 0 :
                        topic_message = self.publish_list.pop(0)
                        topic = topic_message[0]
                        message = topic_message[1]
                    self.publish_list_lock.release()
                    
                    if topic != None and message != None :
                        self.client.publish(topic, message)

                    self.condition.acquire()
                    if len(self.publish_list) == 0:
                        self.condition.wait(timeout = 0.1)
                    self.condition.release()

                self.client.loop_stop()
                self.client.disconnect()
                self.client = None

            print_warning_msg("mqtt连接断开, 准备重启mqtt连接")
        print_error_msg("mqtt 线程停止")

if __name__ == "__main__":
    object_mqtt1 = class_comm_mqtt_interface("object1 handler")
    object_mqtt2 = class_comm_mqtt_interface("object2 handler")
    global_mqtt_thread = class_comm_mqtt_thread()
    global_mqtt_thread.set_mqtt_server(server = "127.0.0.1", port = 1883, keep_alive = 60, user_name="admin", password="admin")
    global_mqtt_thread.add_unique_object("object1", object_mqtt1)
    global_mqtt_thread.add_unique_object("object2", object_mqtt2)
    global_mqtt_thread.start()
