package commons.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import basics.math.BasicStatistics;
import basics.profiler.Profiler;
import basics.utl.NumUtl;
import basics.utl.SysUtl;
import commons.collections.interfaces.Sized;

/**
 * Stores a pair of words and a double value that can be a counter or a weight. - use add(w,w) when
 * you are interested in the frequency of the pair and the total number of words. - use put to
 * overwrite an older put for the same pair.
 * @author ks
 */
public class PairList implements Sized
{
List<Pair>                 list    = new ArrayList<Pair>();
transient MapMap<Integer>  index   = new MapMap<Integer>();
// not persistent yet
public static final String DOCTYPE = "c_classcandidates";

public PairList()
{
}

public PairList(String uslessname)
{
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getList()
 */
public List<Pair> getList()
{
   return list;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#refreshIndex()
 */
public void refreshIndex()
{
   Profiler.resume("pairlist_index");
   index.clear();
   for(int i = 0; i < list.size(); i++)
   {
      Pair p = list.get(i);
      index.put(p.getTerm1(), p.getTerm2(), i);
   }
   Profiler.stop("pairlist_index");
   Profiler.increment("pairlist_index", list.size());
   // OsUtl.trace("pairlist reindexed: " + Profiler.report("pairlist_index"));
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#setList(java.util.List)
 */
public void setList(List<Pair> list)
{
   this.list = list;
   refreshIndex();
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#size()
 */
public int size()
{
   return list.size();
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#get(int)
 */
public Pair get(int index)
{
   return list.get(index);
}

//public void set (int index, Pair p)
//{
//   list.set(index, p);
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#signs()
 */
public long signs()
{
   long signs = 0;
   for(Iterator<String> I = index.keyIterator(); I.hasNext();)
   {
      String t = I.next();
      signs += t.length();
      Iterator<String> II = rightTermIterator(t);
      while(II.hasNext())
         signs += II.next().length();
   }
   return signs;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#iterator()
 */
public Iterator<Pair> iterator()
{
   return getList().iterator();
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#rightTermIterator(java.lang.String)
 */
public Iterator<String> rightTermIterator(final String leftTerm)
{
   return index.innerKeyIterator(leftTerm);
}

/*
 * This iterator is not respecting the sorting order
 * but fastly accessing a subset through a map interface!! 
 */
public Iterator<Pair> filteredPairIterator(final String leftTerm)
{
   final Iterator<String> iter = index.innerKeyIterator(leftTerm);
   return new Iterator<Pair>()
   {
      public boolean hasNext()
      {
         return iter.hasNext();
      }

      public Pair next()
      {
         String rterm = iter.next();
         Pair p = get(leftTerm, rterm);
         return p;
      }

      public void remove()
      {
      }
   };
}

/*
 * It respectOrder is true the sorting that may have taken place earlier
 * will be respected. But comes at a price and is slower.  
 */
public Iterator<Pair> filteredPairIterator(final String leftTerm, boolean respectOrder)
{
   //fast
   if(!respectOrder)
      return filteredPairIterator(leftTerm);
   //slow but respecting sorting
   final Iterator<Pair> iter = getList().iterator();
   return new Iterator<Pair>()
   {
      Pair current = null;

      public boolean hasNext()
      {
         for(; iter.hasNext();)
         {
            current = iter.next();
            if(leftTerm.equals(current.term1))
               return true;
         }
         return false;
      }

      public Pair next()
      {
         return current;
      }

      public void remove()
      {
      }
   };
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#extractSubPairList(java.lang.String)
 */
public List<Pair> extractSubPairList(String leftTerm)
{
   List<Pair> list = new ArrayList<Pair>();
   Iterator<Pair> iter = filteredPairIterator(leftTerm);
   while(iter.hasNext())
      list.add(iter.next());
   return list;
}

// public synchronized void put (Pair p) {
// int ndx = list.indexOf(p);
// if (ndx == -1) list.add(p);
// else list.set(ndx, p);
// }
//	
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#set(java.lang.String, java.lang.String, double)
 */
public synchronized void set(String a, String b, double v)
{
   Integer ndx = index.get(a, b);
   if(ndx == null)
   {
      ndx = list.size();
      list.add(new Pair(a, b, v));
      index.put(a, b, ndx);
   }
   else
   {
      Pair p = list.get(ndx);
      p.value = v;
      list.set(ndx, p);
   }
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#add(java.lang.String, java.lang.String)
 */
public synchronized void add(String a, String b)
{
   add(a, b, 1);
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#add(java.lang.String, java.lang.String, double)
 */
public synchronized void add(String a, String b, double v)
{
   Integer ndx = index.get(a, b);
   if(ndx == null)
   {
      ndx = list.size();
      list.add(new Pair(a, b, v));
      index.put(a, b, ndx);
   }
   else
   {
      Pair p = list.get(ndx);
      p.value += v;
      list.set(ndx, p);
   }
}

///**
// * Removes a pair. Must re-index: this is slow!
// * @param a
// * @param b
// */
//public void remove (String a, String b)
//{
//   list.remove(new Pair(a, b));
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#remove(int)
 */
public void remove(int i)
{
   list.remove(i);
}

// public synchronized void add (Pair p) {
// Integer ndx = index.get(a, b);
// int ndx = list.indexOf(p);
// if (ndx == -1) list.add(p);
// else {
// Pair q = list.get(ndx);
// p.value += q.value;
// list.set(ndx, p);
// }
// }
//public synchronized PairList copyIfEqualOrHigher (double minimalWeight)
//{
//   PairList pl = new PairList();
//   for (int i = 0; i < list.size(); i++)
//   {
//      Pair p = list.get(i);
//      if (p.value >= minimalWeight) pl.add(p.term1, p.term2, p.value);
//   }
//   pl.refreshIndex();
//   return pl;
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#cutIfBelow_NoReIndexing(double)
 */
public synchronized void cutIfBelow_NoReIndexing(double threshold)
{
   int size = list.size();
   for(int i = size - 1; i >= 0; i--)
   {
      Pair p = list.get(i);
      if(p.value < threshold)
      {
         // OsUtl.trace("Removing: " + i + ", threshold:" + threshold + ",
         // p.value:" +
         // p.value);
         list.remove(i);
      }
   }
   int size2 = list.size();
   // OsUtl.trace("Removed " + (size - size2) + " pairs with value below " +
   // threshold
   // + ", leaving " + size2 + " entries.");
}

///**
// * removes pairs below a given threshold. Index is refreshed at the end.
// */
//public synchronized void cutIfBelow (double threshold)
//{
//   cutIfBelow_NoReIndexing(threshold);
//   refreshIndex();
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getValue(java.lang.String, java.lang.String, double)
 */
public Double getValue(String a, String b, double defaultValue)
{
   Integer ndx = index.get(a, b);
   return ndx == null ? defaultValue : list.get(ndx).value;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#contains(java.lang.String, java.lang.String)
 */
public boolean contains(String a, String b)
{
   return index.contains(a, b);
}

///**
// * get value by index position, which must exist. return the corresponding value.
// */
//public Double getValue (int i)
//{
//   return list.get(i).value;
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getPair(java.lang.String, java.lang.String)
 */
public Pair get(String a, String b)
{
   Integer ndx = index.get(a, b);
   return ndx == null ? null : list.get(ndx);
}

//public int getIndex (String a, String b)
//{
//   Integer ndx = index.get(a, b);
//   return ndx == null ? -1 : ndx;
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getPair(int)
 */
public Pair getPair(int i)
{
   return list.get(i);
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#clone()
 */
public Object clone()
{
   PairList nl = new PairList();
   for(Pair p : list)
      nl.list.add(p);
   nl.refreshIndex();
   return nl;
}

//public void substract (PairList other)
//{
//   for (int i = list.size() - 1; i > -1; i--)
//   {
//      Pair p = list.get(i);
//      if (other.contains(p.term1, p.term2)) list.remove(i);
//   }
//   refreshIndex();
//}
/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getMaxValue()
 */
public double getMaxValue()
{
   double max = Double.MIN_VALUE;
   for(int i = 0; i < list.size(); i++)
   {
      Pair p = list.get(i);
      max = p.value > max ? p.value : max;
   }
   return max;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getMinValue()
 */
public double getMinValue()
{
   double min = Double.MAX_VALUE;
   for(int i = 0; i < list.size(); i++)
   {
      Pair p = list.get(i);
      min = p.value < min ? p.value : min;
   }
   return min;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getAverageValue()
 */
public double getAverageValue()
{
   int size = list.size();
   if(size == 0)
      return 0D;
   double avg = 0D;
   for(int i = 0; i < size; i++)
   {
      Pair p = list.get(i);
      avg += p.value;
   }
   avg /= size;
   return avg;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#normaliseTable()
 */
public synchronized void normaliseTable()
{
   String id = this.getClass().getSimpleName() + ":normaliseTable";
   Profiler.start(id);
   double max = getMaxValue();
   max = max == 0 ? 1 : max;
   int size = list.size();
   //   OsUtl.trace("--- size: " + size + " max:" + max);
   for(int i = 0; i < size; i++)
   {
      Pair p = list.get(i);
      double v = p.value / max;
      // p.value /= max;
      // list.set(i, p);
      list.get(i).value = v;
   }
   Profiler.stop(id);
   Profiler.increment(id, size);
   //   OsUtl.trace(Profiler.report(id));
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#getStats()
 */
public BasicStatistics getStats()
{
   BasicStatistics stats = new BasicStatistics();
   for(int i = 0; i < list.size(); i++)
   {
      Pair p = list.get(i);
      stats.add(p.value);
   }
   // OsUtl.trace("stats:" + stats.toString());
   return stats;
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#sort(boolean)
 */
@SuppressWarnings("unchecked")
public void sort(boolean down)
{
   String id = this.getClass().getSimpleName() + ":sort";
   Profiler.resume(id);
   Collections.sort(list, down ? DESCEND : ASCEND);
   Profiler.stop(id);
   refreshIndex();
   Profiler.increment(id, list.size());
}

@SuppressWarnings("unchecked")
public static void sort(List<Pair> list, boolean down)
{
   String id = PairList.class.getSimpleName() + ":staticsort";
   Profiler.resume(id);
   Collections.sort(list, down ? DESCEND : ASCEND);
   Profiler.stop(id);
   Profiler.increment(id, list.size());
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#toString()
 */
public String toString()
{
   StringBuilder s = new StringBuilder();
   long np = size();
   s.append("Pairs: " + np);
   return s.toString();
}

/*
 * (non-Javadoc)
 * @see utl.langpro.PairList#report()
 */
public String report()
{
   StringBuilder s = new StringBuilder();
   for(Pair p : list)
   {
      s.append(p.term1);
      s.append(":");
      s.append(p.term2);
      s.append("=");
      s.append(NumUtl.round(p.value, 2));
      s.append(";");
   }
   return s.toString();
}

@SuppressWarnings("unchecked")
static final Comparator DESCEND = new Comparator()
                                {
                                   public int compare(Object o1, Object o2)
                                   {
                                      Pair r1 = (Pair)o1;
                                      Pair r2 = (Pair)o2;
                                      Double v1 = r1.getValue();
                                      Double v2 = r2.getValue();
                                      return v2.compareTo(v1);
                                   }
                                };
@SuppressWarnings("unchecked")
static final Comparator ASCEND  = new Comparator()
                                {
                                   public int compare(Object o1, Object o2)
                                   {
                                      Pair r1 = (Pair)o1;
                                      Pair r2 = (Pair)o2;
                                      Double v1 = r1.getValue();
                                      Double v2 = r2.getValue();
                                      return v1.compareTo(v2);
                                   }
                                };

public static void main(String [] args)
{
   PairList pt = new PairList();
   String a = "abc";
   String b = a.toUpperCase();
   int x = 0;
   for(int i = 0; i < a.length(); i++)
   {
      for(int j = 0; j < a.length(); j++)
      {
         pt.add("" + a.charAt(i), "" + b.charAt(i), x++);
      }
   }
   SysUtl.trace(pt.toString());
}
}
