/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.purl.sword.client;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

/**
 * List of options that are parsed from the command line. 
 * @author Neil Taylor
 */
public class ClientOptions
{
   /**
    * Label for the service operation. 
    */
   public static final String TYPE_SERVICE = "service";

   /**
    * Label for the post operation. 
    */
   public static final String TYPE_POST = "post";

   /**
    * Label for the multipost operation. 
    */
   public static final String TYPE_MULTI_POST = "multipost";

   /**
    * The access type. 
    */
   private String accessType = null; 

   /** 
    * Proxy host name. 
    */
   private String proxyHost = null;

   /**
    * Proxy host port. 
    */
   private int proxyPort = 8080; 

   /** 
    * Username to access the service/post server. 
    */
   private String username = null;

   /**
    * Password to access the service/post server.
    */
   private String password = null; 

   /**
    * HREF of the server to access. 
    */
   private String href = null;

   /** 
    * Filename to post. 
    */
   private String filename = null; 

   /**
    * Filetype. 
    */
   private String filetype = null;

   /**
    * Specifies that the output streams are not to be captured by the GUI client. 
    */
   private boolean noCapture = false; 
   
   
   /**
    * SLUG Header field.
    */
   private String slug = null; 

   /**
    * NoOp, used to indicate an operation on the server that does not 
    * require the file to be stored. 
    */
   private boolean noOp = false; 

   /**
    * Request verbose output from the server. 
    */
   private boolean verbose = false; 

   /** 
    * OnBehalfOf user id. 
    */
   private String onBehalfOf = null; 

   /**
    * Format namespace to be used for the posted file. 
    */
   private String formatNamespace = null; 

   /**
    * Introduce a checksum error. This is used to simualte an error with the 
    * MD5 value. 
    */
   private boolean checksumError = false;

   /**
    * Logger. 
    */
   private static Logger log = Logger.getLogger(ClientOptions.class);

   /**
    * List of multiple destination items. Used if the mode is set to multipost. 
    */
   private List<PostDestination> multiPost = new ArrayList<PostDestination>();

   /**
    * Pattern string to extract the data from a destination parameter in multipost mode. 
    */
   private static final Pattern multiPattern = Pattern.compile("(.*?)(\\[(.*?)\\]){0,1}(:(.*)){0,1}@(http://.*)");

   /**
    * Flag that indicates if the gui mode has been set. This is 
    * true by default. 
    */
   private boolean guiMode = true; 

   /**
    * Flat that indicates if the MD5 option has been selected. This
    * is true by default. 
    */
   private boolean md5 = false; 

   /**
    * Parse the list of options contained in the specified array. 
    * 
    * @param args The array of options. 
    * 
    * @return True if the options were parsed successfully. 
    */
   public boolean parseOptions( String[] args )
   {
      try
      {
         // iterate over the args
         for( int i = 0; i < args.length; i++ )
         {

            if( "-md5".equals(args[i]))
            {
               md5 = true;
            }

            if( "-noOp".equals(args[i]))
            {
               noOp = true;
            }

            if( "-verbose".equals(args[i]))
            {
               verbose = true; 
            }

            if( "-cmd".equals(args[i]) )
            {
               guiMode = false;  
            }

            if( "-gui".equals(args[i]) )
            {
               guiMode = true;  
            }

            if( "-host".equals(args[i]) )
            {
               i++;
               proxyHost = args[i];
            }

            if( "-port".equals(args[i]) )
            {
               i++;
               proxyPort = Integer.parseInt(args[i]);
            }

            if( "-u".equals(args[i]) )
            {
               i++;
               username = args[i];
            }

            if( "-p".equals(args[i]))
            {
               i++;
               password = args[i];
            }

            if( "-href".equals(args[i]))
            {
               i++;
               href = args[i];
            }

            if( "-help".equals(args[i]) )
            {
               // force the calling code to display the usage information
               return false;     
            }

            if( "-t".equals(args[i]))
            {
               i++;
               accessType = args[i];
            }

            if( "-file".equals(args[i]))
            {
               i++; 
               filename = args[i];
            }

            if( "-filetype".equals(args[i]))
            {
               i++;
               filetype = args[i];
            }

            if( "-slug".equals(args[i]))
            {
               i++;
               slug = args[i]; 
            }

            if( "-onBehalfOf".equals(args[i]))
            {
               i++;
               onBehalfOf = args[i];
            }

            if( "-formatNamespace".equals(args[i]))
            {
               i++;
               formatNamespace = args[i];
            }

            if( "-checksumError".equals(args[i]))
            {
               i++;
               checksumError = true;
            }

            if( "-dest".equals(args[i]))
            {
               i++;
               Matcher m = multiPattern.matcher(args[i]);
               if( ! m.matches() ) 
               {
                  log.debug("Error with dest parameter. Ignoring value: " + args[i]);
               }
               else
               {
                  int numGroups = m.groupCount();
                  for( int g = 0; g <= numGroups; g++ )
                  {
                     log.debug("Group (" + g + ") is: " + m.group(g));
                  }

                  String username = m.group(1);
                  String onBehalfOf = m.group(3);
                  String password = m.group(5);
                  String url = m.group(6);
                  PostDestination destination = new PostDestination(url, username, password, onBehalfOf);

                  multiPost.add(destination);
               }
            }

            if( "-nocapture".equals(args[i]) )
            {
               i++;
               noCapture = true;
            }

         }

         // apply any settings 
         if( href == null && "service".equals(accessType) )
         {
            log.error( "No href specified.");
            return false; 
         }

         if( multiPost.size() == 0 && "multipost".equals(accessType))
         {
            log.error("No destinations specified");
            return false;
         }

         if( accessType == null && ! guiMode ) 
         {
            log.error("No access type specified");
            return false;
         }

         if( ( username == null && password != null ) || (username != null && password == null))
         {
            log.error("The username and/or password are not specified. If one is specified, the other must also be specified.");
            return false; 
         }
      }
      catch( ArrayIndexOutOfBoundsException ex )
      {
         log.error("Error with parameters.");
         return false;
      }

      return true;
   }

   /** 
    * Get the access type. 
    * @return The value, or <code>null</code> if the value is not set. 
    */	
   public String getAccessType()
   {
      return accessType;
   }

   /** 
    * Set the access type. 
    * @param accessType The value, or <code>null</code> to clear the value. 
    */
   public void setAccessType(String accessType)
   {
      this.accessType = accessType;
   }

   /** 
    * Get the proxy host. 
    * @return The value, or <code>null</code> if the value is not set.  
    */
   public String getProxyHost()
   {
      return proxyHost;
   }

   /**
    * Set the proxy host.
    * @param proxyHost The value, or <code>null</code> to clear the value.  
    */
   public void setProxyHost(String proxyHost)
   {
      this.proxyHost = proxyHost;
   }

   /**
    * Get the proxy port. 
    * @return The proxy port. Default value is 80.
    */
   public int getProxyPort()
   {
      return proxyPort;
   }

   /**
    * Set the proxy port. 
    * @param proxyPort The proxy port. 
    */
   public void setProxyPort(int proxyPort)
   {
      this.proxyPort = proxyPort;
   }

   /** 
    * Get the username. 
    * @return The value, or <code>null</code> if the value is not set. 
    */
   public String getUsername()
   {
      return username;
   }

   /**
    * Set the username. 
    * @param username The value, or <code>null</code> to clear the value. 
    */
   public void setUsername(String username)
   {
      this.username = username;
   }

   /**
    * Get the password. 
    * @return The value, or <code>null</code> if the value is not set. 
    */
   public String getPassword()
   {
      return password;
   }

   /**
    * Set the password. 
    * @param password The value, or <code>null</code> to clear the value. 
    */
   public void setPassword(String password)
   {
      this.password = password;
   }

   /**
    * Get the HREF of the service to access. 
    * @return The value, or <code>null</code> if the value is not set. 
    */
   public String getHref()
   {
      return href;
   }

   /**
    * Set the HREF of the service to access. 
    * @param href The value, or <code>null</code> to clear the value. 
    */
   public void setHref(String href)
   {
      this.href = href;
   }

   /**
    * Get the name of the file to post. 
    * @return The value, or <code>null</code> if the value is not set. 
    */
   public String getFilename()
   {
      return filename;
   }

   /** 
    * Set the name of the file to post. 
    * @param filename The value, or <code>null</code> to clear the value. 
    */
   public void setFilename(String filename)
   {
      this.filename = filename;
   }

   /** 
    * Get the type of the file to post. 
    * @return The filetype, or <code>null</code> if the value is not set. 
    */
   public String getFiletype()
   {
      return filetype;
   }

   /**
    * Set the type of the file to post. 
    * @param filetype The value, or <code>null</code> to clear the value. 
    */
   public void setFiletype(String filetype)
   {
      this.filetype = filetype;
   }

   /**
    * Determine if the tool is to be run in GUI mode. 
    * @return True if the tool is set for GUI mode. 
    */
   public boolean isGuiMode()
   {
      return guiMode;
   }

   /**
    * Set the tool to run in GUI mode. 
    * @param guiMode True if the tool is to run in gui mode. 
    */
   public void setGuiMode(boolean guiMode)
   {
      this.guiMode = guiMode; 
   }

   /**
    * Get the MD5 setting. True if the tool is to use MD5 for post operations. 
    * @return The MD5 setting. 
    */
   public boolean isMd5() {
      return md5;
   }

   /**
    * Set the MD5 setting. 
    * @param md5 True if the tool should use MD5 for post operations. 
    */
   public void setMd5(boolean md5) {
      this.md5 = md5;
   }

   /**
    * Determine if the NoOp header should be sent. 
    * @return True if the header should be sent. 
    */
   public boolean isNoOp() {
      return noOp;
   }

   /**
    * Set the NoOp setting. 
    * @param noOp True if the NoOp header should be used. 
    */
   public void setNoOp(boolean noOp) {
      this.noOp = noOp;
   }

   /**
    * Determine if the verbose option is set. 
    * @return True if verbose option is set. 
    */
   public boolean isVerbose() {
      return verbose;
   }

   /**
    * Set the verbose option. 
    * @param verbose True if verbose should be set. 
    */
   public void setVerbose(boolean verbose) {
      this.verbose = verbose;
   }

   /**
    * Get the onBehalfOf value. 
    * @return The value, or <code>null</code> to clear the value.  
    */
   public String getOnBehalfOf() {
      return onBehalfOf;
   }

   /**
    * Set the onBehalf of Value. 
    * @param onBehalfOf The value, or <code>null</code> to clear the value. 
    */
   public void setOnBehalfOf(String onBehalfOf) {
      this.onBehalfOf = onBehalfOf;
   }

   /**
    * Get the format namespace value. 
    * @return The value, or <code>null</code> if the value is not set. 
    */
   public String getFormatNamespace() {
      return formatNamespace;
   }

   /**
    * Set the format namespace value. 
    * @param formatNamespace The value, or <code>null</code> to clear the value. 
    */
   public void setFormatNamespace(String formatNamespace) {
      this.formatNamespace = formatNamespace;
   }

   /**
    * Get the checksum error value. 
    * @return True if an error should be introduced into the checksum. 
    */
   public boolean getChecksumError() {
      return checksumError;
   }

   /**
    * Set the checksum error value. 
    * @param checksumError True if the error should be introduced. 
    */
   public void setChecksumError(boolean checksumError) {
      this.checksumError = checksumError;
   }

   /**
    * Get the current slug header.
    * @return The slug value, or <code>null</code> if the value is not set.  
    */
   public String getSlug( ) 
   {
      return this.slug;
   }

   /**
    * Set the text that is to be used for the slug header.
    * @param slug The value, or <code>null</code> to clear the value.  
    */
   public void setSlug(String slug)
   {
      this.slug = slug;
   }

   /**
    * Get the list of post destinations. 
    * @return An iterator over the list of PostDestination objects. 
    */
   public Iterator<PostDestination> getMultiPost() 
   {
      return multiPost.iterator();
   }


   /**
    * Determine if the noCapture option is set. This indicates that the code 
    * should not attempt to redirect stdout and stderr to a different output 
    * destination. Intended for use in a GUI client. 
    * 
    * @return The noCapture setting. True if set. 
    */
   public boolean isNoCapture()
   {
      return noCapture;
   }
}
