/*
 * File    : TorrentImpl.java
 * Created : 08-Dec-2003
 * By      : parg
 * 
 * Azureus - a Java Bittorrent client
 *
 * 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 ( see the LICENSE file ).
 *
 * 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 org.gudy.azureus2.pluginsimpl.local.torrent;

import java.net.*;

/**
 * @author parg
 *
 */

import java.util.Map;
import java.io.File;

import org.gudy.azureus2.core3.download.DownloadManagerState;
import org.gudy.azureus2.core3.download.DownloadManagerStateFactory;
import org.gudy.azureus2.core3.internat.*;
import org.gudy.azureus2.core3.logging.LogRelation;
import org.gudy.azureus2.core3.torrent.*;
import org.gudy.azureus2.core3.util.*;
import org.gudy.azureus2.plugins.PluginInterface;
import org.gudy.azureus2.plugins.torrent.*;
import org.gudy.azureus2.plugins.download.*;
import org.gudy.azureus2.pluginsimpl.local.download.*;
import org.gudy.azureus2.pluginsimpl.local.utils.UtilitiesImpl;

import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.plugins.magnet.MagnetPlugin;

public class 
TorrentImpl
	extends LogRelation
	implements Torrent
{
	private static MagnetPlugin		magnet_plugin;
	
	private PluginInterface			pi;
	private TOTorrent				torrent;
	private LocaleUtilDecoder		decoder;
	
	private boolean					complete;
	
	public
	TorrentImpl(
		TOTorrent		_torrent )
	{
		this( null, _torrent );
	}
	
	public
	TorrentImpl(
		PluginInterface	_pi,
		TOTorrent		_torrent )
	{
		pi		= _pi;
		torrent	= _torrent;
	}
	
	public String
	getName()
	{
		String utf8Name = torrent.getUTF8Name();
		String	name = utf8Name == null ? decode( torrent.getName()) : utf8Name;
		
		name = FileUtil.convertOSSpecificChars( name, false );

		return( name );
	}
	
	public URL
	getAnnounceURL()
	{
		return( torrent.getAnnounceURL());
	}
	
	public void
	setAnnounceURL(
		URL		url )
	{
		torrent.setAnnounceURL( url );
		
		updated();
	}

	public TorrentAnnounceURLList
	getAnnounceURLList()
	{
		return( new TorrentAnnounceURLListImpl( this ));
	}

	public TOTorrent
	getTorrent()
	{
		return( torrent );
	}
	
	public boolean
	isDecentralised()
	{
		return( TorrentUtils.isDecentralised( torrent ));
	}
	public boolean
	isDecentralisedBackupEnabled()
	{
		return( TorrentUtils.getDHTBackupEnabled( torrent ));
	}
	
	public void
	setDecentralisedBackupRequested(
		boolean	requested )
	{
		TorrentUtils.setDHTBackupRequested( torrent, requested );
	}
	
	public boolean
	isDecentralisedBackupRequested()
	{
		return( TorrentUtils.isDHTBackupRequested( torrent ));

	}
	public boolean
	isPrivate()
	{
		return( TorrentUtils.getPrivate( torrent ));
	}
	
	public void
	setPrivate(
		boolean	priv )
	{
		TorrentUtils.setPrivate( torrent, priv );
	}
	
	public boolean
	wasCreatedByUs()
	{
		return( TorrentUtils.isCreatedTorrent( torrent ));
	}
	
	public URL
	getMagnetURI()
	
		throws TorrentException
	{
		if ( magnet_plugin == null ){
			
			PluginInterface magnet_pi = AzureusCoreFactory.getSingleton().getPluginManager().getPluginInterfaceByClass( MagnetPlugin.class );
			
			if ( magnet_pi != null ){
			
				 magnet_plugin = (MagnetPlugin)magnet_pi.getPlugin();
			}
		}
		
		if ( magnet_plugin == null ){
			
			throw( new TorrentException( "MegnetPlugin unavailable" ));
		}
		
		try{
			URL	res = magnet_plugin.getMagnetURL(  torrent.getHash());
			
			return( res );
				
		}catch( TOTorrentException e ){
			
			throw( new TorrentException(e ));
		}
	}
	
	public byte[]
	getHash()
	{
		try{
			return( torrent.getHash());
			
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace( e );
			
			return( null );
		}
	}
		
	public long
	getSize()
	{
		return( torrent.getSize());
	}
	
	public String
	getComment()
	{
		return( decode(torrent.getComment()));
	}
	
	public void
	setComment(
		String	comment )
	{
		torrent.setComment( comment );
	}
	public long
	getCreationDate()
	{
		return( torrent.getCreationDate());
	}
	
	public String
	getCreatedBy()
	{
		return( decode( torrent.getCreatedBy()));
	}
	

	public long
	getPieceSize()
	{
		return( torrent.getPieceLength());
	}
	
	public long
	getPieceCount()
	{
		return( torrent.getNumberOfPieces());
	}
	
	public byte[][]
  	getPieces()
  	{
		try{
			return( torrent.getPieces());
			
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace( e );
			
			return( new byte[0][0] );
		}
  	}	
	          	
	public TorrentFile[]
	getFiles()
	{
		TOTorrentFile[]	files = torrent.getFiles();
		
		TorrentFile[]	res = new TorrentFile[files.length];
		
		for (int i=0;i<res.length;i++){
		
			TOTorrentFile	tf = files[i];
			
			byte[][]	comps = tf.getPathComponents();
			
			String	name = "";
			
			for (int j=0;j<comps.length;j++){
				
				String	comp = decode(comps[j]);
			
				comp = FileUtil.convertOSSpecificChars( comp, j != comps.length-1 );
				
				name += (j==0?"":File.separator)+comp;
			}
			
			res[i] = new TorrentFileImpl(name, tf.getLength());
		}
		
		return( res );
	}
	
	protected void
	getDecoder()
	{
			// We defer the getting of the decoder until it is required as this Torrent may have been 
			// created in order to simply remove additional properties from it before serialising it
			// Indeed, this was happening and unfortunately resulting in 1) the encoding being
			// serialised 2) the user being prompted for an encoding choice 
		
		try{
			decoder = LocaleTorrentUtil.getTorrentEncoding( torrent );
			
		}catch( Throwable e ){
			
		}
	}
	
	public String
	getEncoding()
	{
		getDecoder();
		
		if ( decoder != null ){
			
			return( decoder.getName());
		}
		
		return( Constants.DEFAULT_ENCODING );
	}
	
	public void
	setEncoding(String encoding)throws TorrentEncodingException {		
		try {
			LocaleTorrentUtil.setTorrentEncoding(torrent, encoding);
		} catch(LocaleUtilEncodingException e) {
			throw new TorrentEncodingException("Failed to set the encoding",e);
		}
	}
	
	public void
	setDefaultEncoding() throws TorrentEncodingException {
		setEncoding(Constants.DEFAULT_ENCODING);
	}
	
	protected String
	decode(
		byte[]		data )
	{
		getDecoder();
		
		if ( data != null ){
			
			if ( decoder != null ){
				
				try{
					return( decoder.decodeString(data));
					
				}catch( Throwable e ){
				}
			}
			
			return( new String(data));
		}
		
		return( "" );
	}
	
	public Object
	getAdditionalProperty(
		String		name )
	{
		return( torrent.getAdditionalProperty( name ));
	}
	
	public Torrent
	removeAdditionalProperties()
	{
		try{
			TOTorrent	t = TOTorrentFactory.deserialiseFromMap(torrent.serialiseToMap());
					
			t.removeAdditionalProperties();
	
			return( new TorrentImpl( t ));
			
		}catch( TOTorrentException e ){
			
			Debug.printStackTrace(e);
			
			return( this );
		}
	}

	public void
	setPluginStringProperty(
		String		name,
		String		value )
	{
		PluginInterface	p = pi;
		
		if ( p == null ){
		
			p = UtilitiesImpl.getPluginThreadContext();	
		}
		
		if ( p == null ){
			
			name = "<internal>." + name;
			
		}else{
			
			name = p.getPluginID() + "." + name;
		}
		
		TorrentUtils.setPluginStringProperty( torrent, name, value );
	}
	
	public String
	getPluginStringProperty(
		String		name )
	{
		PluginInterface	p = pi;
		
		if ( p == null ){
		
			p = UtilitiesImpl.getPluginThreadContext();	
		}
		
		if ( p == null ){
			
			name = "<internal>." + name;
			
		}else{
			
			name = p.getPluginID() + "." + name;
		}
		
		return( TorrentUtils.getPluginStringProperty( torrent, name ));
	}
	
	public void
	setMapProperty(
		String		name,
		Map			value )
	{
		TorrentUtils.setPluginMapProperty( torrent, name, value );
	}
	
	public Map
	getMapProperty(
		String		name )
	{
		return( TorrentUtils.getPluginMapProperty( torrent, name ));
	}
	
	public Map
	writeToMap()
	
		throws TorrentException
	{
		try{
			return( torrent.serialiseToMap());
			
		}catch( TOTorrentException e ){
			
			throw( new TorrentException( "Torrent::writeToMap: fails", e ));
		}
	}
	
	public byte[]
	writeToBEncodedData()
	
		throws TorrentException
	{
		try{
			Map	map = torrent.serialiseToMap();
			
			return( BEncoder.encode( map ));
			
		}catch( Throwable e ){
			
			throw( new TorrentException( "Torrent::writeToBEncodedData: fails", e ));
		}
	}	
	
	public void
	writeToFile(
		File		file )
	
		throws TorrentException
	{
		try{
				// don't use TorrentUtils.writeToFile as this updates the internal torrent
				// file reference an means that the torrent get's auto-written to the new
				// location in future, most likley NOT the desired behaviour
			
			torrent.serialiseToBEncodedFile( file );
			
		}catch( TOTorrentException e ){
			
			throw( new TorrentException( "Torrent::writeToFile: fails", e ));
		}
	}
  
 	protected void
	updated()
	{
		try{
			DownloadImpl dm = (DownloadImpl)DownloadManagerImpl.getDownloadStatic( torrent );
		
			if ( dm != null ){
			
				dm.torrentChanged();
			}
		}catch( DownloadException e ){
			
			// torrent may not be running
		}
	}
	
	public void
	save()
		throws TorrentException
	{
		try{
			TorrentUtils.writeToFile( torrent );
			
		}catch( TOTorrentException e ){
			
			throw( new TorrentException( "Torrent::save Fails", e ));
		}	
	}
	
	public void
	setComplete(
		File		data_dir )
	
		throws TorrentException
	{		
		try{
			LocaleTorrentUtil.setDefaultTorrentEncoding( torrent );
		
			DownloadManagerState	download_manager_state = 
				DownloadManagerStateFactory.getDownloadState( torrent ); 

			TorrentUtils.setResumeDataCompletelyValid( download_manager_state );

			download_manager_state.save();
			
			complete	= true;
			
		}catch( Throwable e ){
			
			throw( new TorrentException("encoding selection fails", e ));
		}
	}
	
	public boolean
	isComplete()
	{
			// TODO: could check the download state too I guess...
		
		return( complete );
	}
	
	
  // Pass LogRelation off to core objects

	/* (non-Javadoc)
	 * @see org.gudy.azureus2.core3.logging.LogRelation#getLogRelationText()
	 */
	public String getRelationText() {
		return propogatedRelationText(torrent);
	}

	/* (non-Javadoc)
	 * @see org.gudy.azureus2.core3.logging.LogRelation#getQueryableInterfaces()
	 */
	public Object[] getQueryableInterfaces() {
		return new Object[] { torrent };
	}
	
	public boolean isSimpleTorrent() {return torrent.isSimpleTorrent();}
}