package Sight.Structures;
import java.io.*;
import java.net.*;
import java.util.*;
import Sight.Agents.util.*;

public class AMap implements java.io.Serializable
 {

   public diString L[];
   int step;
   public int length;
   int Top;

   public AMap() { this(10,25); };
   public AMap(int a, int b)
    { L = new diString[a+1];
      step = b;
      Top = a+1;
      length = 0;
    };

   public AMap(AMap G)
    { int k;
      length = G.length;
      L = new diString[length+5];
      step = G.step;
      Top =  length+5;
      for (k=0;k<length;k++)
       L[k]=G.L[k];
    };

   public boolean has(String N)
    { int k;
      if (length>0)
       for (k=0;k<length;k++)
        if (L[k].name.compareTo(N)==0) return true;
      return false;
    };

   public String get(String key)
    { if (key==null) throw new Error("null key");
      int k;
      if (length>0)
       for (k=0;k<length;k++)
        if (L[k]!=null
          &&
          (L[k].name.compareTo(key)==0)) return L[k].r;
      return null;
    };

   public Collection keySet()
    {
      ArrayList a = new ArrayList(length);
      for (int i = 0; i < length; i++) {
        a.add(L[i].name);
      }
      return a;
    };

   public int size() { return length; };

   public int indexOf(String N)
    { int k;
      if (length>0)
       for (k=0;k<length;k++)
        if (L[k].name.compareTo(N)==0) return k;
      return -1;
    };

   public void Clear() { length = 0; }

   public diString at(int x) throws ArrayIndexOutOfBoundsException
    { String q;
      if (x>=length) return null;
      return L[x];
    };

   public void Sort()
    { int k, l; diString n;
      for (k=0; k<length; k++)
       for (l=k+1; l<length; l++)
        if (L[k].name.compareTo(L[l].name)>0) // diString
         { n = L[k]; L[k] = L[l]; L[l]=n; };
    };

   public void add(String a, String b)
    { add(new diString(a,b)); };

   public void add(diString N)
    { //if (has(N.v)) return;
      if (N.name==null)
       throw new Error("name null");

      diString nL[];
      int k;
      length++;
      if (length>Top)
       { nL = new diString[Top+step];
         for (k=0;k<length-1;k++)
          nL[k]=L[k];
         L = nL;
         Top = Top+step;
       };
      L[length-1] = N;
    };

   public void addAll(AMap a)
    {
      for (int i = 0; i < a.length; i++) {
        add(a.L[i]);
      }

    };

   public void set(String v, double r)
    { set(v, Double.toString(r).trim()); };

   public void put(String v, String r)
    { add(v,r); };

   public void set(String v, int r)
    { set(v, Integer.toString(r).trim()); };

   public void set(String v, String r)
    { int n = indexOf(v);
      if (n<0) // new
       { add( new diString(v,r) ); }
      else     // was
       { L[n].name = v;
         L[n].r = r; };
    };

   public void remove(diString n)
    { int nn;
      int k, l;
      nn = -1;
      for (k = 0 ; k < length ; k++)
       if (L[k]==n)
        { nn=k; break; };
      if (nn<0) return; // diString not found.
      // Shift the rest of array down if this is not the last element:
      if (nn!=length-1)
       for (k = nn+1 ; k < length ; k++)
        L[k-1]=L[k];
      length--;
    };

   public void setElementAt(diString N, int p)
    { L[p] = N;
    };

   public void dump() { dump(System.out); };
   public void dump(PrintStream f)
    { int k;
      for (k=0;k<length;k++)
       f.println(at(k).toString());
    }

   /** Generates string for POST and GET. Items with the names,
    *  containing ':' (special items) are not included. All
    *  other items are included in the form
    *  name1=value1&name2=value2&... &namen=valuen
    */
   public String getPostString()
     { StringBuffer b = new StringBuffer(316);
       for (int k=0;k<length;k++)
        { if (L[k]!=null && L[k].name!=null && L[k].r!=null)
          {
          if (L[k].name.indexOf(':')<0)
           {
            try {
            b.append(URLEncoder.encode(L[k].name,"UTF-8"));
            b.append("=");
            b.append(URLEncoder.encode(L[k].r,"UTF-8"));
            } catch (UnsupportedEncodingException exc)
              {
                exc.printStackTrace();
                System.out.println("UTF-8 not supported???!");
                System.exit(0);
              };

            if (k!=length-1) b.append("&");
           };
          };
        };
       return b.toString();
     };

   /** Generates string for local exec. Items with the names,
    *  containing ':' (special items) are not included. All
    *  other items are included in the form
    *  command name value1 name2 value2 ... namen=valuen
    *  Command is taken from the field exe:command .
    *  In exe:command case, the file: prefix means that
    *  the value must be stored to the hard disk as temporary
    *  file, and the file reference must be included. For the
    *  prefix fastaf: the similar file will be created, but
    *  the fasta header will be added (required by some programs).
    *
    *  The exception is throwed if some file errors occur
    *  when writing the parameters under file: or fastaf: prefix.
    *
    * To delete the temporary files immediately, call deleteRequest(String cmd),
    * using the returned command string. The files will be automatically
    * deleted on exit.
    */
   public String getExeString()
    throws FatalAgentException
     {
       File[] files = null;
       try {
       StringBuffer b = new StringBuffer(316);
       String name;
       String header = null;

       b.append(get("exe:command"));
       b.append(' ');
       for (int k=0;k<length;k++)
        { if (L[k]!=null && L[k].name!=null && L[k].r!=null)
          {
          name = L[k].name;
          if (name.indexOf(':')<0)
           {
            b.append(L[k].name);
            b.append(" ");
            b.append(L[k].r);
            if (k!=length-1) b.append(" ");
           } else
           {
             if (name.startsWith("file:") || name.startsWith("fastaf:") )
              {
               if ( name.startsWith("file:") )
                {
                  if (files==null) files = new File[length];
                   // just reserve array for all parameters, some will be nulls.
                   // there are seldom more then 10 parameters.
                  name = name.substring("file:".length());
                  header = null;
                } else

               if ( name.startsWith("fastaf:") )
                {
                  if (files==null) files = new File[length];
                   // just reserve array for all parameters, some will be nulls.
                   // there are seldom more then 10 parameters.
                  name = name.substring("fastaf:".length());
                  header = ">sight_query\n";
                };

                File file = store(name, L[k].r, header);
                files[k] = file;
                b.append(name);
                b.append(" ");
                b.append(file.getAbsolutePath());
                System.out.println("Storing "+file.getAbsolutePath());
               };
            }
          };
        };

       String string = b.toString();
       if (files!=null)
        deletable.put(string, files);

       return string;
       } catch (Exception exc)
         {
           throw new FatalAgentException("Unable to create exe string.", exc);
         };

     };

   /** Stores the content to the hard disk, returns the name of
    *  the resulted file.
    */
   File store(String name, String value, String header) throws Exception
    {

      StringBuffer sbn = new StringBuffer("sa_"+name);
      for (int i = 0; i < sbn.length(); i++) {
        if (!Character.isLetterOrDigit(sbn.charAt(i)))
         sbn.setCharAt(i,'_');
      }

      File tmp = File.createTempFile(sbn.toString(), null);
      tmp.deleteOnExit();
      FileWriter writer = new FileWriter(tmp);
      if (header!=null) writer.write(header);
      writer.write(value);
      writer.close();
      return tmp;
    };

   /** Maps collections of files to be deleted to the returned command line strings. */
   static Map deletable = Collections.synchronizedMap(new HashMap());

   public static void deleteRequest(String command)
    {
      File [] files = (File[]) deletable.get(command);
      if (files!=null)
       for (int i = 0; i < files.length; i++) {
         if (files[i]!=null)
          {
           System.out.println("Deleting "+files[i].getAbsolutePath());
           files[i].delete();
          };
       }

    }

 }