/*
 * (c) 2005 Research Group Dr. Fuhrmann, University of Karlsruhe, Germany
 * All rights reserved.
 *
 * Author:
 *         Stefan Denk <denk@ira.uka.de>
 *         Kendy Kutzner <kutzner@ira.uka.de>
 *
 * $Id: task.h,v 1.4 2005/12/27 12:12:33 kutzner Exp $
 *
 */

#ifndef _TASK_H_
#define _TASK_H_

// vidtrans

#include "downloadstatus.h"
// #include "clientthread.h"
#include "videosources.h"
class clientthread;

/** A single thing we plan to do.
 * Every recording we potentially can fulfil is one instance of this class.
 * Not all tasks will have a client thread, because too many client threads will
 * bog down the network link.
 *
 * Big bug: a task should send queries and maintain a sourcelist for every
 * minute it covers. Currently only the first minute is queried for and kept in
 * sources.
 *
 * This is not a bug, now there is a single task for every minute we want to record.
 */

class task {
	public:
		/// Construct a new task.
		task(time_t start, time_t stop, const cChannel * channel, int count, int total, downloadstatus *, const char * name);
		/** Get the client thread.
		 * \return The client thread, NULL if there is no such thread.
		 * Note that the thread may be inactive.
		 */
		clientthread * getclientthread(){return _clientthread;}
		/** Start a thread working on this task.
		 * Try to start a new thread which should work on this particular task.
		 */
		void startthread();
		/// Delete the client thread again
		void deletethread();
		/** Internal status of this task.
		 * - changed to requesting_sources at construction time
		 * - changed to ready_to_spawn by ctasks::request_fobar
		 * - changed to clientthreadrunning by task::start_client_threads
		 * - changed to requesting_sources or done by clientthread
		 */
		enum status {
			requesting_sources,	///< busy doing igor-lookups
			readytospawn,		///< igor-lookup is done, waiting for client thread to be created
			clientthreadrunning,	///< client thread started
			//parsing,		///< client thread came back, checking results \todo move this into client thread because it may take a while?
			//done,			///< done, waiting for destruction
		};
	private:
		/** Start time of the user wish.
		 * In seconds since the unix epoch. May be rounded to a full
		 * minute.
		 */
		time_t _start;
		/** Stop time of the user wish.
		 * In seconds since the unix epoch. May be rounded to a full
		 * minute.
		 */
		time_t _stop;
		/** Which channel the user wants to record.
		 * Pointer to internal vdr-structures.
		 * Because points to a vdr-structure, only
		 * channels available in vdr can be fetched
		 * from the network.
		 */
		const cChannel * _channel;
		// ...
		/** Time of last request towards IGOR.
		 * This timer memorizes when we last requested a video source
		 * for this block from the IGOR. Its purpose is to avoid
		 * excessive requests for non-existing blocks.
		 */
		time_t _last_request;
		/** The worker thread.
		 * Pointer to the thread working on this task.
		 */
		clientthread * _clientthread;
		/** Where to get this piece of video.
		 * If we are in state requesting_sources, this list may be empty.
		 */
		sourcelist_t _sources;
		/// The status of this task.
		status _state;
		/// When the last query was sent out.
		time_t _lastqueried;
	public:
		/// Export the state
		status state(){return _state;}
		/// Change the state
		void changestate(status s){_state = s;}
		/** Tell last time when we sent out a query for this task
		 * \return
		 *  - seconds since the unix epoch when the last query message was sent out.
		 *  - 0 When no query was sent yet
		 */
		time_t lastqueried() const {return _lastqueried;}
		/// Return the start time.
		time_t getstart() const {return _start;}
		/// Return the stop time.
		time_t getstop() const {return _stop;}
		/// Return the channel.
		const cChannel * getchannel() const {return _channel;}
		/// Return a reference to the source list.
		sourcelist_t & sourcesref(){return _sources;}
		/// Send a query to find sources.
		void sendquery(int sd);
	private:
		/// We are potentially part of a larger download, this is the running number starting with 1.
		int _count;
		/// And if we are, we would like to know how many parts (minutes) this download will be composed of.
		int _total;
		/// And we would also like to know the filename the user has specified to save the recording to.
		const char * _name;
		
};

/** All we have to do.
 * Remember everything we have to do. Starts clients threads to get things
 * done.
 */

class ctasks {
	private:
		/** The structure used to keep track of tasks.
		 * Currently this is a list. Maybe a map (or maps) from start
		 * time / channel to task would be more appropriate?
		 */
		typedef list<task*> tasklist_t;
		/// All we have to do.
		tasklist_t tasklist;
		/// How many clients are running.
		int clients_running();
		/** Maximum number of client threads.
		 * This limit may be set via the public getter/setter methods.
		 * The new limit will not be effective imediately. If the
		 * limit is raised, the number of running threads will increase
		 * slowly. If the limit is lowerd, no threads will be killed,
		 * but no new threads will be started. It is ok to set this
		 * value to zero, in this case all client activity is stopped.
		 */
		int _client_threads_max;
		/** Minimum interval between two requests for the same source.
		 * This number is only a lower bound, it may become much
		 * larger if there are many things to request.
		 * \todo Make it changeable via user interface.
		 */
		int singletask_request_interval;
		/** Maximum number of requests per single housekeeping call.
		 * The number of requests sent can be smaller if the
		 * singletask_request_intevals have been hit.
		 * \todo Make it changeable via user interface.
		 */
		int requests_per_housekeeping;
		/** Mutex class for locking
		 */
		cMutex _mutex;
	public:
		/** ctor to initialize things */
		ctasks();
		/// Potentially start client threads.
		void potentially_start_client_threads();
		/** Copy vdr timers as tasks.
		 * Iterates over the things vdr wants to have and creates tasks from it.
		 */
		void copy_vdr_timers();
		/** Check if we already trying to do this.
		 * \return true iff we already try this.
		 */
		bool have(time_t, time_t, const cChannel*);
		/// Create a new task.
		void add(time_t start, time_t stop, const cChannel*, int count, const char * name);
		/** Get client thread limit.
		 * \return the current limit.
		 */
		int get_client_threads_max(){return _client_threads_max;}
		/** Set the client thread limit.
		 * See docu on the parameter itself.
		 * \param num The new number of allowed client threads.
		 */
		void set_client_threads_max(int num);
		/// Do the stuff that needs to be done in regular intervals.
		bool housekeeping(int sd, cvideosources &);
		/** Change status
		 * Tell the vdr that thing is now on disk
		 */
		void mark_done(time_t, time_t, const cChannel*);
};


#endif // _TASK_H_
