#!/usr/bin/env python3
import asyncio
from tcp import Servidor
import re


def validar_nome(nome):
    return re.match(br'^[a-zA-Z][a-zA-Z0-9_-]*$', nome) is not None


def sair(conexao):
    if conexao.nick != b'*':
        print(conexao, conexao.nick, 'conexão fechada')
        del servidor.users[conexao.nick]

        for canal in servidor.canais:
            print(canal, servidor.canais[canal])
            if conexao.nick in servidor.canais[canal]:
                servidor.canais[canal].remove(conexao.nick)
                for nick in servidor.canais[canal]:
                    servidor.users[nick].enviar(b':' + conexao.nick + b' QUIT :Connection closed\r\n')
    else:
        print(conexao, 'conexão fechada')

    conexao.fechar()


def dados_recebidos(conexao, dados):
    if dados == b'':
        return sair(conexao)

    conexao.msg_buffer += dados
    split_pos = conexao.msg_buffer.find(b'\r\n')

    while split_pos != -1:
        msg = conexao.msg_buffer[:split_pos]
        # print("New full msg: ", msg)
        conexao.msg_buffer = conexao.msg_buffer[split_pos + 2:]

        if msg.startswith(b'PING'):
            # print("Recv ping")
            conexao.enviar(b':server PONG server :' + msg.split(b' ', 1)[1] + b'\r\n')
        elif msg.startswith(b'NICK'):
            nick = msg.split(b'\r\n')[0].split(b' ', 1)[1]
            # print("Recv nick", nick)
            if not validar_nome(nick):
                # print("Nick invalido")
                conexao.enviar(b':server 432 ' + conexao.nick + b' ' + nick + b' :Erroneous nickname\r\n')
            elif nick.lower() in servidor.users:
                # print("Nick em uso")
                conexao.enviar(b':server 433 ' + conexao.nick + b' ' + nick + b' :Nickname is already in use\r\n')
            else:
                # print("Nick aceito")
                if conexao.nick == b'*':
                    conexao.enviar(b':server 001 ' + nick + b' :Welcome\r\n')
                    conexao.enviar(b':server 422 ' + nick + b' :MOTD File is missing\r\n')
                else:
                    print(conexao.nick, nick)
                    conexao.enviar(b':' + conexao.nick + b' NICK ' + nick + b'\r\n')
                    del servidor.users[conexao.nick]

                conexao.nick = nick.lower()
                servidor.users[conexao.nick] = conexao
        elif msg.startswith(b'PRIVMSG'):
            dest, priv_msg = msg.split(b'\r\n')[0].split(b' ', 1)[1].split(b' :', 1)

            print(servidor.users.keys(), dest, conexao.nick)

            if dest.startswith(b'#'):
                if dest.lower() in servidor.canais:
                    for user_nick in servidor.canais[dest.lower()]:
                        if conexao.nick == user_nick:
                            continue
                        print("send msg from " + str(conexao.nick) + " to " + str(user_nick) + " in channel " + str(
                            dest.lower()))
                        servidor.users[user_nick].enviar(
                            b':' + conexao.nick + b' PRIVMSG ' + dest + b' :' + priv_msg + b'\r\n')
            elif dest.lower() in servidor.users:
                # print("dest in servidor")
                servidor.users[dest.lower()].enviar(
                    b':' + conexao.nick + b' PRIVMSG ' + dest + b' :' + priv_msg + b'\r\n')
        elif msg.startswith(b'JOIN'):
            canal = msg.split(b'\r\n')[0].split(b' ', 1)[1]

            if not canal.startswith(b'#') or not validar_nome(canal[1:]):
                conexao.enviar(b':server 403 canal :No such channel\r\n')
            else:
                if canal.lower() in servidor.canais:
                    print("add " + str(conexao.nick) + " to channel " + str(canal.lower()))
                    servidor.canais[canal.lower()].append(conexao.nick)
                else:
                    print("create channel " + str(canal.lower()) + " with user " + str(conexao.nick))
                    servidor.canais[canal.lower()] = [conexao.nick]

                for user_nick in servidor.canais[canal.lower()]:
                    print("send " + str(conexao.nick) + " join to " + str(user_nick))
                    servidor.users[user_nick].enviar(b':' + conexao.nick + b' JOIN :' + canal.lower() + b'\r\n')

                membros = b' '.join(sorted(servidor.canais[canal.lower()]))
                conexao.enviar(b':server 353 ' + conexao.nick + b' = ' + canal + b' :' + membros + b'\r\n')
                conexao.enviar(b':server 366 ' + conexao.nick + b' ' + canal + b' :End of /NAMES list.\r\n')

        elif msg.startswith(b'PART'):
            canal = msg.split(b'\r\n')[0].split(b' ')[1]

            if canal.lower() in servidor.canais and conexao.nick in servidor.canais[canal.lower()]:
                for user_nick in servidor.canais[canal.lower()]:
                    print("send " + str(conexao.nick) + " leave " + str(canal.lower()) + " to " + str(user_nick))
                    servidor.users[user_nick].enviar(b':' + conexao.nick + b' PART ' + canal.lower() + b'\r\n')
                servidor.canais[canal.lower()].remove(conexao.nick)

        split_pos = conexao.msg_buffer.find(b'\r\n')


def conexao_aceita(conexao):
    print(conexao, 'nova conexão')
    conexao.msg_buffer = b''
    conexao.nick = b'*'
    conexao.registrar_recebedor(dados_recebidos)


servidor = Servidor(6667)
servidor.users = {}
servidor.canais = {}
servidor.registrar_monitor_de_conexoes_aceitas(conexao_aceita)
asyncio.get_event_loop().run_forever()
