/*
 * Created on May 25, 2004
 * Created by Alon Rohter
 * Copyright (C) Azureus Software, Inc, All Rights Reserved.
 *
 * 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.
 *
 */

package com.aelitis.azureus.core.helpers;

import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.config.ParameterListener;
import org.gudy.azureus2.core3.download.DownloadManager;
import org.gudy.azureus2.core3.global.GlobalManager;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.torrent.TOTorrent;
import org.gudy.azureus2.core3.util.*;

/**
 * Watches a folder for new torrents and imports them.
 * NOTE: Folder-to-watch and other watching params are taken from a global
 *       config option right now, so starting multiple instances of
 *       TorrentFolderWatcher is useless as currently coded.
 */
public class TorrentFolderWatcher {
	private final static LogIDs LOGID = LogIDs.CORE;

	private final static String PARAMID_FOLDER = "Watch Torrent Folder";

	private volatile GlobalManager _global_manager;

	private volatile boolean running = false;

	private final ArrayList to_delete = new ArrayList();

	protected AEMonitor this_mon = new AEMonitor("TorrentFolderWatcher");

	private FilenameFilter filename_filter = new FilenameFilter() {
		public boolean accept(File dir, String name) {
			String lc_name = name.toLowerCase();

			return (lc_name.endsWith(".torrent") || lc_name.endsWith(".tor"));
		}
	};

	private ParameterListener param_listener = new ParameterListener() {
		public void parameterChanged(String parameterName) {
			if (COConfigurationManager.getBooleanParameter(PARAMID_FOLDER)) {
				if (!running) {
					running = true;
					if ( !watch_thread.isAlive()){
							// crap code this but I can't be bothered to fix it
						watch_thread.setDaemon(true);
						watch_thread.setPriority(Thread.MIN_PRIORITY);
						watch_thread.start();
					}
				}
			} else{
				running = false;
			}
		}
	};

	private final Thread watch_thread = 
		new AEThread("FolderWatcher")
		{
			private long	last_run = 0;	// trigger a run on startup
			
			private AESemaphore wait_sem = new AESemaphore( "fw:wait" );
			
			{
				COConfigurationManager.addParameterListener(
					new String[]{
						PARAMID_FOLDER,
						"Watch Torrent Folder Interval Secs",
					},
					new ParameterListener()
					{
						public void 
						parameterChanged(
							String name )
						{
							wait_sem.release();
						};
					});
			}
			
			public void 
			runSupport() 
			{
				while( true ){
					
					while( true ){
						
						long	now = SystemTime.getMonotonousTime();
						
						int sleep_secs = COConfigurationManager.getIntParameter("Watch Torrent Folder Interval Secs");
						
						if ( sleep_secs < 1 ){
							
							sleep_secs = 1;
						}
						
						int sleep_ms	= sleep_secs*1000;
						
						long	remaining = last_run + sleep_ms - now;
						
						if ( remaining < 250 || last_run == 0 ){
							
							last_run = now;
							
							break;
						}
						
						if ( remaining < 250 ){
								
							remaining = 250;
						}
														
						wait_sem.reserve( remaining );
					}
									
					try{
						if ( running ){
							
							importAddedFiles();
														
						}else{
							
							wait_sem.reserve(60*1000);
						}
												
					}catch( Throwable e){
						
						Debug.out( e );
					}
				}
			}
		};

	/**
	 * Start a folder watcher, which will auto-import torrents via the given
	 * manager.
	 * 
	 * @param global_manager
	 */
	public TorrentFolderWatcher(GlobalManager global_manager) {
		_global_manager = global_manager;
	}
	
	public void
	start()
	{
		if (COConfigurationManager.getBooleanParameter(PARAMID_FOLDER)) {
			running = true;
			watch_thread.setDaemon(true);
			watch_thread.setPriority(Thread.MIN_PRIORITY);
			watch_thread.start();
		}

		COConfigurationManager.addParameterListener(PARAMID_FOLDER, param_listener);
	}

	/**
	 * Stop and terminate this folder importer watcher.
	 */
	public void destroy() {
		running = false;
		_global_manager = null;
		COConfigurationManager.removeParameterListener(PARAMID_FOLDER,
				param_listener);
	}

	private void importAddedFiles() {

		try {
			this_mon.enter();

			if (!running){
				return;
			}
			
			GlobalManager global_manager = _global_manager;
			
			if ( global_manager == null ){
				
				return;
			}
			
			boolean save_torrents = COConfigurationManager
					.getBooleanParameter("Save Torrent Files");

			String torrent_save_path = COConfigurationManager
					.getStringParameter("General_sDefaultTorrent_Directory");

			int start_state = COConfigurationManager
					.getBooleanParameter("Start Watched Torrents Stopped")
					? DownloadManager.STATE_STOPPED : DownloadManager.STATE_QUEUED;

			String folder_path = COConfigurationManager
					.getStringParameter("Watch Torrent Folder Path");

			String data_save_path = COConfigurationManager
					.getStringParameter("Default save path");

			File folder = null;

			if (folder_path != null && folder_path.length() > 0) {
				folder = new File(folder_path);
				if (!folder.isDirectory()) {
					if (!folder.exists()) {
						FileUtil.mkdirs(folder);
					}
					if (!folder.isDirectory()) {
						if (Logger.isEnabled())
							Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
									"[Watch Torrent Folder Path] " + "does not exist or "
											+ "is not a dir"));
						folder = null;
					}
				}
			}

			if (folder == null) {
				if (Logger.isEnabled())
					Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
							"[Watch Torrent Folder Path] not configured"));
				return;
			}

			File f = null;
			if (data_save_path != null && data_save_path.length() > 0) {
				f = new File(data_save_path);
				
				// Path is not an existing directory.
				if (!f.isDirectory()) {
					if (!f.exists()) {FileUtil.mkdirs(f);}
					
					// If path is still not a directory, abort.
					if (!f.isDirectory()) {
						if (Logger.isEnabled()) {
							Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
									"[Default save path] does not exist or is not a dir"));
						}
						Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR,
								"[Default save path] does not exist or is not a dir"));
						return;
					}
				}
			}
			
			// If we get here, and this is true, then data_save_path isn't valid.
			if (f == null){
				if (Logger.isEnabled()) {
 					Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
						"[Default save path] needs to be set for auto-.torrent-import to work"));
				}
				Logger.log(new LogAlert(LogAlert.UNREPEATABLE, LogAlert.AT_ERROR,
						"[Default save path] needs to be set for auto-.torrent-import to work"));
			}
			
			// if we are saving torrents to the same location as we import them from
			// then we can't assume that its safe to delete the torrent after import! 

			if (torrent_save_path.length() == 0
					|| torrent_save_path.equals(folder_path)
					|| !new File(torrent_save_path).isDirectory()) {

				save_torrents = false;
			}

			//delete torrents from the previous import run

			for (int i = 0; i < to_delete.size(); i++) {

				TOTorrent torrent = (TOTorrent) to_delete.get(i);

				try {
					TorrentUtils.delete(torrent);

				} catch (Throwable e) {

					Debug.printStackTrace(e);
				}
			}

			to_delete.clear();

			String[] currentFileList = folder.list(filename_filter);
			if (currentFileList == null) {
				Logger.log(new LogEvent(LOGID, LogEvent.LT_ERROR,
						"There was a problem trying to get a listing of torrents from " + folder));
				return;
			}

			for (int i = 0; i < currentFileList.length; i++) {

				if ( !running ){
					
					return;
				}
				
				File file = new File(folder, currentFileList[i]);

				// make sure we've got a valid torrent file before proceeding

				try {

					TOTorrent torrent = TorrentUtils.readFromFile(file, false);

					if (global_manager.getDownloadManager(torrent) != null) {

						if (Logger.isEnabled())
							Logger.log(new LogEvent(LOGID, file.getAbsolutePath()
									+ " is already being downloaded"));

						// we can't touch the torrent file as it is (probably) 
						// being used for the download

					} else {
						
						byte[] hash = null;
						try {
							hash = torrent.getHash();
						} catch (Exception e) { }

						if (!save_torrents) {

							File imported = new File(folder, file.getName() + ".imported");

							TorrentUtils.move(file, imported);

							global_manager.addDownloadManager(imported.getAbsolutePath(), hash,
									data_save_path, start_state, true);

						} else {

							global_manager.addDownloadManager(file.getAbsolutePath(), hash,
									data_save_path, start_state, true);

							// add torrent for deletion, since there will be a 
							// saved copy elsewhere
							to_delete.add(torrent);
						}

						if (Logger.isEnabled())
							Logger.log(new LogEvent(LOGID, "Auto-imported "
									+ file.getAbsolutePath()));
					}

				} catch (Throwable e) {

					Debug.out("Failed to auto-import torrent file '"
							+ file.getAbsolutePath() + "' - "
							+ Debug.getNestedExceptionMessage(e));
					Debug.printStackTrace(e);
				}
			}
		} finally {
			this_mon.exit();
		}
	}

}