/*
 * UserInterfaceMain.java
 *
 * Created on 9. Oktober 2003, 19:50
 */

package org.gudy.azureus2.ui.common;

import java.io.FileReader;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.StringTokenizer;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.commons.cli.PosixParser;
import org.apache.log4j.Appender;
import org.apache.log4j.ConsoleAppender;
import org.apache.log4j.Logger;
import org.apache.log4j.PatternLayout;
import org.apache.log4j.varia.DenyAllFilter;

import com.aelitis.azureus.core.*;
import com.aelitis.azureus.core.impl.AzureusCoreSingleInstanceClient;
import com.aelitis.azureus.launcher.Launcher;

import org.gudy.azureus2.core3.config.COConfigurationManager;
import org.gudy.azureus2.core3.util.Constants;
import org.gudy.azureus2.ui.common.IUserInterface;
import org.gudy.azureus2.ui.common.UserInterfaceFactory;
/**
 *
 * @author  Tobias Minich
 */
public class Main {
  
  public static String DEFAULT_UI = "swt";
  
  public static StartServer start = null;
  
  protected static AzureusCore	core;
  
  private static CommandLine parseCommands(String[] args, boolean constart) {
    
    if (args==null)
      return null;
    
    CommandLineParser parser = new PosixParser();
    Options options = new Options();
    options.addOption("h", "help", false, "Show this help.");
 
    OptionBuilder.withLongOpt("exec");
    OptionBuilder.hasArg();
    OptionBuilder.withArgName("file");
    OptionBuilder.withDescription("Execute script file. The file should end with 'logout', otherwise the parser thread doesn't stop.");
    options.addOption( OptionBuilder.create('e'));
    
    OptionBuilder.withLongOpt("command");
    OptionBuilder.hasArg();
    OptionBuilder.withArgName("command");
    OptionBuilder.withDescription("Execute single script command. Try '-c help' for help on commands.");
    options.addOption(OptionBuilder.create('c'));
    
    OptionBuilder.withLongOpt("ui");
    OptionBuilder.withDescription("Run <uis>. ',' separated list of user interfaces to run. The first one given will respond to requests without determinable source UI (e.g. further torrents added via command line).");
    OptionBuilder.withArgName("uis");
    OptionBuilder.hasArg();
    options.addOption(OptionBuilder.create('u'));
    
    CommandLine commands = null;
    try {
      commands = parser.parse(options, args, true);
    } catch( ParseException exp ) {
      Logger.getLogger("azureus2").error("Parsing failed.  Reason: " + exp.getMessage(), exp);
      if (constart)
        System.exit(2);
    }
    if ( commands != null && commands.hasOption('h')) {
      if (constart) {
        HelpFormatter hf = new HelpFormatter();
        hf.printHelp("java org.gudy.azureus2.ui.common.Main", "Optionally you can put torrent files to add to the end of the command line.\r\n", options, "Available User Interfaces: swt (default), web, console\r\nThe default interface is not started if you give either the '-e' or '-c' option (But you can start it by hand with '-u').", true);
        System.exit(0);
      }
    }
    return commands;
  }
  
  public static void initRootLogger() {
    if (Logger.getRootLogger().getAppender("ConsoleAppender")==null) {
      Appender app;
      app = new ConsoleAppender(new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN));
      app.setName("ConsoleAppender");
      app.addFilter( new DenyAllFilter() );  //'log off' by default
      Logger.getRootLogger().addAppender(app);
    }
  }
  
  public static void main(String[] args) {
	  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();
 
    String  mi_str = System.getProperty( "MULTI_INSTANCE" );
    
    boolean mi = mi_str != null && mi_str.equalsIgnoreCase("true");

    initRootLogger();
    
    try{
       	CommandLine commands = parseCommands(args, true);

       	if ( commands != null && directLaunch( args, commands )){
       		
       		return;
       	}
       	 
       		// don't create core until we know we really need it
       	
    	if( mi ){
    		
    		System.out.println( "MULTI_INSTANCE enabled" );
    		
    	   	core = AzureusCoreFactory.create();

    		processArgs(args, core, commands);
    		
    		return;
    	}
      
    	start = new StartServer();
      
	    if ((start == null) || (start.getServerState()==StartServer.STATE_FAULTY)) {
	    	
	 
	    	new StartSocket( args );
	    	
	    }else{
	    	
	   
	      core = AzureusCoreFactory.create();


	      start.start();
	      
	      processArgs(args, core, commands);
	    }
    }catch( AzureusCoreException e ){
    	
    	System.out.println( "Start fails:" );
    	
    	e.printStackTrace();
    }
  }
  
  public static void shutdown() {
    if (start!=null){
    	
      start.stopIt();
    }
    
    if ( core != null ){
    	try{
    		core.stop();
    		
    	}catch( AzureusCoreException e ){
    		
    		System.out.println( "Stop fails:" );
    		
    		e.printStackTrace();
    	}
    }
    
    SimpleDateFormat temp = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    Logger.getLogger("azureus2").fatal("Azureus stopped at "+temp.format(new Date()));
    //System.exit(0);	- we don't want to force quit, wait until other threads have completed
    // so that resume data etc is saved....
  }
  
  	public static boolean 
  	directLaunch(
  		String[] 		args, 
  		CommandLine 	commands) 
  	{
  			// frig to support launch of SWT ui via this means pending a proper rewrite
  			// of this stuff
  		
  		if ( commands.hasOption('u')) {
    	  
  			String uinames = commands.getOptionValue('u');
       
  			if ( uinames.indexOf(',') != -1 ){
  				
  				return( false );
  			}
  			
  			if ( !uinames.equalsIgnoreCase( DEFAULT_UI )){
  				
  				return( false );
  			}
  		}
           
  		try{
  			String uiclass = "org.gudy.azureus2.ui." + DEFAULT_UI + ".Main";
  	   
  			Class	main_class = Class.forName( uiclass );
  		
  			Method main_method = main_class.getMethod( "main", new Class[]{ String[].class });
  			
   			main_method.invoke( null, new Object[]{ commands.getArgs()});
  			
  			return( true );
  			
  		}catch( Throwable e ){
  			
  			e.printStackTrace();
  			
  			return( false );
  		}
  }
  
  public static void 
  processArgs(
  	String[] 		args, 
  	AzureusCore 	new_core, 
	CommandLine 	commands) 
  {
    if (commands==null) {
      commands = parseCommands(args, false);
    }
    if (commands!=null && ( args.length > 0 || new_core != null)) {
      if (UIConst.UIS == null) {
        UIConst.UIS = new HashMap();
      }
      if (commands.hasOption('u')) {
        String uinames = commands.getOptionValue('u');
        if (uinames.indexOf(',')==-1) {
          if (!UIConst.UIS.containsKey(uinames))
          UIConst.UIS.put(uinames,UserInterfaceFactory.getUI(uinames));
        } else {
          StringTokenizer stok = new StringTokenizer(uinames, ",");
          while (stok.hasMoreTokens()) {
            String uin = stok.nextToken();
            if (!UIConst.UIS.containsKey(uin))
              UIConst.UIS.put(uin,UserInterfaceFactory.getUI(uin));
          }
        }
      } else {
        if (UIConst.UIS.isEmpty() && !commands.hasOption('c') && !commands.hasOption('e'))
          UIConst.UIS.put(DEFAULT_UI, UserInterfaceFactory.getUI(DEFAULT_UI));
      }

      Iterator uis = UIConst.UIS.values().iterator();
      boolean isFirst = true;
      String [] theRest = commands.getArgs();
      while (uis.hasNext()) {
        IUserInterface ui = (IUserInterface) uis.next();
        ui.init(isFirst, (UIConst.UIS.size()>1));
        theRest = ui.processArgs(theRest);
        isFirst = false;
      }

      if ( new_core != null ){
      	
        SimpleDateFormat temp = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
        
        UIConst.startTime = new Date();
        
        Logger.getLogger("azureus2").fatal("Azureus started at "+temp.format(UIConst.startTime));
        
        UIConst.setAzureusCore( new_core );
      }

      uis = UIConst.UIS.values().iterator();
      while (uis.hasNext())
        ((IUserInterface) uis.next()).startUI();
           
      Class clConsoleInput;
      Constructor conConsoleInput =null;
      try {
      	clConsoleInput = Class.forName("org.gudy.azureus2.ui.console.ConsoleInput");
      	
      		// change this and you'll need to change the parameters below....
      	
      	Class params[] = {String.class, AzureusCore.class, Reader.class, PrintStream.class, Boolean.class};
      	
      	conConsoleInput=clConsoleInput.getConstructor(params);
      } catch (Exception e) {
      	e.printStackTrace();
      }
      if (commands.hasOption('e')) {
      	if (conConsoleInput != null) {
	        try {
	        	Object params[] = {commands.getOptionValue('e'), new_core, new FileReader(commands.getOptionValue('e')), System.out, Boolean.FALSE};
	        	conConsoleInput.newInstance(params);
	        } catch (java.io.FileNotFoundException e) {
	          Logger.getLogger("azureus2").error("Script file not found: "+e.toString());
	        } catch (Exception e) {
	        	Logger.getLogger("azureus2").error("Error invocating the script processor: "+e.toString());
	        }
      	} else
      		Logger.getLogger("azureus2").error("ConsoleInput class not found. You need the console ui package to use '-e'");
      }
      
      if (commands.hasOption('c')) {
      	if (conConsoleInput != null) {
	        String comm = commands.getOptionValue('c');
	        comm+="\nlogout\n";
	        Object params[] = {commands.getOptionValue('c'), UIConst.getAzureusCore(), new StringReader(comm), System.out, Boolean.FALSE};
	        try {
	        	conConsoleInput.newInstance(params);
	        } catch (Exception e) {
	        	Logger.getLogger("azureus2").error("Error invocating the script processor: "+e.toString());
	        }
      	} else
      		Logger.getLogger("azureus2").error("ConsoleInput class not found. You need the console ui package to use '-e'");
      }
      
      openTorrents(theRest);
    } else {
      Logger.getLogger("azureus2").error("No commands to process");
    }
  }
  
  public static void openTorrents(String[] torrents) {
    if ((UIConst.UIS!=null) && (!UIConst.UIS.isEmpty()) && (torrents.length>0)) {
      for(int l=0; l<torrents.length; l++) {
        ((IUserInterface) UIConst.UIS.values().toArray()[0]).openTorrent(torrents[l]);
      }
    }
  }
  
  public static class StartSocket {
    public StartSocket(String args[]) {
      Socket sck = null;
      PrintWriter pw = null;
      try {
        System.out.println("StartSocket: passing startup args to already-running process.");
        
		// NOTE - this formatting is also used by AzureusCoreSingleInstanceClient and other org.gudy.azureus2.ui.swt.StartSocket
        
        sck = new Socket("127.0.0.1", Constants.INSTANCE_PORT );
        pw = new PrintWriter(new OutputStreamWriter(sck.getOutputStream()));
        StringBuffer buffer = new StringBuffer(AzureusCoreSingleInstanceClient.ACCESS_STRING+";args;");
        for(int i = 0 ; i < args.length ; i++) {
          String arg = args[i].replaceAll("&","&&").replaceAll(";","&;");
          buffer.append(arg);
          buffer.append(';');
        }
        pw.println(buffer.toString());
        pw.flush();
      } catch(Exception e) {
        e.printStackTrace();
      } finally {
        try {
          if (pw != null)
            pw.close();
        } catch (Exception e) {
        }
        try {
          if (sck != null)
            sck.close();
        } catch (Exception e) {
        }
      }
    }
  }
}
