import socket
import sys
import traceback

BUFSIZE = 4096

SUCCESS_CODES = {
	"0000" : "OK",
	"0001" : "Welcome",
	"0002" : "Reading configuration",
	"0003" : "Reconfigured",
	"0004" : "Reconfiguration in progress",
	"0005" : "Reconfiguration already in progress, queueing",
	"0006" : "Reconfiguration ignored, shutting down",
	"0007" : "Shutdown ordered",
	"0008" : "Already disabled",
	"0009" : "Disabled",
	"0010" : "Already enabled",
	"0011" : "Enabled",
	"0012" : "Restarted",
	"0013" : "Status report",
	"0014" : "Route count",
	"0015" : "Reloading",
	"0016" : "Access restricted",
}

TABLES_ENTRY_CODES = {
	"1000" : "BIRD version",
	"1001" : "Interface list",
	"1002" : "Protocol list",
	"1003" : "Interface address",
	"1004" : "Interface flags",
	"1005" : "Interface summary",
	"1006" : "Protocol details",
	"1007" : "Route list",
	"1008" : "Route details",
	"1009" : "Static route list",
	"1010" : "Symbol list",
	"1011" : "Uptime",
	"1012" : "Route extended attribute list",
	"1013" : "Show ospf neighbors",
	"1014" : "Show ospf",
	"1015" : "Show ospf interface",
	"1016" : "Show ospf state/topology",
	"1017" : "Show ospf lsadb",
	"1018" : "Show memory",
}

ERROR_CODES = {
	"8000" : "Reply too long",
	"8001" : "Route not found",
	"8002" : "Configuration file error",
	"8003" : "No protocols match",
	"8004" : "Stopped due to reconfiguration",
	"8005" : "Protocol is down => cannot dump",
	"8006" : "Reload failed",
	"8007" : "Access denied",

	"9000" : "Command too long",
	"9001" : "Parse error",
	"9002" : "Invalid symbol type",
}

END_CODES = ERROR_CODES.keys() + SUCCESS_CODES.keys()

global bird_sockets 
bird_sockets = {}

def BirdSocketSingleton(host, port):
	global bird_sockets 
	s = bird_sockets.get((host,port), None)
	if not s:
		s = BirdSocket(host,port)
		bird_sockets[(host,port)] = s
	return s

class BirdSocket:

	def __init__(self, host, port):
		self.__host = host
		self.__port = port
		self.__sock = None

	def __connect(self):
		if self.__sock:  return 

		self.__sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
		self.__sock.settimeout(3.0)
		self.__sock.connect((self.__host, self.__port))

	def close(self):
		if self.__sock:
			try: self.__sock.close()
			except: pass
			self.__sock = None
		
	def cmd(self, cmd):
		try:
			self.__connect()
			self.__sock.send(cmd + "\n")
			return self.__read()
		except socket.error:
			why = sys.exc_info()[1]
			self.close()
			return False, "Bird connection problem: %s" % why

	def __read(self):

		code = "7000" # Not used  in bird
		parsed_string = ""
		lastline = ""

		while code not in END_CODES:
			data = self.__sock.recv(BUFSIZE)
			
			lines = (lastline + data).split("\n")
			if len(data) == BUFSIZE:
				lastline = lines[-1]
				lines = lines[:-1]

			for line in lines:
				code = line[0:4]

				if not line.strip():
					continue
				elif code == "0000":
					return True, parsed_string
				elif code in SUCCESS_CODES.keys():
					return True, SUCCESS_CODES.get(code)
				elif code in ERROR_CODES.keys():
					return False, ERROR_CODES.get(code)
				elif code[0] in [ "1", "2"] :
					parsed_string += line[5:] + "\n"
				elif code[0] == " ":
					parsed_string += line[1:] + "\n"
				elif code[0] == "+": 
					parsed_string += line[1:]
				else:
					parsed_string += "<<<unparsable_string(%s)>>>\n"%line

		return True, parsed_string


__all__ = ['BirdSocketSingleton' , 'BirdSocket' ]