context: #region Copyright (c) 2013 Hathi Project < http://hathi.sourceforge.net >
  /*
  * This file is part of Hathi Project
  * Hathi Developers Team:
  * andrewdev, beckman16, biskvit, elnomade_devel, ershyams, grefly, jpierce420,
  * knocte, kshah05, manudenfer, palutz, ramone_hamilton, soudamini, writetogupta
  *
  * Hathi is a fork of Lphant Version 1.0 GPL
  * Lphant Team
  * Juanjo, 70n1, toertchn, FeuerFrei, mimontyf, finrold, jicxicmic, bladmorv,
  * andrerib, arcange|, montagu, wins, RangO, FAV, roytam1, Jesse
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either
  * version 2 of the License, or (at your option) any later version.
  *
  * This program 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 General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
  #endregion
  
  using System;
  using System.Collections;
  using System.Diagnostics;
  
  namespace Hathi.eDonkey
  {
  /// <summary>
  /// Summary description for CSourcesList.
  /// </summary>
  internal class CSourcesList
  {
      private ArrayList m_ArrayList;
      private CElement m_Element;
      private ushort[] m_Avaibility;
      private DateTime m_LastTCPSearch;
      private DateTime m_LastSourceExchangeRequest;
      private float m_DownloadSpeed;
      private float m_PrevDownloadSpeed;
      private ushort m_nUsableClients;
      private ushort m_nDownloadingClients;
  
      public DateTime GetLastSourceExchangeRequest() {
          return m_LastSourceExchangeRequest;
      }
      public void SetLastSourceExchangeRequest() {
          m_LastSourceExchangeRequest=DateTime.Now;
      }
      public float GetDownloadSpeed() {
          return (float)Math.Round(m_DownloadSpeed/1024F,1);
      }
  
      public ushort GetDownloadingClients() {
          return m_nDownloadingClients;
      }
      public ushort GetUsableClients() {
          return m_nUsableClients;
      }
      public ushort[] GetChunksAvaibility() {
          return m_Avaibility;
      }
  
      public CSourcesList(CElement in_Element)
      {
          m_ArrayList=ArrayList.Synchronized(new ArrayList());
          m_Element=in_Element;
          m_Avaibility=null;
          Random rnd=new Random();
          m_LastTCPSearch=DateTime.MinValue; //-TimeSpan.FromSeconds(rnd.Next(60));
          m_LastSourceExchangeRequest=DateTime.MinValue;
          m_DownloadSpeed=0;
      }
  
      public int Count()
      {
          return m_ArrayList.Count;
      }
  
      public IEnumerator GetEnumerator()
      {
          return m_ArrayList.GetEnumerator();
      }
  
      public virtual object SyncRoot()
      {
          return m_ArrayList.SyncRoot;
      }
  
      public object Item(int index)
      {
          return m_ArrayList[index];
      }
  
      public CClient GetSource(uint ID,ushort port,uint serverIP,byte[] ClientHash)
      {
          return CKernel.GetClient(m_ArrayList,ID,port,serverIP,ClientHash);
      }
  
      public bool RemoveNoNeededSource()
      {
          CClient sourceToRemove=null;
          CClient sourceToRemove2=null;
          lock (m_ArrayList.SyncRoot)
          {
              foreach (CClient source in m_ArrayList)
              {
                  //try to remove no need source
                  if (source.DownloadState==Protocol.DownloadState.NoNeededParts)
                  {
                      sourceToRemove=source;
                      break;
                  }
                  //if not possible try to remove queue full source
                  if ((source.DownloadState==Protocol.DownloadState.OnQueue)&&
                          ((source.connection==null)||(!source.connection.Connected))&&
                          (source.QRDownload==0))
                  {
                      sourceToRemove2=source;
                  }
              }
          }
          if (sourceToRemove!=null)
          {
              Debug.WriteLine("REMOVED: " + sourceToRemove.UserName);
              RemoveSource(sourceToRemove);
              return true;
          }
          if (sourceToRemove2!=null)
          {
              Debug.WriteLine("REMOVED2: " + sourceToRemove2.UserName);
              RemoveSource(sourceToRemove2);
              return true;
          }
          return false;
      }
  
      public bool AddSource(CClient source)
      {
          bool notMaxSources;
          notMaxSources=((m_ArrayList.Count<CKernel.Preferences.GetInt("MaxSourcesPerFile"))||
                         (RemoveNoNeededSource()));
          if ((notMaxSources)
                  &&(!m_ArrayList.Contains(source))
                  &&((source.UserID!=CKernel.Preferences.GetUInt("ID"))
                     ||(source.Port!=CKernel.Preferences.GetUShort("TCPPort"))))
          {
              //this is an additional check to avoid duplicated sources
              //is it really needed, does it solve the problem at 100% ?
              CClient existingSource=CKernel.GetClient(m_ArrayList,source.UserID,source.Port,source.ServerIP,source.UserHash);
              if (existingSource!=null)
              {
  #if DEBUG
                  //CLog.Log(Types.Constants.Log.Verbose,"Duplicated source detected: "+ source.UserName + "-" + source.UserID.ToString() + "-" + m_ArrayList.Contains(source).ToString());
  #endif
                  //attach the client to the existing one
                  source=existingSource;
                  return false;
              }
              //
              m_ArrayList.Add(source);
              CKernel.NewSource(source, CKernel.HashToString(source.DownFileHash));
              return true;
          }
          else CKernel.ClientsList.IsClientNeeded(source);
          return false;
      }
  
      public void UpdateChunksAvaibility()
      {
          byte[] DownFileChunks;
          if (m_Avaibility==null) m_Avaibility=new ushort[CHash.GetChunksCount(m_Element.File.FileSize)];
          ushort[] avaibilitytmp=new ushort[m_Avaibility.Length];
          if (m_Element==null) return;
          int nChunks=m_Element.File.NumChunks;
          for (int i=0; i!=avaibilitytmp.Length; i++) avaibilitytmp[i]=0;
          lock (m_ArrayList.SyncRoot)
          {
              foreach (CClient source in m_ArrayList)
              {
                  if (m_Element==null) return;
                  DownFileChunks=source.DownFileChunks;
                  if (DownFileChunks!=null)
                      for (int i=0; i!=nChunks; i++)
                      {
                          if ((Protocol.ChunkState)DownFileChunks[i]==Protocol.ChunkState.Complete)
                              avaibilitytmp[i]++;
                      }
              }
          }
          m_Avaibility=avaibilitytmp;
      }
      public void AddChunksAvaibility(byte[] chunkStatus)
      {
          if (m_Avaibility==null) m_Avaibility=new ushort[CHash.GetChunksCount(m_Element.File.FileSize)];
          if ((chunkStatus==null)||(chunkStatus.Length!=m_Avaibility.Length)) return;
          for (int i=0; i!=chunkStatus.Length; i++)
          {
              if ((Protocol.ChunkState)chunkStatus[i]==Protocol.ChunkState.Complete)
                  m_Avaibility[i]++;
          }
      }
      public void RemoveChunksAvaibility(byte[] chunkStatus)
      {
          if (m_Element==null) return; //possible when stopping the file
          if (m_Avaibility==null) m_Avaibility=new ushort[CHash.GetChunksCount(m_Element.File.FileSize)];
          if ((chunkStatus==null)||(chunkStatus.Length!=m_Avaibility.Length)) return;
          for (int i=0; i!=chunkStatus.Length; i++)
          {
              if (((Protocol.ChunkState)chunkStatus[i]==Protocol.ChunkState.Complete)
                      &&(m_Avaibility[i]>0))
                  m_Avaibility[i]--;
          }
      }
      public int SelectChunk(byte[] clientPartStatus,int lastRequestedPart)
      {
          ArrayList ActiveRareChunks=new ArrayList(); //prioridad 1
          ArrayList ParcialRareChunks=new ArrayList();    //prioridad 2
          ArrayList RareChunks=new ArrayList();           //prioridad 3
          ArrayList ParcialChunks=new ArrayList();            //prioridad 4
          ArrayList EmptyChunks=new ArrayList();          //prioridad 5
          ArrayList ActiveChunks=new ArrayList();     //prioridad 6
          uint minRareChunks=10;
          uint maxRareChunks=1;
          if (m_Element==null) return -1;
          if ((lastRequestedPart>-1)&&(m_Element.File.RemainingToAskInChunk(lastRequestedPart)))
              return lastRequestedPart;
          if (m_Avaibility.Length==1) //si el fichero solo tiene una parte
              return 0;
          byte[] chunkStatus=m_Element.File.ChunksStatus;
          //calcular la ventana de partes Raras
  
gt:         for (int i=0; i!=m_Avaibility.Length; i++)