import logging
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
from flandre.utils.RfMeta import RfSequenceMeta

logger = logging.getLogger(__name__)


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

    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()

    def midi_event_loop(self):
        while self.do_loop:
            midi_msg: Message = self.m_input.receive()
            print(midi_msg)
            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.v2 = int(100 + 6000 * (msg.pitch / 127))
                                # print(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 'note_on':
                        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_t_start = mmax_shape0
                    self.m_t_end = mmax_shape0
                    self.m_dct_center = mmax_shape0
                    self.m_dct_bandwidth = mmax_shape0
                    self.m_f_rows = mmax_shape0
