package basics.application;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Properties;
import java.util.Set;
import basics.testing.Test;
import basics.utl.SUtl;
import basics.utl.SysUtl;

/**
 * Parser fuer die Kommandozeile. Erkennt: Argumente, Optionen (-x), Switches (/x). Example: Args
 * arg0 arg1 -u user arg2 -pwd "secret pwd" /s /y arg3 Give: Args: arg0, arg1, arg2, arg3 Options:
 * u=user, pwd=secret pwd Switches: s, y
 * @author kst, 3/05
 */
public class Args
{
public static final String SPACESUBST      = "{SP}";
public static final String QUOTESUBST      = "{Q}";
public static final String EQSUBST         = "{E}";
public boolean             NOENV           = true;
public ArrayList<String>   arguments;
public ArrayList<String>   switches;
public Properties          options;
int                        argumentCounter = 0;
Properties                 environment     = null;

public Args(String args[])
{
   try
   {
      environment = SysUtl.getProperties();
   }
   catch(Throwable e)
   {
      environment = new Properties();
   }
   arguments = new ArrayList<String>();
   switches = new ArrayList<String>();
   options = new Properties();
   parse(args, 0, "");
}

private String replace(String input)
{
   input = SUtl.replaceAll(input, SPACESUBST, " ");
   input = SUtl.replaceAll(input, QUOTESUBST, "\"");
   input = SUtl.replaceAll(input, EQSUBST, "=");
   return input;
}

public void addOption(String key, String value)
{
   key = replace(key);
   if(value.length() == 0 || value.indexOf("%") > -1)
      value = getEnvironmentValue(key, value);
   value = replace(value);
   options.setProperty(key, value);
}

private String getEnvironmentValue(String key, String old)
{
   if(NOENV)
      return old;
   String v1 = environment.getProperty(key);
   if(v1 != null)
   {
      return v1;
   }
   return old;
}

/**
 * Connandozeile parsen. 3 Eingabetypen moeglich: argument0 argument1 ... -pwd password -U username
 * ... /S /Y ...
 */
private void parse(String argv[], int pos, String cmd)
{
   if(pos < argv.length)
   {
      // lesen des zu einer option gehoerenden parameters
      if(cmd.length() > 0)
      {
         addOption(cmd, argv[pos]);
         parse(argv, pos + 1, "");
      }
      else
         if(argv[pos].length() > 0)
         {
            // pruefen, ob weder option noch switch, dann ist es ein argument:
            if(argv[pos].charAt(0) != '-' && argv[pos].charAt(0) != '/')
            {
               arguments.add(replace(argv[pos]));
               parse(argv, pos + 1, "");
            }
            // c ist jetzt - oder /, argv[pos] enthaelt option oder switch:
            // argv[pos] = argv[pos].substring (1);
            // ist es eine option?
            else
               if(argv[pos].charAt(0) == '-')
               {
                  // ja, dann verarbeite sie:
                  parse(argv, pos + 1, argv[pos].substring(1));
               }
               // es ist ein switch
               else
                  if(argv[pos].charAt(0) == '/')
                  {
                     // ja, dann verarbeite sie:
                     switches.add(replace(argv[pos].substring(1)));
                     parse(argv, pos + 1, "");
                  }
         }
         else
         {
            // System.out.println ("Empty arg at position " + pos );;
         }
   }
   else
   {
      if(cmd.length() != 0)
         System.out.println("Missing parameter for option " + cmd);
   }
}

public String getArgumentAsString()
{
   StringBuilder sb = new StringBuilder();
   Iterator<String> I = arguments.iterator();
   while(I.hasNext())
   {
      sb.append((String)I.next());
      sb.append(" ");
   }
   return sb.toString().trim();
}

public Iterator<String> getArguments()
{
   return arguments.iterator();
}

public int countArguments()
{
   return arguments.size();
}

public String getArgument(int i)
{
   return i < arguments.size() ? (String)arguments.get(i) : "";
}

public String getFirstArgument(String dv)
{
   argumentCounter = 0;
   if(argumentCounter >= arguments.size())
      return dv;
   String v = ((String)arguments.get(argumentCounter)).trim();
   if(v.length() == 0)
      return dv;
   return v;
}

public String getNextArgument(String dv)
{
   argumentCounter++;
   if(argumentCounter >= arguments.size())
      return dv;
   String v = ((String)arguments.get(argumentCounter)).trim();
   if(v.length() == 0)
      return dv;
   return v;
}

public boolean getSwitch(String k, boolean dv)
{
   boolean s = switches.contains(k);
   return s || dv;
}

public void setSwitch(String k)
{
   if(!switches.contains(k))
      switches.add(k);
}

public Iterator<String> getSwitches()
{
   return switches.iterator();
}

@SuppressWarnings("unchecked")
public Iterator getOptions()
{
   return options.keySet().iterator();
}

public String getOption(String k, String dv)
{
   boolean s = options.containsKey(k);
   return s ? (String)options.getProperty(k) : dv;
}

@SuppressWarnings("unchecked")
public static void main(String [] args) throws IOException
{
   Iterator<String> I = null;
   Args cl = new Args(args);
   cl.addOption("x", "yyy");
   System.out.println("Arguments:");
   I = cl.getArguments();
   while(I.hasNext())
   {
      System.out.println(" " + (String)I.next());
   }
   System.out.println("Options:");
   I = cl.getOptions();
   while(I.hasNext())
   {
      String k = (String)I.next();
      System.out.println(" " + k + "=" + cl.getOption(k, "??"));
   }
   System.out.println("Switches:");
   I = cl.getSwitches();
   while(I.hasNext())
   {
      System.out.println(" " + (String)I.next());
   }
}// end main

@SuppressWarnings("unchecked")
public String toString()
{
   StringBuilder s = new StringBuilder();
   Iterator<String> I = getArguments();
   s.append("Args{");
   while(I.hasNext())
   {
      s.append((String)I.next());
      s.append(";");
   }
   s.append("}Options{");
   if(options != null)
   {
      Set keys = options.keySet();
      Iterator iter = keys.iterator();
      while(iter.hasNext())
      {
         String k = (String)iter.next();
         Object o = options.get(k);
         String v = o == null ? "null" : o.toString();
         s.append(k);
         s.append("=");
         s.append(v);
         s.append(";");
      }
   }
   s.append("}Switches{");
   I = getSwitches();
   while(I.hasNext())
   {
      s.append((String)I.next());
      s.append(";");
   }
   s.append("}");
   return s.toString();
}

public static void unittest()
{
   Args cl = new Args(new String [] { "arg0", "-name", "ks", "/login" });
   Test.assertTrue(cl.getSwitch("login", false) == true, "swith login expected");
   Test.assertEqualsTrue(cl.getOption("name", ""), "ks", "option name expected should be ks");
   Test.assertEqualsTrue(cl.getFirstArgument(""), "arg0", "option name expected should be ks");
}
}
