#!/usr/bin/env python
#################### Protocol #################################################################
# Marca - Tipo - Destino - Origem - Prioridade - Tamanho - Inic MSG - Mensagem - Fim MSG - CRC#
###############################################################################################

#Marca - 1 Byte Marca inicio de transmissao, inicio de msg fim de msg
#Tipo - 0 Token, 1 - Msg, 2 - ACK, 3 - NACK
#Destino - pode variar de 1 a 4
#Origem - pode variar de 1 a 4
#Prioridade - pode variar de 0 a 7
#Tamanho - tanto faz
#Marca (inicio msg) - 1 Byte
#Mensagem 
#CRC - 8 bytes

#Exemplo msg#
#~11313~oie~352D1036
# ~ - inic transmissao
# 1 - tipo da mensagem
# 1 - destino da mensagem
# 3 - origem da mensagem
# 1 - prioridade da mensagem
# 3 - tamanho da string da mensagem
# ~ - delimitador de inicio da mensagem
# oie - mensagem
# ~ - delimitador de fim da mensagem
# 352D1036 - CRC 

import socket
import threading
import sys
import binascii
import time
import Queue

global TOKEN 
global StartTime
global HighPriority
global RECEBIDA
global PRIORITY_TOKEN
PRIORITY_TOKEN = 8
RECEBIDA = 1
StartTime = 0
HighPriority = 10
TOKEN = 0
SEND_PORT = 3131        # Porta que o Servidor envia
RECV_PORT = 3131       # Porta que o Servidor recebe

class Protocol():

    def __init__ (self):
        self.marca = "~" #Marca
        self.type = str(1) #Alterar Depois
        self.destiny = str(0)
        self.origin = str(MACHINE_ID)
        self.priority = str(0)
        self.msg = str(0)
        self.sizemsg = str(0)
        self.crc = str(0)

    #set destiny message
    def setDestiny(self):
    	sys.stdout.write('Destino: ')
    	self.destiny = raw_input()
    	while (0 >= int(self.destiny)) or (int(self.destiny) >= 5):
    		print 'Entrada Invalida, Por favor escolha entre 1 a 4'
    		sys.stdout.write('Destino: ')
    		self.destiny = raw_input()
    	self.destiny = str(self.destiny)

    #set Priority messsage
    def setPriority(self):
    	sys.stdout.write('Prioridade: ')
    	self.priority = raw_input()
    	while (0 >= int(self.priority)) or (int(self.priority) >= 8):
    		print 'Entrada Invalida, Por favor escolha entre 1 a 7'
        	sys.stdout.write('Prioridade: ')
    		self.priority = raw_input()		
    	self.priority = 7 - int(self.priority)

    #set message 
    def setMessage(self):
    	sys.stdout.write('Mensagem: ')
    	self.msg = raw_input()

    #set sizeof message
    def setSizeof (self):
    	self.sizemsg = len(self.msg)
    	self.sizemsg = str(self.sizemsg) #String convert
    
    #set CRC32
    def setCRC32(self):
    	buff = (binascii.crc32(self.msg) & 0xFFFFFFFF)
    	self.crc = "%08X" % buff

    def getEmpacotar(self):
        return  self.marca + self.type + self.destiny + self.origin + str(self.priority)  + self.sizemsg + self.marca + self.msg + self.marca + self.crc

    def setDesempacota(self, msg):
        self.marca = msg[0]
        self.type = msg[1]
        self.destiny = str(msg[2])
        self.origin = msg[3]
        self.priority = msg[4]
        self.msg = msg.split('~', 3)[2]
        self.crc = msg.split('~', 3)[3]

    def getCRC32(self):
     	buff = (binascii.crc32(self.msg) & 0xFFFFFFFF)
    	return "%08X" % buff


def token ():
    global TOKEN
    global queue
    global PRIORITY_TOKEN
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    tokenMessage = Protocol()
    msgProtocol = Protocol()
    dest = (HOST, SEND_PORT)
    if (TOKEN == 2):
        TOKEN = 0
        tokenMessage.type = str(0)
        tokenMessage.priority = str(PRIORITY_TOKEN)
        msg = tokenMessage.getEmpacotar()
        udp.sendto (msg,dest)
    if (not queue.empty() and TOKEN == 1):
        top = queue.queue[0]
        if (int(PRIORITY_TOKEN) >= int(top[0])):
            top = queue.get()
            msgProtocol.setDesempacota(top[1])
            msgProtocol.priority = str(7)
            msg = msgProtocol.getEmpacotar()
            udp.sendto (top[1], dest)
            time.sleep(1)
            return True
        else:
            return False
    udp.close()

def client ():
    try:
        global TOKEN
        protocolMessage = Protocol()
        global queue
        queue = Queue.PriorityQueue()
        while True:
            protocolMessage.setDestiny()
            protocolMessage.setPriority()
            protocolMessage.setMessage()
            protocolMessage.type = str(1)
            protocolMessage.setSizeof()
            protocolMessage.setCRC32()
            msg = protocolMessage.getEmpacotar()
            queue.put ((protocolMessage.priority, msg))
        threadServer._Thread__stop()
    except:
        print 'OPS, ALGO OCORREU ERRADO'
        threadServer._Thread__stop()

def server ():
    global TOKEN
    global StartTime
    global RECEBIDA
    global queue
    global PRIORITY_TOKEN
    global TOKEN_MSG
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    orig = ('', RECV_PORT)
    dest = (HOST, SEND_PORT)
    udp.bind(orig)
    protocolDescompactMessage = Protocol()
    aux1 = Protocol()
    aux2 = Protocol()
    while True:
        msg, cliente = udp.recvfrom(1024)
        protocolDescompactMessage.setDesempacota(msg)
        
        if (protocolDescompactMessage.type == '1' and int(MACHINE_ID) == int(protocolDescompactMessage.origin)):
            #print 'SETANDO A PRIORIDADE do TOKEN'
            #print protocolDescompactMessage.priority
            
            PRIORITY_TOKEN = protocolDescompactMessage.priority
            TOKEN = 2
            token()
            #print 'EU RECEBI A MSG QUE ENVIEI'
        
        elif (protocolDescompactMessage.type == '0' and TOKEN == 0):
            #print 'RECEBI O TOKEN'
            TOKEN = 1
            if (token() == False):
                #print 'Vou enviar o token'
                TOKEN = 0
                udp.sendto(msg,dest)
                
        #A msg e pra mim
        elif (msg[1] == '1' and int(MACHINE_ID) == int(protocolDescompactMessage.destiny)):
            
            udp.sendto(msg,dest)
            if (protocolDescompactMessage.crc == protocolDescompactMessage.getCRC32()):
                print '\n' + protocolDescompactMessage.origin + ' Escreveu: ' + protocolDescompactMessage.msg
            else:
                print 'ERRO DE MENSAGEM'
        
        #A msg n e pra mim
        elif (msg[1] == '1'):
            if (not queue.empty()):
                top = queue.queue[0]
                HighPriorat = int(top[0])
                if (HighPriorat < int(msg[4])):
                    aux1.setDesempacota(msg)
                    aux1.priority = str(HighPriorat)
                    msg = aux1.getEmpacotar()
            print msg
            udp.sendto (msg, dest)
 
    udp.close()
    
#------------------MAIN-----------------#

if len(sys.argv) > 1:
    TOKEN = 2
    print 'COMECEI COM O TOKEN'
    PRIORITY_TOKEN = 7
    StartTime = time.time()
print 'ID Dessa maquina, escolha entre 1 e 4'
MACHINE_ID = raw_input ()
print "Ip da proxima maquina"
HOST = raw_input()
if TOKEN == 1:
    StartTime = time.time()
threadServer = threading.Thread(target=server)
threadClient = threading.Thread(target=client)
threadServer.start()
threadClient.start()
token()