context: #region using
  
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.IO;
  using System.Linq;
  using System.Net;
  using System.Net.NetworkInformation;
  using System.Net.Sockets;
  using System.Text;
  using System.Text.RegularExpressions;
  using System.Threading;
  using System.Threading.Tasks;
  using ZkData;
  using ZkData.UnitSyncLib;
  using Timer = System.Timers.Timer;
  
  #endregion
  
  namespace LobbyClient
  {
      
      public class TasClient:Connection
      {
          public const int MaxAlliances = 16;
          public const int MaxTeams = 16;
  
          public ProtocolExtension Extensions { get; private set; }
  
          public delegate void Invoker();
  
          public delegate void Invoker<TArg>(TArg arg);
  
  
          readonly string appName = "UnknownClient";
          Dictionary<int, Battle> existingBattles = new Dictionary<int, Battle>();
          Dictionary<string, User> existingUsers = new Dictionary<string, User>();
          readonly bool forcedLocalIP = false;
          bool isLoggedIn;
          Dictionary<string, Channel> joinedChannels = new Dictionary<string, Channel>();
  
          int lastUdpSourcePort;
          int lastUserStatus;
          readonly string localIp;
  
          int pingInterval = 30; // how often to ping server (in seconds)
          readonly Timer pingTimer;
          public string serverHost { get; private set; }
  
          int serverPort;
          public Dictionary<int, Battle> ExistingBattles { get { return existingBattles; } set { existingBattles = value; } }
  
          public Dictionary<string, User> ExistingUsers { get { return existingUsers; } set { existingUsers = value; } }
  
          
          public override async Task OnConnected()
          {
              MyBattle = null;
              ExistingUsers = new Dictionary<string, User>();
              joinedChannels = new Dictionary<string, Channel>();
              existingBattles = new Dictionary<int, Battle>();
              isLoggedIn = false;
          }
  
          public override async Task OnConnectionClosed(bool wasRequested)
          {
              ExistingUsers = new Dictionary<string, User>();
              joinedChannels = new Dictionary<string, Channel>();
              existingBattles = new Dictionary<int, Battle>();
  
              MyBattle = null;
              isLoggedIn = false;
              ConnectionLost(this, new TasEventArgs(string.Format("Connection {0}", wasRequested ? "closed on user request" : "disconnected")));
          }
  
          public override async Task OnLineReceived(string line)
          {
              try {
                  dynamic obj = CommandJsonSerializer.DeserializeLine(line);
                  await (Task)Process(obj); //explicit specify as Task<> to fix "GetAwaiter() not defined". Reference: http://stackoverflow.com/questions/11853812/task-does-not-contain-a-definition-for-getawaiter
              } catch (Exception ex) {
                  Trace.TraceError("Error processing line {0} : {1}", line, ex);
              }
          }
  
          public async Task SendCommand<T>(T data)
          {
              try {
                  var line = CommandJsonSerializer.SerializeToLine(data);
                  await SendString(line);
              } catch (Exception ex) {
                  Trace.TraceError("Error sending {0} : {1}", data,ex);
              }
          }
  
          static CommandJsonSerializer CommandJsonSerializer = new CommandJsonSerializer();
  
          public bool IsLoggedIn { get { return isLoggedIn; } }
  
          public Dictionary<string, Channel> JoinedChannels { get { return joinedChannels; } }
  
          public Battle MyBattle { get; protected set; }
          //public int MyBattleID { get { return MyBattle != null ? MyBattle.BattleID : 0; } }
  
  
          public UserBattleStatus MyBattleStatus
          {
              get
              {
                  if (MyBattle != null) {
                      UserBattleStatus ubs;
                      MyBattle.Users.TryGetValue(UserName, out ubs);
                      return ubs;
                  }
                  return null;
              }
          }
  
          public int MyBattleID
          {
              get
              {
                  var bat = MyBattle;
                  if (bat != null) return bat.BattleID;
                  else return 0;
              }
          }
  
          public User MyUser
          {
              get
              {
                  User us;
                  ExistingUsers.TryGetValue(UserName, out us);
                  return us;
              }
          }
  
  
          public int PingInterval
          {
              get { return pingInterval; }
              set
              {
                  pingInterval = value;
                  pingTimer.Interval = pingInterval*1000;
              }
          }
          public string ServerSpringVersion {get { return ServerWelcome != null ? ServerWelcome.Engine : null; }  }
  
          public string UserName { get; private set; }
          public string UserPassword { get; private set; }
  
          public event EventHandler<EventArgs<User>> UserAdded = delegate { };
          public event EventHandler<EventArgs<UserDisconnected>> UserRemoved = delegate { };
          public event EventHandler<EventArgs<OldNewPair<User>>> UserStatusChanged = delegate { };
          public event EventHandler<EventArgs<Battle>> BattleFound = delegate { };
          public event EventHandler<EventArgs<ChannelUserInfo>> ChannelUserAdded = delegate { };
          public event EventHandler<EventArgs<ChannelUserRemovedInfo>> ChannelUserRemoved = delegate { };
          public event EventHandler<EventArgs<Welcome>> Connected = delegate { };
          public event EventHandler<EventArgs<JoinChannelResponse>> ChannelJoinFailed = delegate { };
          public event EventHandler<TasEventArgs> LoginAccepted = delegate { };
          public event EventHandler<EventArgs<LoginResponse>> LoginDenied = delegate { };
          public event EventHandler<TasSayEventArgs> Said = delegate { }; // this is fired when any kind of say message is recieved
          public event EventHandler<SayingEventArgs> Saying = delegate { }; // this client is trying to say somethign
          public event EventHandler<BattleUserEventArgs> BattleUserJoined = delegate { };
          public event EventHandler<BattleUserEventArgs> BattleUserLeft = delegate { };
          public event EventHandler<CancelEventArgs<Channel>> PreviewChannelJoined = delegate { };
          public event EventHandler<TasEventArgs> RegistrationAccepted = delegate { };
          public event EventHandler<EventArgs<RegisterResponse>> RegistrationDenied = delegate { };
          public event EventHandler<EventArgs<Battle>> BattleRemoved = delegate { }; // raised just after the battle is removed from the battle list
          public event EventHandler<EventArgs<Battle>> MyBattleRemoved = delegate { }; // raised just after the battle is removed from the battle list
          public event EventHandler<EventArgs<Battle>> BattleClosed = delegate { };
          public event EventHandler<EventArgs<Battle>> BattleOpened = delegate { };
          public event EventHandler<EventArgs<Battle>> BattleJoined = delegate { };
          public event EventHandler<EventArgs<UserBattleStatus>> BattleUserStatusChanged = delegate { };
          public event EventHandler<EventArgs<UserBattleStatus>> BattleMyUserStatusChanged = delegate { };
          public event EventHandler<EventArgs<Channel>> ChannelJoined = delegate { };
          public event EventHandler<EventArgs<Channel>> ChannelLeft = delegate { };
          public event EventHandler<EventArgs<BotBattleStatus>> BattleBotAdded = delegate { };
          public event EventHandler<EventArgs<BotBattleStatus>> BattleBotUpdated = delegate { };
          public event EventHandler<EventArgs<BotBattleStatus>> BattleBotRemoved = delegate { };
          public event EventHandler<TasEventArgs> ConnectionLost = delegate { };
          public event EventHandler<EventArgs<Say>> Rang = delegate { };
          public event EventHandler<CancelEventArgs<TasSayEventArgs>> PreviewSaid = delegate { };
          public event EventHandler<EventArgs<Battle>> MyBattleHostExited = delegate { };
          public event EventHandler<EventArgs<Battle>> MyBattleStarted = delegate { };
          public event EventHandler<EventArgs<SetRectangle>> StartRectAdded = delegate { };
          public event EventHandler<EventArgs<SetRectangle>> StartRectRemoved = delegate { };
          public event EventHandler<EventArgs<OldNewPair<Battle>>> BattleInfoChanged = delegate { };
          public event EventHandler<EventArgs<OldNewPair<Battle>>> BattleMapChanged = delegate { };
          public event EventHandler<EventArgs<OldNewPair<Battle>>> MyBattleMapChanged = delegate { };
          public event EventHandler<EventArgs<Battle>> ModOptionsChanged = delegate { };
  
          
          public event EventHandler<TasEventArgs> ChannelTopicChanged = delegate { };
          public event EventHandler<EventArgs<User>> UserExtensionsChanged = delegate { };
          public event EventHandler<EventArgs<User>> MyExtensionsChanged = delegate { };
          
   
  
          public TasClient(string appName, Login.ClientTypes? clientTypes = null, string ipOverride = null)
          {
              if (clientTypes.HasValue) this.clientType = clientTypes.Value;
              this.appName = appName;
              
  
              if (!string.IsNullOrEmpty(ipOverride))
              {
                  localIp = ipOverride;
                  forcedLocalIP = true;
              }
              else
              {
                  var addresses = Dns.GetHostAddresses(Dns.GetHostName());
  
                  localIp = addresses[0].ToString();
                  foreach (var adr in addresses)
                  {
                      if (adr.AddressFamily == AddressFamily.InterNetwork)
                      {
                          localIp = adr.ToString();
                          break;
                      }
                  }
              }
  
              Extensions = new ProtocolExtension(this, (user, data) => {
                                                                           User u;
                                                                           if (ExistingUsers.TryGetValue(user, out u))
                                                                           {
                                                                               UserExtensionsChanged(this, new EventArgs<User>(u));
                                                                           }
  
  
              });
  
              pingTimer = new Timer(pingInterval*1000) { AutoReset = true };
              pingTimer.Elapsed += OnPingTimer;
          }
  
  
          public async Task AddBattleRectangle(int allyno, BattleRect rect)
          {
              {
                  if (allyno < Spring.MaxAllies && allyno >= 0) {
                      await SendCommand(new SetRectangle() { Number = allyno, Rectangle = rect });
                  }
              }
          }
  
          private async Task Process(SetRectangle rect)
          {
              var bat = MyBattle;
              if (bat != null) {
                  if (rect.Rectangle == null) {
                      BattleRect org;
                      bat.Rectangles.TryRemove(rect.Number, out org);
                      StartRectAdded(this, new EventArgs<SetRectangle>(rect));
                  } else {
                      bat.Rectangles[rect.Number] = rect.Rectangle;
                      StartRectRemoved(this, new EventArgs<SetRectangle>(rect));
                  }
              }
          }
  
          private async Task Process(SetModOptions options)
          {
              var bat = MyBattle;
              if (bat != null) {
                  bat.ModOptions = options.Options;
                  ModOptionsChanged(this, new EventArgs<Battle>(bat));
              }
          }
  
  
          public Task AddBot(string name, string aiDll, int? allyNumber= null, int? teamNumber= null)
          {
  
gt:             var u = new UpdateBotStatus();