import logging
import time
from threading import Thread
from unittest import case

import mido
import zmq
from mido import Message
from mido.backends.rtmidi import Input, Output

from flandre.nodes.Node import Node
from flandre.utils.Msg import KillMsg, MidiMsg, Msg, ImageArgMsg, SetSeqMetaMsg, SetSidMsg, MaxMsg
from flandre.utils.RfMeta import RfSequenceMeta

logger = logging.getLogger(__name__)


class Midi(Node):
    topics = [ImageArgMsg, SetSeqMetaMsg, MaxMsg]

    def __init__(self, level=logging.INFO):
        super(Midi, self).__init__(level=level)
        self.m_input: Input = None
        self.m_output: Output = None
        self.do_loop = True
        self.t_midi_event_loop: Thread = None
        self.isa: zmq.Socket = None
        self.arg = ImageArgMsg('midi', 0)

        self.m_t_start = 100
        self.m_t_end = 100
        self.m_dct_center = 100
        self.m_dct_bandwidth = 100
        self.m_f_rows = 100
        self.last_pitch: dict[int, int] = dict()
        self.sid = 0

    def custom_setup(self):
        self.isa = self.c.ctx.socket(zmq.PUSH)
        self.isa.bind("inproc://midi")

        self.m_input: Input = mido.open_input('SMC-Mixer:SMC-Mixer Bluetooth 128:0')
        self.m_output: Output = mido.open_output('SMC-Mixer:SMC-Mixer Bluetooth 128:0')
        self.t_midi_event_loop = Thread(target=self.midi_event_loop)
        self.t_midi_event_loop.start()

        self.pp = self.c.ctx.socket(zmq.PUSH)
        self.pp.connect('tcp://q1hyb.as:23456')

    def midi_event_loop(self):
        while self.do_loop:
            midi_msg: Message = self.m_input.receive()
            d = midi_msg.dict()
            match d['type']:
                case 'pitchwheel':
                    channel = d['channel']
                    # 0-127
                    pitch = int(d['pitch'] / 128) + 64
                    if channel not in self.last_pitch:
                        self.last_pitch[channel] = pitch
                    # elif abs(pitch - self.last_pitch[channel]) > 5:
                    #     pass
                    else:
                        self.last_pitch[channel] = pitch
                        # pitch_p = int(pitch / 127)
                        self.isa.send(MidiMsg(type='pitchwheel', channel=channel, pitch=pitch).encode_msg())
                case 'control_change':
                    for i in range(16, 24):
                        if i in [d['control'], d['value']]:
                            if 1 in [d['control'], d['value']]:
                                self.isa.send(MidiMsg(type='control_change', value=1, control=i - 16).encode_msg())
                            else:
                                self.isa.send(MidiMsg(type='control_change', value=-1, control=i - 16).encode_msg())
                case 'note_on':
                    self.isa.send(MidiMsg(type='note_on', note=d['note'], velocity=d['velocity']).encode_msg())
                case 'note_off':
                    self.isa.send(MidiMsg(type='note_off', note=d['note'], velocity=d['velocity']).encode_msg())

    def loop(self):
        isb = self.c.ctx.socket(zmq.PULL)
        isb.connect("inproc://midi")
        self.c.poller.register(isb, zmq.POLLIN)
        while True:
            p = dict(self.c.poller.poll())
            if isb in p:
                msg: MidiMsg = Msg.decode_msg(isb.recv())
                match msg.type:
                    case 'pitchwheel':
                        match msg.channel:
                            case 0:
                                self.arg.g1 = msg.pitch
                            case 1:
                                self.arg.g2 = msg.pitch
                            case 2:
                                self.arg.g3 = msg.pitch
                            case 3:
                                self.arg.g4 = msg.pitch
                            case 4:
                                self.arg.g5 = msg.pitch
                            case 5:
                                self.arg.g6 = msg.pitch
                            case 6:
                                self.arg.g7 = msg.pitch
                            case 7:
                                self.arg.g8 = msg.pitch
                        self.send(self.arg)
                    case 'control_change':
                        match msg.control:
                            case 0:
                                self.arg.t_start = sorted((1, self.arg.t_start + msg.value * 10, self.m_t_start))[1]
                            case 1:
                                self.arg.t_end = sorted((1, self.arg.t_end + msg.value * 10, self.m_t_end))[1]
                            case 2:
                                self.arg.v2 = sorted((500, self.arg.v2 + msg.value * 10, 7000))[1]
                            case 3:
                                self.arg.dct_center = \
                                    sorted((500, self.arg.dct_center + msg.value * 10, self.m_dct_center))[1]
                            case 4:
                                self.arg.dct_bandwidth = \
                                    sorted((500, self.arg.dct_bandwidth + msg.value * 10, self.m_dct_bandwidth))[1]
                            case 5:
                                self.arg.f_rows = sorted((500, self.arg.f_rows + msg.value * 10, self.m_f_rows))[1]
                            case 6:
                                self.arg.beta = sorted((1, self.arg.beta + msg.value, 60))[1]
                            case 7:
                                self.arg.tgc = sorted((1, self.arg.tgc + msg.value, 127))[1]
                    case 'note_on':
                        match msg.note:
                            case 23:
                                self.pp.send_string('start')
                            case 15:
                                self.pp.send_string('stop')
                            case 7:
                                self.pp.send_string('single')
                            case 31:
                                self.pp.send_string('orig')
                        # self.sid += 1
                        # self.send(SetSidMsg(self.sid))

                self.send(self.arg)
            if self.c.sub in p:
                msg = self.recv()
                if isinstance(msg, KillMsg):
                    if msg.name == '':
                        self.do_loop = False
                        return
                elif isinstance(msg, ImageArgMsg):
                    if msg.sender != 'midi':
                        self.arg = msg
                        self.arg.sender = 'midi'
                elif isinstance(msg, SetSeqMetaMsg):
                    seq_meta = RfSequenceMeta.from_name(msg.name)
                    mmax_shape0 = max(seq_meta.shape)
                    self.m_dct_center = mmax_shape0
                    self.m_dct_bandwidth = mmax_shape0
                    self.m_f_rows = mmax_shape0
                elif isinstance(msg, MaxMsg):
                    mmax_shape0 = msg.value
                    self.m_t_start = mmax_shape0
                    self.m_t_end = mmax_shape0
