// ProtocolActionQueue.cs
//
//  Copyright (C) 2008 Christian Eide
//
// 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using bareFTP.Protocol;

namespace bareFTP.Connection
{
	
	public class ProtocolActionQueue
	{
		private List<IProtocolAction> tq;
		private List<Thread> threads;
		private Thread threadxfer;
		private Connection conn;
		
		public event EventHandler QueueEmpty;
		
		public virtual void OnQueueEmpty()
		{
			QueueEmpty(this, new EventArgs());
		}
		
		public ProtocolActionQueue(Connection conn)
		{
			this.conn = conn;
			tq = new List<IProtocolAction>();
			threads = new List<Thread>();
			threadxfer = new Thread(DoAction);
		}
		
		public void Add(IProtocolAction action)
		{
			lock(tq)
			{
				tq.Add(action);
			}
		}
		
		public void StartTransfers()
		{
			if(threadxfer.ThreadState == ThreadState.Unstarted)
				threadxfer.Start();
			else if(threadxfer.ThreadState == ThreadState.Stopped)
			{
				threadxfer = new Thread(DoAction);
				threadxfer.Start();
			}
		}
		
		public void AbortAllTransfers()
		{
			for(int x=0;x<threads.Count;x++)
				threads[0].Abort();
			
			this.threadxfer.Abort();
		}
		
		public void Reorder(XferFile file, int direction)
		{
			try
			{
				System.Collections.IEnumerator en = tq.GetEnumerator();
				int position = 0;
				int counter = 0;
				IProtocolAction action = null;
				while(en.MoveNext())
				{
					if(((IProtocolAction)en.Current).File.FileId == file.FileId)
					{
						action = en.Current as IProtocolAction;
						position = counter;
					}
					counter++;
				}
				
				tq.RemoveAt(position);
				
				if(direction == 0)
					tq.Insert(position - 1, action);
				
				else if(direction == 1)
					tq.Insert(position + 1, action);
			}
			catch{}
		}
		
		private void DoAction()
		{
			
			while(tq.Count > 0)
			{
				if(!tq[0].File.IsDir && conn.Configuration.General_SimultaneousTransfers && conn.Protocol != ProtocolType.SFTP)
				{
					
					while(threads.Count >= conn.Configuration.General_MaxConnections)
					{
						Thread.Sleep(500);
					}
					
					Thread xferthread = new Thread(ExecuteTask);
					xferthread.Start();
					threads.Add(xferthread);
					Thread.Sleep(300);
				}
				else
					ExecuteTask();
			}
			
		}
		
		private void CheckCleanup()
		{
			
			for(int x=0;x<threads.Count;x++)
			{
				if(!threads[x].IsAlive)
					threads.RemoveAt(x);
			}
			
			if(tq.Count == 0 && threads.Count <= 1)
			{
				OnQueueEmpty();	
			}
				
		}
		
		private void ExecuteTask()
		{
			IProtocolAction a = null;
			
			lock(tq)
			{
				if(tq.Count > 0)
				{
					a = tq[0];
					tq.RemoveAt(0);
				}
			}
			
			if(a != null)
				a.Execute(conn);
			CheckCleanup();
		}	
	}
}
