/*
 * Created on 8 juil. 2003
 * 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 org.gudy.azureus2.ui.swt;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.logging.*;
import org.gudy.azureus2.core3.util.*;

import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreException;
import com.aelitis.azureus.core.AzureusCoreFactory;
import com.aelitis.azureus.launcher.Launcher;

/**
 * @author Olivier
 * 
 */
public class 
Main 
{  
	private static final LogIDs LOGID = LogIDs.GUI;
  public static final String	PR_MULTI_INSTANCE	= "MULTI_INSTANCE";	// values "true" or "false"
	
  StartServer startServer;
  
	public static long startTime = System.currentTimeMillis();

  // This method is called by other Main classes via reflection - must be kept public.
  public Main(String args[])
	{
  	try{
  		// this should not be necessary, but since it's public let's play safe
		if(Launcher.checkAndLaunch(Main.class, args))
			return;
  		
  			// This *has* to be done first as it sets system properties that are read and cached by Java
  		
  		COConfigurationManager.preInitialise();

			Constructor constructor = null;
			try {
				Class az3Class = Class.forName("com.aelitis.azureus.ui.swt.Initializer");

				constructor = az3Class.getConstructor(new Class[] {
						AzureusCore.class,
						StartServer.class,
						String[].class
				});
			} catch (ClassNotFoundException cnfe) {
				System.err.println(cnfe.toString() + "\nDid you include the azureus3 module?");
				return;
			} catch (Throwable t) {

				t.printStackTrace();

				return;
			}

  		
	  	String	mi_str = System.getProperty( PR_MULTI_INSTANCE );
	  	
	  	boolean mi = mi_str != null && mi_str.equalsIgnoreCase("true");
	  	
	    startServer = new StartServer();

		    
	
	    boolean debugGUI = Boolean.getBoolean("debug");
	    
	    if( mi || debugGUI){
	    	
	    	// create a MainWindow regardless to the server state
	    	
		  AzureusCore		core = AzureusCoreFactory.create();

  			constructor.newInstance(new Object[] {
  				core,
  				startServer,
  				args
  			});

	      return;
	    }
	    
	    
	    if ( processParams(args, startServer) ){
	
	    	AzureusCore		core = AzureusCoreFactory.create();
	    	
	    	startServer.pollForConnections(core);
	
  			constructor.newInstance(new Object[] {
  				core,
  				startServer,
  				args
  			});
	      
	    }
	    
  	}catch( AzureusCoreException e ){
  		
  		Logger.log(new LogEvent(LOGID, "Start failed", e));

  	}catch( Throwable t ){

			t.printStackTrace();
 
  	}
  }
  
  
  
  /**
	 * @param args
	 * @return whether to init the core
	 */
	public static boolean processParams(String[] args, StartServer startServer) {
    boolean	closedown	= false;
    
    boolean another_instance = startServer.getState() != StartServer.STATE_LISTENING;
    
    /*  if another instance is running then set the property which is checked during
     *	class instantiation by various stuff to to avoid pulling in too much state
     *	from the already running instance  
     */
    if(another_instance)
    	System.setProperty("transitory.startup", "1");
    
    	// WATCH OUT FOR LOGGING HERE - we don't want to use Logger if this is a secondary instance as
    	// it initialised TOO MUCH of AZ core
    
    for (int i=0;i<args.length;i++){

    	String	arg = args[i];
    	
    	if ( arg.equalsIgnoreCase( "--closedown" ) || arg.equalsIgnoreCase( "--shutdown" ) || arg.equalsIgnoreCase( "--restart" ) ){
    		
    		closedown	= true;
    		
    		break;
    		
    	}else if ( arg.equalsIgnoreCase( "--open" )){
    	
    		continue;
    	}
    	
	        // Sometimes Windows use filename in 8.3 form and cannot
	        // match .torrent extension. To solve this, canonical path
	        // is used to get back the long form
	    	
        String filename = arg;
          
        if ( filename.length() == 40 ){
	        
        	byte[]	hash = null;
        	
        	try{
        		hash = ByteFormatter.decodeString( filename );
        		
        	}catch( Throwable e ){
        	}
        	
        	if ( hash != null && hash.length == 20 ){
        		
        		filename = "magnet:?xt=urn:btih:" + Base32.encode( hash );
        	}
        }
        
        	// handle base32 info hash
       
        if ( filename.length() == 32 ){
            
        	byte[]	hash = null;
        	
        	try{
        		hash = Base32.decode( filename );
        		
        	}catch( Throwable e ){
        	}
        	
        	if ( hash != null && hash.length == 20 ){
        		
        		filename = "magnet:?xt=urn:btih:" +filename;
        	}
        }
        
        if( 	filename.toUpperCase().startsWith( "HTTP:" ) || 
        		filename.toUpperCase().startsWith( "HTTPS:" ) || 
        		filename.toUpperCase().startsWith( "MAGNET:" ) ||
        		filename.toUpperCase().startsWith( "MAGGOT:" ) ||
           		filename.toUpperCase().startsWith( "BC:" ) ||
           		filename.toUpperCase().startsWith( "BCTP:" ) ||
        		filename.toUpperCase().startsWith( "DHT:" ) ) {

        	if ( !another_instance ){

        		Logger.log(new LogEvent(LOGID, "Main::main: args[" + i
        				+ "] handling as a URI: " + filename));
        	}

        	continue;  //URIs cannot be checked as a .torrent file
        }            
        
        try{
        	File	file = new File(filename);
        	
        	if ( !file.exists()){
        		
        		throw( new Exception("File '" + file + "' not found" ));
        	}
        	
        	args[i] = file.getCanonicalPath();
          	
        		// don't use logger if we're not the main instance as we don't want all
        		// the associated core initialisation + debug file moving...
        	
        	if ( (!another_instance) && Logger.isEnabled()){
        		
        		Logger.log(new LogEvent(LOGID, "Main::main: args[" + i
        				+ "] exists = " + new File(filename).exists()));
        	}
        }catch( Throwable e ){
        	
        	if ( another_instance ){
        		
        		e.printStackTrace();
        		
        	}else{
        		
	        	Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_ERROR,
							"Failed to access torrent file '" + filename
									+ "'. Ensure sufficient temporary "
									+ "file space available (check browser cache usage)."));
        	}
        }
    }
    
    
     if( another_instance ) {  //looks like there's already a process listening on 127.0.0.1:<port>
    	//attempt to pass args to existing instance
    	 
    	 // First, do some OSX magic because parameters are passed via OpenDocument API and other callbacks
    	 args = CocoaMagic(args);
    	 
    	StartSocket ss = new StartSocket(args);
    	
    	if( !ss.sendArgs() ) {  //arg passing attempt failed, so start core anyway
    		another_instance = false;
    		String msg = "There appears to be another program process already listening on socket [127.0.0.1: "+Constants.INSTANCE_PORT+"].\nLoading of torrents via command line parameter will fail until this is fixed.";
    		System.out.println( msg );
    		Logger.log(new LogAlert(LogAlert.REPEATABLE, LogAlert.AT_WARNING, msg));
    	}
    }
    
    if ( !another_instance ){
    	
    	if ( closedown ){
    			// closedown request and no instance running
    		return false;
    	}
    	
    	return true;
    }
    return false;
	}


	private static String[] CocoaMagic(String[] args) {
		if (!Constants.isOSX) {
			return args;
		}
		try {

			// hack to tell OSXAccess static initializer to only initilize a light version
			System.setProperty("osxaccess.light", "1");

			Class<?> claOSXAccess = Class.forName("org.gudy.azureus2.platform.macosx.access.jnilib.OSXAccess");
			if (claOSXAccess != null) {
				Method method = claOSXAccess.getMethod("runLight", new Class[] {
					String[].class
				});
				Object invoke = method.invoke(null, new Object[] {
					args
				});
				return (String[]) invoke;
			}

		} catch (Throwable e) {

			Debug.printStackTrace(e);
		}
		return args;
	}



	public static void main(String args[]) 
  {
		if(Launcher.checkAndLaunch(Main.class, args))
			return;
  	//Debug.dumpThreads("Entry threads");
 
  	//Debug.dumpSystemProperties();
  	
		if (System.getProperty("ui.temp") == null) {
			System.setProperty("ui.temp", "az2");
		}

		new Main(args);
  }
}
