package ftp_client;
import java.util.ArrayList;
import java.util.Collection;

// Classe abstraite définissant une tache de transfert
public abstract class Task {
	
	/* Parent scheduler */
	Scheduler scheduler; // Ordonnanceur a qui l'object appartient
	public Scheduler getScheduler(){ return scheduler; }
	
	/* Variables */
	protected boolean aborted, stopped, finished; // Annulee, arretee, finie
	public boolean isAborted(){ return aborted; }
	public boolean hasStopped(){ return stopped; }
	public boolean isFinished(){ return finished; }
	
	private boolean auto; // Tache a demarrage automatique (tache non-manuelle)
	public void setAuto(final boolean auto){
		if(this.auto == auto) return; // Si elle est deja dans l'etat "auto", alors quitter
		this.auto = auto;
		this.hasToStop = false; // La classe n'a plus a s'arreter car le gerant (ordonnanceur ou utilisateur) change
		fireAutomaticListener(this, auto); // On previent les ecouteurs que le mode de la tache a change
		scheduler.Start();	// On demande a l'ordonnanceur de demarrer une tache (si la tache devient manuelle, elle laisse la place a une autre, sinon elle demarre ou se met en attente)
	}
	public boolean getAuto(){ return auto; }
	
	// Demande d'arret
	public void Stop(){
		if(hasToStop || stopped || finished || aborted || !working) return;
		hasToStop = true;
		fireStoppingListener(this); // On previent les ecouteurs qu'on est en train de s'arreter
	}
	public boolean hasToStop(){ return hasToStop; }
	private boolean hasToStop;
	
	// En travail ou non
	private boolean working;
	public boolean isWorking(){ return working; }
	
	// Nombre de tentative en cas d'erreur
	int ExceptionCount;
	int MaxExceptionCount;
	
	// Detruit ou non
	private boolean _isDisposed;
	public	boolean isDisposed(){ return _isDisposed; }
	
	// Informations (octets envoyes, taille du fichier, nom fichier source, nom fichier destination, repertoire distant)
	protected long bytesSent, fileSize;	
	protected String 	srcFile, dstFile, rmtPath;
		
	public String 	getSourcesrcFile(){	return srcFile;	}
	public String 	getdstFile(){		return dstFile;}
	public String 	getrmtPath(){			return rmtPath;	}
	public long 	getTotalBytesSent(){	return bytesSent;	}
	public long 	getFileSize(){			return fileSize;	}
	// Ratio du transfert
	public double 	getProgress(){	if(fileSize==bytesSent) return 1.0;	return ((double)bytesSent) / fileSize;	}
	
	
	/* Constructor */
	public Task(Scheduler scheduler) throws Exception{
		if(scheduler == null) throw new Exception("Scheduler null");
		
		this.scheduler = scheduler;
		
		this.hasToStop = this.working = this.aborted = this._isDisposed = this.stopped = this.finished = false;
		this.ExceptionCount = 0;
		this.MaxExceptionCount = 3;
		this.auto = false;
		
		this.scheduler.addTask(this);
	}
	
	/* Dispose */
	// Destruction de la tache
	public void finalize(){ Dispose(); } // Appeler lors de la suppression de l'element par le GarbageCollector
	public void Dispose(){	if(_isDisposed) return; _isDisposed = true; scheduler.removeTask(this); fireDisposedListener(this); }
		
	/* Unarbort */
	// "De-annulation" de la tache
	public void unAbort(){
		if(!aborted) return;
		aborted = false;
		ExceptionCount = 0;
		fireUnabortedListener(this);
		scheduler.Start();
	}
	
	/* Start */
	// Demarrage de la tache
	public synchronized void Start(){
		// Si annulee ou deja en travail ou detruite ou finie, quitter
		if(aborted || working || _isDisposed || finished) return;
		hasToStop = false; // La tache vient de demarrer, on ne lui demande donc pas de s'arreter
		
		// Le travail debute
		workBegin();
		
		new Thread(){
			public void run(){
				doWork(); // Travail de la tache
				if(stopped) fireStoppedListener(Task.this); // On previent que la tache s'est arrete si c'est le cas
				workEnd(!stopped); // Fin du travail, on demande la destruction seulement si la tache a fini son travail sans etre arretee
			}
		}.start();
	}
	
	/* The task's work, need to be override */
	// Travail de la tache, a definir dans la classe fille
	protected abstract void doWork();
	
	/* Call when work begin */
	// Debut du travail
	private void workBegin(){
		working = true; // On est en travail comme cela commence
		fireStartedListener(this); // On previent les ecouteurs que le travail debutte
	}
	
	/* Call when work finish */
	private void workEnd(boolean dispose){
		working = false; // On ne travail plus etant donne qu'on a fini
		if(dispose) Dispose(); // On se detruit
		scheduler.taskFinished(this); // On previent l'ordonnanceur qu'on a fini le travail
		if(!stopped){ // Si la tache n'a pas ete arretee, alors
			finished = true; // Indiquer que le travail est fini
			fireFinishedListener(this); // On previent les ecouteurs que le travail se termine
		}
	}

	/* Communicate with task owners (here TransfertItem) */
	// Interface de tache
	public interface TaskListener {
		void Disposed(Task who); // Lorsqu'elle est detruite
		void Started(Task who);  // Lorsqu'elle debute
		void Stopped(Task who);  // Lorsqu'elle s'est arretee
		void Stopping(Task who); // Lorsqu'elle s'arrete
		void Finished(Task who); // Lorsqu'elle s'est finie
		void Aborted(Task who);  // Lorsqu'elle est annulee
		void Unaborted(Task who);// Lorsqu'elle est "de-annulee"
		void Automatic(Task who, boolean yes); // Lorsqu'elle devient automatique ou manuelle
	}

	private final Collection<TaskListener> taskListeners = new ArrayList<TaskListener>();

	protected void fireAutomaticListener(Task who, boolean yes) {
		for(TaskListener listener : taskListeners)
			listener.Automatic(who, yes);
	}

	protected void fireAbortedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Aborted(who);
	}

	protected void fireUnabortedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Unaborted(who);
	}

	protected void fireStoppedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Stopped(who);
	}

	protected void fireDisposedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Disposed(who);
	}

	protected void fireStoppingListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Stopping(who);
	}

	protected void fireFinishedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Finished(who);
	}

	protected void fireStartedListener(Task who) {
		for(TaskListener listener : taskListeners)
			listener.Started(who);
	}

	public void addTaskListener(TaskListener listener) {
		taskListeners.add(listener);
	}

	public void removeTaskListener(TaskListener listener) {
		taskListeners.remove(listener);
	}

}
