context: using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Threading;
  using System.Net.Sockets;
  using System.Net;
  using MonoTorrent.Client.Messages;
  using MonoTorrent.Client.Messages.UdpTracker;
  using MonoTorrent.Common;
  
  namespace MonoTorrent.Client.Tracker
  {
     public class UdpTracker : Tracker
     {
         private long connectionId;
         private UdpClient tracker;
         private IPEndPoint endpoint;
         bool hasConnected;
         bool amConnecting;
         internal TimeSpan RetryDelay;
         int timeout;
  
         public UdpTracker(Uri announceUrl)
             : base(announceUrl)
         {
             CanScrape = true;
             CanAnnounce = true;
             RetryDelay = TimeSpan.FromSeconds(15);
             tracker = new UdpClient(announceUrl.Host, announceUrl.Port);
             endpoint = (IPEndPoint)tracker.Client.RemoteEndPoint;
         }
  
         #region announce
  
         public override void Announce(AnnounceParameters parameters, object state)
         {
             //LastUpdated = DateTime.Now;
             if (!hasConnected && amConnecting)
                 return;
  
             if (!hasConnected)
             {
                 amConnecting = true;
                 try
                 {
                     Connect(new ConnectAnnounceState(parameters, ConnectAnnounceCallback, state));
                 }
                 catch (SocketException)
                 {
                     DoAnnounceComplete(false, state, new List<Peer>());
                     return;
                 }               
             }
             else
                 DoAnnounce(parameters, state);
         }
  
         private void DoAnnounce(AnnounceParameters parameters, object state)
         {
             ConnectAnnounceState announceState = new ConnectAnnounceState(parameters, AnnounceCallback, state);
             announceState.Message = new AnnounceMessage(DateTime.Now.GetHashCode(), connectionId, parameters);
             try
             {
                 SendAndReceive(announceState);
             }
             catch (SocketException)
             {
                 DoAnnounceComplete(false, state, new List<Peer>());
             }
         }
  
         private void ConnectAnnounceCallback(IAsyncResult ar)
         {
             ConnectAnnounceState announceState = (ConnectAnnounceState)ar;
              try
              {
                  if (announceState.SavedException != null)
                  {
                      FailureMessage = announceState.SavedException.Message;
                      amConnecting = false;
                      DoAnnounceComplete(false, announceState.AsyncState, new List<Peer>());
                      return;
                  }
                  if (!ConnectCallback(ar))//bad transaction id
                  {
                      DoAnnounceComplete(false, announceState.AsyncState, new List<Peer>());
                      return;
                  }
                  DoAnnounce(announceState.Parameters, announceState.AsyncState);
              }
              catch
              {
                  DoAnnounceComplete(false, announceState.AsyncState, null);
              }
         }
  
         private void AnnounceCallback(IAsyncResult ar)
         {
             ConnectAnnounceState announceState = (ConnectAnnounceState)ar;
              try
              {
                  if (announceState.SavedException != null)
                  {
                      FailureMessage = announceState.SavedException.Message;
                      DoAnnounceComplete(false, announceState.AsyncState, new List<Peer>());
                      return;
                  }
                  UdpTrackerMessage rsp = Receive(announceState, announceState.Data);
                  if (!(rsp is AnnounceResponseMessage))
                  {
                      DoAnnounceComplete(false, announceState.AsyncState, new List<Peer>());
                      return;
                  }
  
                  MinUpdateInterval = ((AnnounceResponseMessage)rsp).Interval;
                  CompleteAnnounce(rsp, announceState.AsyncState);
              }
              catch
              {
                  DoAnnounceComplete(false, announceState.AsyncState, null);
              }
         }
  
         private void CompleteAnnounce(UdpTrackerMessage message, object state)
         {
             ErrorMessage error = message as ErrorMessage;
             if (error != null)
             {
                 FailureMessage = error.Error;
                 DoAnnounceComplete(false, state, new List<Peer>());
             }
             else
             {
                 AnnounceResponseMessage response = (AnnounceResponseMessage)message;
                 DoAnnounceComplete(true, state, response.Peers);
                 
                 //TODO seeders and leechers is not used in event.
             }
         }
  
         private void DoAnnounceComplete(bool successful, object state, List<Peer> peers)
         {
             RaiseAnnounceComplete(new AnnounceResponseEventArgs(this, state, successful, peers));
         }
  
         #endregion
  
         #region connect
  
         private void Connect(UdpTrackerAsyncState connectState)
         {
             connectState.Message = new ConnectMessage();
             tracker.Connect(Uri.Host, Uri.Port);
             SendAndReceive(connectState);
         }
  
         private bool ConnectCallback(IAsyncResult ar)
          {
              UdpTrackerAsyncState trackerState = (UdpTrackerAsyncState)ar;
              try
              {
                  UdpTrackerMessage msg = Receive(trackerState, trackerState.Data);
                  if (msg == null)
                      return false;//bad transaction id
                  ConnectResponseMessage rsp = msg as ConnectResponseMessage;
                  if (rsp == null)
                  {
                      //is there a possibility to have a message which is not error message or connect rsp but udp msg
                      FailureMessage = ((ErrorMessage)msg).Error;
                      return false;//error message
                  }
                  connectionId = rsp.ConnectionId;
                  hasConnected = true;
                  amConnecting = false;
                  return true;
              }
              catch
              {
                  return false;
              }
         }
         #endregion
  
         #region scrape
  
         public override void Scrape(ScrapeParameters parameters, object state)
         {
             //LastUpdated = DateTime.Now;
             if (!hasConnected && amConnecting)
                 return;
  
             if (!hasConnected)
             {
                 amConnecting = true;
                 try
                 {
                     Connect(new ConnectScrapeState(parameters, ConnectScrapeCallback, state));
                 }
                 catch (SocketException)
                 {
                     DoScrapeComplete(false, state);
                     return;
                 }
             }
             else
                 DoScrape(parameters, state);
         }
         private void ConnectScrapeCallback(IAsyncResult ar)
         {
             ConnectScrapeState scrapeState = (ConnectScrapeState)ar;
              try
              {
                  if (scrapeState.SavedException != null)
                  {
                      FailureMessage = scrapeState.SavedException.Message;
                      amConnecting = false;
                      DoScrapeComplete(false, scrapeState.AsyncState);
                      return;
                  }
  
gt:                 if (!ConnectCallback(ar))//bad transaction id