#!/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
#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

global TOKEN 
global StartTime
StartTime = 0
TOKEN = 0
MACHINE_ID = 1
SEND_PORT = 5000        # Porta que o Servidor envia
RECV_PORT = 5000       # Porta que o Servidor recebe

class Protocol():
    def __init__ (self):
        self.marca = "~" #Marca
        self.type = str(0) #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 = str(self.priority)

    def setType (self):
        if (self.msg == 'token'):
            self.type = str(0)
        else:
            self.type = str(1)

    #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 + 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 = int(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 StartTime
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    tokenMessage = Protocol()
    dest = (HOST, SEND_PORT)
    while True:
	EndTime = time.time()
        if (EndTime - StartTime >= 10  and TOKEN == 1):
            print 'Vou mandar o token'
            TOKEN = 0
            tokenMessage.Type = str(0)
            msg = tokenMessage.getEmpacotar()
            udp.sendto (msg,dest)
    udp.close()

def client ():
    global TOKEN
    try:
        udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        dest = (HOST, SEND_PORT)
        print 'Para sair digite exit\n'
        protocolMessage = Protocol()
        protocolMessage.setDestiny()
        protocolMessage.setPriority()
        protocolMessage.setMessage()
        protocolMessage.setType()
        protocolMessage.setSizeof()
        protocolMessage.setCRC32()
        msg = protocolMessage.getEmpacotar()
        while protocolMessage.msg <> 'exit':
            udp.sendto (msg, dest)
            protocolMessage = Protocol()
            protocolMessage.setDestiny()
            protocolMessage.setPriority()
            protocolMessage.setMessage()
            protocolMessage.setType()
            protocolMessage.setSizeof()
            protocolMessage.setCRC32()
            msg = protocolMessage.getEmpacotar()
        threadServer._Thread__stop()
        threadToken._Thread__stop()
        udp.close()
    except:
        print 'OPS, ALGO OCORREU ERRADO'
        threadServer._Thread__stop()
        threadToken._Thread__stop()

def server ():
    global TOKEN
    global StartTime
    udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    orig = ('', RECV_PORT)
    udp.bind(orig)
    protocolDescompactMessage = Protocol()
    while True:
        msg, cliente = udp.recvfrom(1024)
        print msg
        protocolDescompactMessage.setDesempacota(msg)
        print protocolDescompactMessage.type 
       	if (protocolDescompactMessage.type == '0' and TOKEN == 0):
            print 'RECEBI O TOKEN'
            TOKEN = 1
            StartTime = time.time()
        else:
            if protocolDescompactMessage.crc == protocolDescompactMessage.getCRC32():
       	        print msg
       	        print '\n' + protocolDescompactMessage.origin + ' Escreveu: ' + protocolDescompactMessage.msg
       	    else:
       	        print 'ERRO, A MENSAGEM FOI RECIBIDA ERRADA'
    udp.close()
    
#------------------MAIN-----------------#

if len(sys.argv) > 1:
    TOKEN = 1
    print 'COMECEI COM O TOKEN'
    StartTime = time.time()

print "Ip da proxima maquina"
HOST = raw_input()
threadServer = threading.Thread(target=server)
threadClient = threading.Thread(target=client)
threadToken = threading.Thread(target=token)
threadServer.start()
threadClient.start()
threadToken.start()