# coding: utf-8
# pylint: skip-file
from socket import socket, AF_INET, SOCK_DGRAM
import sys
import threading
import time



NUM_MASTERSERVERS = 4
MASTERSERVER_PORT = 8300

TIMEOUT = 2

SERVERTYPE_NORMAL = 0
SERVERTYPE_LEGACY = 1

PACKET_GETLIST = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreqt"
PACKET_GETLIST2 = "\x20\x00\x00\x00\x00\x00\xff\xff\xff\xffreq2"
PACKET_GETINFO = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgief"
PACKET_GETINFO2 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie2" + "\x00"
PACKET_GETINFO3 = "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xffgie3" + "\x00"



class Server_Info(threading.Thread):

	def __init__(self, address, typ):
		self.address = address
		self.type = typ
		self.finished = False
		threading.Thread.__init__(self, target = self.run)

	def run(self):
		self.info = None
		if self.type == SERVERTYPE_NORMAL:
			self.info = get_server_info3(self.address)
		elif self.type == SERVERTYPE_LEGACY:
			self.info = get_server_info(self.address)
			if self.info:
				self.info = get_server_info2(self.address)
		self.finished = True


def get_server_info(address):
	try:
		sock = socket(AF_INET, SOCK_DGRAM)
		sock.settimeout(TIMEOUT)
		sock.sendto(PACKET_GETINFO, address)
		data, _addr = sock.recvfrom(1024)
		sock.close()

		data = data[14:] # skip header
		slots = data.split("\x00")

		server_info = {}
		server_info["version"] = slots[0]
		server_info["name"] = slots[1]
		server_info["map"] = slots[2]
		server_info["gametype"] = slots[3]
		server_info["flags"] = int(slots[4])
		server_info["progression"] = int(slots[5])
		server_info["num_players"] = int(slots[6])
		server_info["max_players"] = int(slots[7])
		server_info["players"] = []

		for i in range(0, server_info["num_players"]):
			player = {}
			player["name"] = slots[8+i*2]
			player["score"] = int(slots[8+i*2+1])
			server_info["players"].append(player)

		return server_info

	except:
		sock.close()
		return None


def get_server_info2(address):
	try:
		sock = socket(AF_INET, SOCK_DGRAM)
		sock.settimeout(TIMEOUT)
		sock.sendto(PACKET_GETINFO2, address)
		data, _addr = sock.recvfrom(1024)
		sock.close()

		data = data[14:] # skip header
		slots = data.split("\x00")

		server_info = {}
		server_info["token"] = slots[0]
		server_info["version"] = slots[1]
		server_info["name"] = slots[2]
		server_info["map"] = slots[3]
		server_info["gametype"] = slots[4]
		server_info["flags"] = int(slots[5])
		server_info["progression"] = int(slots[6])
		server_info["num_players"] = int(slots[7])
		server_info["max_players"] = int(slots[8])
		server_info["players"] = []

		for i in range(0, server_info["num_players"]):
			player = {}
			player["name"] = slots[9+i*2]
			player["score"] = int(slots[9+i*2+1])
			server_info["players"].append(player)

		return server_info

	except:
		sock.close()
		return None


def get_server_info3(address):
	try:
		sock = socket(AF_INET, SOCK_DGRAM)
		sock.settimeout(TIMEOUT)
		sock.sendto(PACKET_GETINFO3, address)
		data, addr = sock.recvfrom(1400)
		sock.close()

		data = data[14:] # skip header
		slots = data.split("\x00")

		server_info = {}
		server_info["token"] = slots[0]
		server_info["version"] = slots[1]
		server_info["name"] = slots[2]
		server_info["map"] = slots[3]
		server_info["gametype"] = slots[4]
		server_info["flags"] = int(slots[5])
		server_info["num_players"] = int(slots[6])
		server_info["max_players"] = int(slots[7])
		server_info["num_clients"] = int(slots[8])
		server_info["max_clients"] = int(slots[9])
		server_info["players"] = []

		for i in range(0, server_info["num_clients"]):
			player = {}
			player["name"] = slots[10+i*5]
			player["clan"] = slots[10+i*5+1]
			player["country"] = int(slots[10+i*5+2])
			player["score"] = int(slots[10+i*5+3])
			if int(slots[10+i*5+4]):
				player["player"] = True
			else:
				player["player"] = False
			server_info["players"].append(player)

		return server_info

	except:
		sock.close()
		return None



class Master_Server_Info(threading.Thread):

	def __init__(self, address):
		self.address = address
		self.finished = False
		threading.Thread.__init__(self, target = self.run)

	def run(self):
		self.servers = get_list(self.address) + get_list2(self.address)
		self.finished = True


def get_list(address):
	servers = []

	try:
		sock = socket(AF_INET, SOCK_DGRAM)
		sock.settimeout(TIMEOUT)
		sock.sendto(PACKET_GETLIST, address)

		while 1:
			data, _addr = sock.recvfrom(1024)

			data = data[14:]
			num_servers = len(data) / 6

			for n in range(0, num_servers):
				ip = ".".join(map(str, map(ord, data[n*6:n*6+4])))
				port = ord(data[n*6+5]) * 256 + ord(data[n*6+4])
				servers += [[(ip, port), SERVERTYPE_LEGACY]]

	except:
		sock.close()

	return servers


def get_list2(address):
	servers = []

	try:
		sock = socket(AF_INET, SOCK_DGRAM)
		sock.settimeout(TIMEOUT)
		sock.sendto(PACKET_GETLIST2, address)

		while 1:
			data, _addr = sock.recvfrom(1400)

			data = data[14:]
			num_servers = len(data) / 18

			for n in range(0, num_servers):
				if data[n*18:n*18+12] == "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff":
					ip = ".".join(map(str, map(ord, data[n*18+12:n*18+16])))
				else:
					ip = ":".join(map(str, map(ord, data[n*18:n*18+16])))
				port = (ord(data[n*18+16])<<8) + ord(data[n*18+17])
				servers += [[(ip, port), SERVERTYPE_NORMAL]]

	except:
		sock.close()

	return servers



master_servers = []

for i in range(1, NUM_MASTERSERVERS+1):
	m = Master_Server_Info((f"master{int(i)}.teeworlds.com", MASTERSERVER_PORT))
	master_servers.append(m)
	m.start()
	time.sleep(0.001) # avoid issues

servers = []

while len(master_servers) != 0:
	if master_servers[0].finished:
		if master_servers[0].servers:
			servers += master_servers[0].servers
		del master_servers[0]
	time.sleep(0.001) # be nice

servers_info = []

print(str(len(servers)) + " servers")

for server in servers:
	s = Server_Info(server[0], server[1])
	servers_info.append(s)
	s.start()
	time.sleep(0.001) # avoid issues

num_players = 0
num_clients = 0

while len(servers_info) != 0:
	if servers_info[0].finished:

		if servers_info[0].info:
			num_players += servers_info[0].info["num_players"]
			if servers_info[0].type == SERVERTYPE_NORMAL:
				num_clients += servers_info[0].info["num_clients"]
			else:
				num_clients += servers_info[0].info["num_players"]

		del servers_info[0]

	time.sleep(0.001) # be nice

print(str(num_players) + " players and " + str(num_clients-num_players) + " spectators")
