/***************************************************************************
 *   Copyright (C) 2005 by Joris Guisson                                   *
 *   joris.guisson@gmail.com                                               *
 *                                                                         *
 *   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.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#ifndef BTCHUNKMANAGER_H
#define BTCHUNKMANAGER_H

#include <QMap>
#include <QSet>
#include <QString>
#include <QObject>
#include <vector> 
#include <util/bitset.h>
#include <torrent/torrent.h>
#include <ktorrent_export.h>
#include "chunk.h"


class QStringList;

namespace bt
{
	class TorrentFile;
	class PreallocationThread;
	class TorrentFileInterface;
	class CacheFactory;
	class Job;

	struct NewChunkHeader
	{
		unsigned int index; // the Chunks index
		unsigned int deprecated; // offset in cache file
	};
	
	/**
	 * @author Joris Guisson
	 * 
	 * Manages all Chunk's and the cache file, where all the chunk's are stored.
	 * It also manages a separate index file, where the position of each piece
	 * in the cache file is stored.
	 *
	 * The chunks are stored in the cache file in the correct order. Eliminating
	 * the need for a file reconstruction algorithm for single files.
	 */
	class KTORRENT_EXPORT ChunkManager : public QObject
	{
		Q_OBJECT
	public:
		ChunkManager(Torrent & tor,
					 const QString & tmpdir,
					 const QString & datadir,
					 bool custom_output_name,
					 CacheFactory* fac);
		virtual ~ChunkManager();

		/// Get the torrent
		const Torrent & getTorrent() const {return tor;}
		
		/// Get the data dir
		QString getDataDir() const;
		
		/// Get the actual output path
		QString getOutputPath() const;
		
		void changeOutputPath(const QString& output_path);
		
		/// Remove obsolete chunks
		void checkMemoryUsage();
		
		/**
		 * Change the data dir.
		 * @param data_dir 
		 */
		void changeDataDir(const QString & data_dir);
		
		/**
		 * Move the data files of the torrent.
		 * @param ndir The new directory
		 */
		Job* moveDataFiles(const QString & ndir);
		
		/**
		 * A move of data files has finished
		 * @param job The job doing the move
		 */
		void moveDataFilesFinished(Job* job);
		
		/**
		 * Move some data files to a new location
		 * @param files Map of files and their new location
		 */
		Job* moveDataFiles(const QMap<TorrentFileInterface*,QString> & files);
		
		
		/**
		 * A move of data files with the map has finished
		 * @param files Map of files and their new location
		 * @param job The job doing the move
		 */
		void moveDataFilesFinished(const QMap<TorrentFileInterface*,QString> & files,Job* job);
		
		/**
		 * Loads the index file.
		 * @throw Error When it can be loaded
		 */
		void loadIndexFile();
		
		/**
		 * Create the cache file, and index files.
		 * @param check_priority Check if priority of chunk matches that of files
		 * @throw Error When it can be created
		 */
		void createFiles(bool check_priority = false);
		
		/**
		 * Test all files and see if they are not missing.
		 * If so put them in a list
		 */
		bool hasMissingFiles(QStringList & sl);
		
		/**
		 * Prepare diskspace preallocation
		 * @param prealloc The thread going to do the preallocation
		 */
		void preparePreallocation(PreallocationThread* prealloc);
		
		/**
		 * Open the necessary files when the download gets started.
		 */
		void start();
		
		/**
		 * Closes files when the download gets stopped.
		 */
		void stop();
		
		/**
		 * Get's the i'th Chunk.
		 * @param i The Chunk's index
		 * @return The Chunk, or 0 when i is out of bounds
		 */
		Chunk* getChunk(unsigned int i);
		
		/**
		 * Reset a chunk as if it were never downloaded.
		 * @param i The chunk
		 */
		void resetChunk(unsigned int i);
		
		/**
		 * Mark a chunk as downloaded.
		 * @param i The Chunk's index
		 */
		void chunkDownloaded(unsigned int i);
		
		/**
		 * Calculates the number of bytes left for the tracker. Does include 
		 * excluded chunks (this should be used for the tracker).
		 * @return The number of bytes to download + the number of bytes excluded
		 */
		Uint64 bytesLeft() const;
		
		/**
		 * Calculates the number of bytes left to download.
		 */
		Uint64 bytesLeftToDownload() const;

		/**
		 * Calculates the number of bytes which have been excluded.
		 * @return The number of bytes excluded
		 */
		Uint64 bytesExcluded() const;
		
		/**
		 * Calculates the number of chunks left to download. 
		 * Does not include excluded chunks.
		 * @return The number of chunks to download
		 */
		Uint32 chunksLeft() const;
		
		/**
		 * Check if we have all chunks, this is not the same as
		 * chunksLeft() == 0, it does not look at excluded chunks.
		 * @return true if all chunks have been downloaded
		 */
		bool haveAllChunks() const;

		/**
		 * Get the number of chunks which have been excluded.
		 * @return The number of excluded chunks
		 */
		Uint32 chunksExcluded() const;
		
		/**
		 * Get the number of downloaded chunks
		 * @return 
		 */
		Uint32 chunksDownloaded() const;
		
		/**
		 * Get the number of only seed chunks.
		 */
		Uint32 onlySeedChunks() const;
		
		/**
		 * Get a BitSet of the status of all Chunks
		 */
		const BitSet & getBitSet() const {return bitset;}

		/**
		 * Get the excluded bitset
		 */
		const BitSet & getExcludedBitSet() const {return excluded_chunks;}
		
		/**
		 * Get the only seed bitset. 
		 */
		const BitSet & getOnlySeedBitSet() const {return only_seed_chunks;}

		/// Get the number of chunks into the file.
		Uint32 getNumChunks() const {return tor.getNumChunks();}

		/// Print memory usage to log file
		void debugPrintMemUsage();

		/**
		 * Make sure that a range will get priority over other chunks.
		 * @param from First chunk in range
		 * @param to Last chunk in range
		 */
		void prioritise(Uint32 from,Uint32 to, Priority priority);

		/**
		 * Make sure that a range will not be downloaded.
		 * @param from First chunk in range
		 * @param to Last chunk in range
		 */
		void exclude(Uint32 from,Uint32 to);

		/**
		 * Make sure that a range will be downloaded.
		 * Does the opposite of exclude.
		 * @param from First chunk in range
		 * @param to Last chunk in range
		 */
		void include(Uint32 from,Uint32 to);
		
		
		/**
		 * Data has been checked, and these chunks are OK.
		 * The ChunkManager will update it's internal structures
		 * @param ok_chunks The ok_chunks
		 * @param from First chunk of the check
		 * @param to Last chunk of the check
		 */
		void dataChecked(const BitSet & ok_chunks, Uint32 from, Uint32 to);
		
		/// Test if the torrent has existing files, only works the first time a torrent is loaded
		bool hasExistingFiles() const;
		
		/// Mark all existing files as downloaded
		void markExistingFilesAsDownloaded();
		
		/// Recreates missing files
		void recreateMissingFiles();
		
		/// Set missing files as do not download
		void dndMissingFiles();
		
		/// Delete all data files 
		Job* deleteDataFiles();
		
		/// Are all not deselected chunks downloaded.
		bool completed() const;
		
		/// Set the preview sizes for audio and video files
		static void setPreviewSizes(Uint32 audio,Uint32 video);

		/// Get the current disk usage of all the files in this torrent
		Uint64 diskUsage();
		
		/// Get the size in chunks of the preview range of a file of the torrent
		Uint32 previewChunkRangeSize(const TorrentFile & tf) const;
		
		/// Get the size in chunks of the preview range for a single file torrent
		Uint32 previewChunkRangeSize() const;
		
		/// The download priority of a file has changed
		void downloadPriorityChanged(TorrentFile* tf,Priority newpriority,Priority oldpriority);
		
		/// Is the storage mounted ?
		bool isStorageMounted(QStringList & missing);
		
	signals:
		/**
		 * Emitted when a range of chunks has been excluded
		 * @param from First chunk in range
		 * @param to Last chunk in range
		 */
		void excluded(Uint32 from,Uint32 to);
		
		/**
		 * Emitted when a range of chunks has been included back.
		 * @param from First chunk in range
		 * @param to Last chunk in range
		 */
		void included(Uint32 from,Uint32 to);
		
		/**
		 * Emitted when chunks get excluded or included, so
		 * that the statistics can be updated.
		 */
		void updateStats();
		
		/**
		 * A corrupted chunk has been found during uploading.
		 * @param chunk The chunk
		 */
		void corrupted(Uint32 chunk);
		
	private:
		static Uint32 preview_size_audio;
		static Uint32 preview_size_video;
		
		class Private;
		Private* d;
		Torrent & tor;
		BitSet bitset;
		BitSet excluded_chunks;
		BitSet only_seed_chunks;
	};

}

#endif
