class AsynchatExtended(asynchat.async_chat):

	def __init__(self, event_loop):
		asynchat.async_chat.__init__(self, map=event_loop.socket_map)

	def send(self, data):
		try:
			return asynchat.async_chat.send(self, data)
		except socket.error as err:
			if err.args[0] == errno.WSAEWOULDBLOCK:
				return 0
			raise

	def connect(self, address):
		try:
			self._opened = True
			return asynchat.async_chat.connect(self, address)
		except socket.error as err:
			if err.args[0] == errno.WSAEWOULDBLOCK:
				self.addr = address
				return
			raise

	def handle_close(self):
		self.close()

	def log_info(self, message, type="info"):
		'''Undocumented feature of asyncore. Called by asyncore to print log
		messages. Converts the log message to WOT logging.
		'''
		if type == "info":
			LOG_NOTE(message)
		elif type == "warning":
			LOG_WARNING(message)
		elif type == "error":
			LOG_ERROR(message) class StateMachine(object):
	
	def __init__(self):
		self._state_id = None
		self._states = []
		self._transitions = {}
		self._events = []
		self._is_state_done = True

	def add_state(self, name, on_enter=noop):
		self._states.append(State(name, on_enter=on_enter))
		return len(self._states) - 1

	def add_transition(self, start_state, end_state, event_name, on_transit=noop):
		if start_state not in self._transitions:
			self._transitions[start_state] = []
		self._transitions[start_state].append((Transition(event_name, on_transit), end_state))

	def send_event(self, event_name):
		self._events.append(event_name)

	def tick(self):
		new_state_id = None

		if self._events and self._is_state_done:
			event_name = self._events.pop(0)
			LOG_DEBUG("Handling event '{0}'".format(event_name))
			try:
				for transition, next_state_id in self._transitions[self._state_id]:
					if transition.check_event(event_name):
						transition.transit()
						new_state_id = next_state_id
						break
			except IndexError:
				LOG_DEBUG("No transitions for event '{0}'".format(event_name))

		if self._state_id is None:
			new_state_id = 0
		if new_state_id is not None:
			self._state_id = new_state_id
			self._is_state_done = self._states[self._state_id].enter() is not False

	def current_state(self):
		return self._state_id

	def set_state_done(self):
		self._is_state_done = True
 def noop(*args, **kwargs):
	'''Function that does nothing. A safe default value for callback
	parameters.
	'''
	pass
 def with_args(func, *args, **kwargs):
	def wrapper():
		return func(*args, **kwargs)
	return wrapper
 def LOG_CALL(msg=""):
	def wrap(func):
		def wrapper(*args, **kwargs):
			try:
				if CURRENT_LOG_LEVEL <= LOG_LEVEL.DEBUG:
					callargs = inspect.getcallargs(func, *args, **kwargs)
					callargs = { key: repr(callargs[key]) for key in callargs }
					debug_utils._doLog('DEBUG', _prefix_with_timestamp(func.__name__ + "():"), msg.format(**callargs))
			except:
				pass
			return func(*args, **kwargs)
		functools.update_wrapper(wrapper, func)
		return wrapper
	return wrap
 def LOG_DEBUG(msg, *args):
	if CURRENT_LOG_LEVEL <= LOG_LEVEL.DEBUG:
		debug_utils._doLog('DEBUG', _prefix_with_timestamp(msg), args)
 def LOG_NOTE(msg, *args):
	if CURRENT_LOG_LEVEL <= LOG_LEVEL.NOTE:
		debug_utils._doLog('NOTE', _prefix_with_timestamp(msg), args)
 def LOG_WARNING(msg, *args):
	if CURRENT_LOG_LEVEL <= LOG_LEVEL.WARNING:
		debug_utils._doLog('WARNING', _prefix_with_timestamp(msg), args)
 def LOG_ERROR(msg, *args):
	if CURRENT_LOG_LEVEL <= LOG_LEVEL.ERROR:
		debug_utils._doLog('ERROR', _prefix_with_timestamp(msg), args)
 LOG_CURRENT_EXCEPTION = debug_utils.LOG_CURRENT_EXCEPTION
 class RepeatTimer(object):

	def __init__(self, timeout):
		self._timeout   = timeout
		self._stopped   = True
		self._timer_id = None
		self.on_timeout = Event.Event()

	def fini(self):
		self.stop()
		self.on_timeout.clear()
		self.on_timeout = None

	def start(self):
		self._stopped = False
		self._do_call()

	def stop(self):
		self._stopped = True
		if self._timer_id is not None:
			try:
				BigWorld.cancelCallback(self._timer_id)
			except ValueError:
				# Ignore error:
				#     ValueError: py_cancelCallback: Incorrect callback ID.
				pass
			self._timer_id = None

	def _do_call(self):
		if not self._stopped:
			self._timer_id = BigWorld.callback(self._timeout, self._on_timeout)

	def _on_timeout(self):
		if not self._stopped:
			self.on_timeout()
			self._do_call()

import errno
import functools
import re
import socket
import sys
import time
import weakref
import BigWorld
import Event
from . import async_tools
from . import clientquery
from .asyncore_utils import AsynchatExtended
from .statemachine import StateMachine
from .utils import (
	noop,
	with_args,
	LOG_CALL,
	LOG_DEBUG,
	LOG_NOTE,
	LOG_WARNING,
	LOG_ERROR,
	LOG_CURRENT_EXCEPTION,
	RepeatTimer
)

# TessuMod: Mod for integrating TeamSpeak into World of Tanks
# Copyright (C) 2014  Janne Hakonen
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

'''This module contains implementation for communicating with TeamSpeak's
client query interface.

For more information of client query, see:
   http://forum.teamspeak.com/showthread.php/66509-Official-ClientQuery-Plugin

The main entry point is TS3Client class. After instancing call connect() to
connect to the TeamSpeak client. The TS3Client will continue to connect until
connection to the client is succesfully made.
The TS3Client has check_events() method which implements event handling
mechanism and it needs to be called periodically, e.g. with
BigWorld.callback().

The class provides several functions for querying information from TS client in
a non-blocking asyncronous manner.
'''




_RETRY_TIMEOUT = 10
_COMMAND_WAIT_TIMEOUT = 30
_UNREGISTER_WAIT_TIMEOUT = 5
_API_NOT_CONNECTED_TO_SERVER = 1794
_API_INVALID_SCHANDLER_ID = 1799

class TS3Client(object):
	'''Main entry point for access to TeamSpeak's client query interface.'''

	HOST = "localhost"
	PORT = 25639
	NICK_META_PATTERN = "<wot_nickname_start>(.+)<wot_nickname_end>"

	def __init__(self, event_loop):
		# public events
		self.on_connected = Event.Event()
		self.on_disconnected = Event.Event()
		self.on_connected_to_server = Event.Event()
		self.on_disconnected_from_server = Event.Event()
		self.on_authenticate_error = Event.Event()

		# public models
		self.users = UserModel()
		self.users_in_my_channel = UserFilterProxy(
			source      = self.users,
			filter_func = lambda user: user.channel_id == self._my_channel_id
		)
		self.users_in_my_channel.on_added += self.on_user_entered_my_channel

		self._ping_timer = RepeatTimer(_RETRY_TIMEOUT)
		self._ping_timer.on_timeout += self._ping

		self._wot_nickname = None
		self._my_client_id = None
		self._my_channel_id = None
		self._schandler_id = None
		self.__apikey = None

		self._protocol = _ClientQueryProtocol(self, event_loop)
		self._protocol.on_ready += functools.partial(self._send_sm_event, "protocol_ready")
		self._protocol.on_closed += functools.partial(self._send_sm_event, "protocol_closed")


		self._sm = StateMachine()
