package yi.iu.imgdb.desc;

import java.util.*;
import yi.util.io.ReadArgu;
import java.io.*;
import yi.util.data.*;
import java.awt.event.*;
import javax.swing.event.*;
import yi.iu.imgdb.desc.gui.*;
import yi.util.config.*;
import yi.ds.*;

/**
 * ImgDescManager is the data model for image descriptions (keywords).
 *
 * It mainly maintains two data structures:
 *
 * m_fileDesc is a Hashmap having the mappings from image names to description
 * lists (keywords).  The types of those lists are HashSet.
 *
 * m_objNames is a HashSet containing all the descriptions (keywords) appeared
 * for any images.  So it the union set of those description lists maintained by
 * m_fileDesc.
 */

public class ImgDescManager {

  /**
   * Store the mapping from images to objects
   */
  HashMap m_fileDesc = new HashMap();

  /**
   * Store the object names
   */
  HashSet m_objNames = new HashSet();

  boolean m_objListChanged = false;
  boolean m_fileListChanged = false;
  boolean m_fileDescChanged = false;


  private transient Vector fileDescriptionListeners;
  private transient Vector fileListListeners;
  private transient Vector objListListeners;

  public ImgDescManager() {
  }

  public void addDesc(String file_name, String obj_name) {
    // add to object list
    addObj(obj_name);
    // add to the file description list
    addFile(file_name);
    // get the descriptions
    HashSet desc = getDesc(file_name);
    if (!desc.contains(obj_name)) {
      desc.add(obj_name);
      m_fileDescChanged = true;
      // System.out.println("manager added: " + file_name + ": " + obj_name);
    }
  }

  public void addDesc(String file_name, Collection obj_names) {

    addFile(file_name);
    HashSet desc = (HashSet)m_fileDesc.get(file_name);

    Iterator iter = obj_names.iterator();
    while (iter.hasNext()) {
      String obj = (String)iter.next();
      addObj(obj);
      if (!desc.contains(obj)) {
        desc.add(obj);
        m_fileDescChanged = true;
        // System.out.println("manager added: " + file_name + ": " + obj_name);
      }
    }
  }

  public void remove(String file_name, String obj_name) {
    // add to object list
    addObj(obj_name);
    // add to the file description list
    addFile(file_name);
    HashSet desc = getDesc(file_name);
    if (desc.contains(obj_name)) {
      desc.remove(obj_name);
      m_fileDescChanged = true;
      // System.out.println("manager added: " + file_name + ": " + obj_name);
    }
  }

  public void addFiles(java.io.File[] files) {
    clearEventFlags();
    for (int i=0; i<files.length; i++) {
      try {
        String name = files[i].getCanonicalPath();
        addFile(name);
      } catch (IOException e) {
        e.printStackTrace();
      }
    }
    fireEvents();
  }

  public void removeFiles(String[] files) {
    clearEventFlags();
    for (int i=0; i<files.length; i++) {
      removeFile(files[i]);
    }
    System.out.println("image # left: " + m_fileDesc.size());
    fireEvents();
  }

  public void removeObj(String selection) {
    clearEventFlags();
    Iterator iter = m_fileDesc.keySet().iterator();
    while (iter.hasNext()) {
      remove((String)iter.next(), selection);
    }
    if (m_objNames.contains(selection)) {
      m_objNames.remove(selection);
      m_objListChanged = true;
    }
    fireEvents();
  }

  public void clearAll() {
    clearEventFlags();
    if (!m_fileDesc.isEmpty()) {
      m_fileDesc.clear();
      m_fileListChanged = true;
      m_fileDescChanged = true;
    }
    if (!m_objNames.isEmpty()) {
      m_objNames.clear();
      m_objListChanged = true;
    }
    fireEvents();
  }

  private boolean addObj(String obj) {
    boolean isAdded = false;
    if (!m_objNames.contains(obj)) {
      m_objNames.add(obj);
      isAdded = true;
      m_objListChanged = true;
    }
    return isAdded;
  }

  private boolean addFile(String file) {
    boolean isAdded = false;
    if (!m_fileDesc.containsKey(file)) {
      m_fileDesc.put(file, new HashSet());
      isAdded = true;
      m_fileListChanged = true;
    }
    return isAdded;
  }

  private boolean removeFile(String file) {
    boolean isAdded = false;
    if (m_fileDesc.containsKey(file)) {
      m_fileDesc.remove(file);
      isAdded = true;
      m_fileListChanged = true;
//      System.out.println("removefile");
    }
    return isAdded;
  }

  public HashSet getDesc(String file_name) {
    HashSet desc = (HashSet)m_fileDesc.get(file_name);
    if (desc == null) desc = new HashSet();
    return desc;
  }


  private void parseLines(String[] descriptions) {
    String seperator = "::";
    int seperator_len = seperator.length();

    for (int j=0; j<descriptions.length; j++) {
      // parse line by line
      // e.g. D://images/bp32.jpg: building, sun, sky
      int index_of_colon = descriptions[j].indexOf(seperator);

      if (index_of_colon < 0) {
        continue;
      }
      String file_name = descriptions[j].substring(0, index_of_colon);
      file_name = StringUtil.removeBoundaryBlanks(file_name);
      String objects_line = descriptions[j].substring(index_of_colon + seperator_len);
//      System.out.println(objects_line);
      String[] objects = StringUtil.getDeliminatedStrings(objects_line, ",", true);
      addDesc(file_name, Arrays.asList(objects));
    }
  }

  public void readFromFile(java.io.File description_file) {
    String[] descriptions;

    // read all the lines from the file
    try {
      descriptions = ReadArgu.readStrings(description_file);
    } catch (IOException e) { // descriptions.txt not exist
      System.out.println("can't find " + description_file);
      return;
    }
    clearEventFlags();
    parseLines(descriptions);
    fireEvents();
  }

  private void clearEventFlags() {
    m_fileListChanged = m_objListChanged = m_fileDescChanged = false;
  }

  private void fireEvents() {
    if (m_fileListChanged) fireFileAdded(new FileListEvent(this));
    if (m_objListChanged) fireObjAdded(new ObjListEvent(this));
    if (m_fileDescChanged) fireDescChanged(new FileDescriptionEvent(this));
    clearEventFlags();
  }

  public void readFromFile(String description_file) {
    String[] descriptions;

    // read all the lines from the file
    try {
      descriptions = ReadArgu.readStrings(description_file);
    } catch (IOException e) { // descriptions.txt not exist
      System.out.println("can't find " + description_file);
      return;
    }
    clearEventFlags();
    parseLines(descriptions);
    fireEvents();
  }

  public void writeToFile(java.io.File description_file) {
    try {

      PrintWriter out = new PrintWriter(new FileWriter(description_file));
      Set files = m_fileDesc.keySet();
      Object[] file_array = files.toArray();
      Arrays.sort(file_array);
      for (int i=0; i<file_array.length; i++) {
        String file_name = (String)file_array[i];
        out.print(file_name + ":: ");
        Collection objs = (HashSet)m_fileDesc.get(file_array[i]);
        Object[] obj_array = objs.toArray();
        Arrays.sort(obj_array);
        for (int j=0; j<obj_array.length; j++) {
          if (j != 0) out.print(", ");
          out.print(obj_array[j]);
        }
        out.println();
      }
      out.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }


  public void writeToFile(String description_file) {
    writeToFile(new java.io.File(description_file));
  }

  public Set getFiles() {
    return m_fileDesc.keySet();
  }

  public Collection getObjects() {
    return m_objNames;
  }

  /**
   * Filter the images using their keywords.  The options are:
   * keep those images having all the keywords
   * keep those images having any of the keywords
   * remove those images having all the keywords
   * remove those images having any of the keywords
   */
  public static class ShowImages {
    public final static String USAGE = "Usage: -d desc_file -k keyword[,keyword]* -a(and_relation) -l(output_list) -p(keep_them) -o(output_desc)";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

        // init the datacenter
        String desc_file = ReadArgu.getOption('d', args);
        if (desc_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }

        String keywords = ReadArgu.getOption('k', args);
        if (keywords == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String keywords2 = keywords.replace('_', ' ');
        String[] keyword_list = keywords2.split(",");

        ImgDescManager img_database = new ImgDescManager();
        img_database.readFromFile(desc_file);

        String img_path = Configuration.getParameter("IMAGE_PATH", "");

        boolean and_relation = (ReadArgu.getOption('a', args) != null);

        boolean output_list = (ReadArgu.getOption('l', args)!= null);
        // keep those having all the keywords or any of the keywords
        boolean keep_them = (ReadArgu.getOption('p', args)!= null);

        // only show those image containing the specified keywords
        ArrayList files = new ArrayList(img_database.getFiles());
        Collections.sort(files);
        for (Iterator i=files.iterator(); i.hasNext(); ) {
          String f = (String)i.next();
          HashSet desc = img_database.getDesc(f);
          boolean found = true;
          if (and_relation) { // and relation of the keywords
            for (int j = 0; j < keyword_list.length; j++) {
              if (!desc.contains(keyword_list[j])) {
                found = false;
                break;
              }
            }
          } else {
            found = false;
            for (int j = 0; j < keyword_list.length; j++) {
              if (desc.contains(keyword_list[j])) {
                found = true;
                break;
              }
            }
          }
          if ((!found&&keep_them) || (found&&!keep_them)) {
            img_database.removeFile(f);
            continue;
          }
          if (output_list) {
            System.out.println(f);
          } else { // output html
            String demo_img = img_path + f;
            demo_img = toThumbImageName(demo_img);
            System.out.println("<a href=" + img_path + f + "><img src=" + demo_img +
                               "></a><br>");
            System.out.println(f + "<br>");
            ArrayList desc_list = new ArrayList(desc);
            Collections.sort(desc_list);
            for (Iterator j = desc_list.iterator(); j.hasNext(); ) {
              String d = (String) j.next();
              System.out.print(d + " ");
            }
            System.out.println("<br>");
          }
        }

        // output the description file
        String out_desc = ReadArgu.getOption('o', args);
        if (out_desc != null) {
          img_database.writeToFile(out_desc);
        }


      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public static String toThumbImageName(String img_name) {
    img_name = img_name.replaceFirst("groundtruth",
                                     "groundtruth.THUMB");
    img_name = img_name.replaceFirst("yiimg", "yiimg.THUMB");
    if (img_name.indexOf("freefoto") >= 0) {
      img_name = img_name.replaceFirst("_web",
                                     "_thumb");
    }
    return img_name;
  }

  /**
   * show the images having each of the keywords
   */
  public static class ShowImages2 {
    public final static String USAGE = "Usage: -d desc_file -l(output_list)";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

        // init the datacenter
        String desc_file = ReadArgu.getOption('d', args);
        if (desc_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        ImgDescManager img_database = new ImgDescManager();
        img_database.readFromFile(desc_file);

        String img_path = Configuration.getParameter("IMAGE_PATH", "");

        Collection keyword_list = img_database.getObjects();

        boolean output_list = (ReadArgu.getOption('l', args)!= null);

        for (Iterator k=keyword_list.iterator(); k.hasNext(); ) {
          String keyword = (String)k.next();

          String out_name = desc_file + "." + keyword + ".html";
          PrintStream out = new PrintStream(new FileOutputStream(out_name));

          // only show those image containing the specified keywords
          ArrayList files = new ArrayList(img_database.getFiles());
          Collections.sort(files);
          for (Iterator i = files.iterator(); i.hasNext(); ) {
            String f = (String) i.next();
            HashSet desc = img_database.getDesc(f);
            boolean found = true;
            if (!desc.contains(keyword)) {
              continue;
            }
            if (output_list) {
              out.println(img_path + f);
            }
            else { // output html
              String demo_img = img_path + f;
              demo_img = toThumbImageName(demo_img);
              out.println("<a href=" + img_path + f + "><img src=" + demo_img +
                                 "></a><br>");
              out.println(f + "<br>");
              ArrayList desc_list = new ArrayList(desc);
              Collections.sort(desc_list);
              for (Iterator j = desc_list.iterator(); j.hasNext(); ) {
                String d = (String) j.next();
                out.print(d + " ");
              }
              out.println("<br>");
            }
          }
          out.close();
        }

      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public static class ListImages {
    public final static String USAGE = "Usage: -d desc_file";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

        // init the datacenter
        String desc_file = ReadArgu.getOption('d', args);
        if (desc_file == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        ImgDescManager img_database = new ImgDescManager();
        img_database.readFromFile(desc_file);

        // only show those image containing the specified keywords
        ArrayList files = new ArrayList(img_database.getFiles());
        Collections.sort(files);
        for (Iterator i=files.iterator(); i.hasNext(); ) {
          String f = (String)i.next();
          System.out.println(f);
//          System.out.println("<img src=" + f + "><br>");
//          System.out.println(f + "<br>");

//          ArrayList desc_list = new ArrayList(desc);
//          Collections.sort(desc_list);
//          for (Iterator j=desc_list.iterator(); j.hasNext();) {
//            String d = (String)j.next();
//            System.out.print(d + " ");
//          }
//          System.out.println("<br>");
        }

      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  public static class GetKeywords {
    public final static String USAGE = "Usage: -d desc_file -i img_list -k keyword[,keyword]* -o output_desc_file -l(output list file) -k(keep_keywords)";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

        // init the datacenter
        String desc_file = ReadArgu.getOption(
            'd', args,
            "f:/creatas/descriptions.txt"
            );

        String img_list_file = ReadArgu.getOption(
            'i', args,
            "creatas.select.list"
            );
        String[] img_list = ReadArgu.readStrings(img_list_file);
        HashSet img_set = new HashSet();
        for (int i=0; i<img_list.length; i++) img_set.add(img_list[i]);


        String keywords = ReadArgu.getOption('k', args);
        if (keywords == null) {
          System.err.println(USAGE);
          System.exit(1);
        }
        String keywords2 = keywords.replace('_', ' ');
        String[] keyword_list = keywords.split(",");
        HashSet keyword_set = new HashSet();
        for (int i=0; i<keyword_list.length; i++) keyword_set.add(keyword_list[i]);

        // output desc file name
        String out_desc_file = ReadArgu.getOption(
            'o', args,
            "out.desc"
            );

        ImgDescManager img_database = new ImgDescManager();
        img_database.readFromFile(desc_file);

        boolean keep_keywords = (ReadArgu.getOption('k', args)!= null);

        boolean output_list = (ReadArgu.getOption('l', args)!= null);

        // only show those image containing the specified keywords
        ArrayList files = new ArrayList(img_database.getFiles());
        Collections.sort(files);
        for (Iterator i=files.iterator(); i.hasNext(); ) {
          String f = (String)i.next();
          if (!img_set.contains(f)) {
            i.remove();
            img_database.removeFile(f);
            continue;
          }
          HashSet desc = (HashSet)img_database.getDesc(f).clone();
          // see whether the descriptions have at least one of the keywords defined.
          boolean found = false;
          for (Iterator j=keyword_set.iterator(); j.hasNext(); ) {
            if (desc.contains(j.next())) {
                found = true;
                break;
            }
          }
          if (!found) {
            img_database.removeFile(f);
          }
          // remove those keywords other than the defined
          if (!keep_keywords) {
            for (Iterator j = desc.iterator(); j.hasNext(); ) {
              String d = (String) j.next();
              if (!keyword_set.contains(d)) {
                j.remove();
                img_database.remove(f, d);
              }
            }
          }
          if (output_list) {
            System.out.println(f);
          } else { // output html
            System.out.println("<img src=" + f + "><br>");
            System.out.println(f + "<br>");
            ArrayList desc_list = new ArrayList(desc);
            Collections.sort(desc_list);
            for (Iterator j = desc_list.iterator(); j.hasNext(); ) {
              String d = (String) j.next();
              System.out.print(d + " ");
            }
            System.out.println("<br>");
          }
        }

        img_database.writeToFile(out_desc_file);

      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   * count the occurance of each "keyword"
   * @return
   */
  public CounterHashMap getLabelStat() {

    // count the occurance of each "keyword"
    CounterHashMap counter = new CounterHashMap();
    ArrayList files = new ArrayList(getFiles());

    for (Iterator i = files.iterator(); i.hasNext(); ) {
      String f = (String) i.next();
      HashSet desc = getDesc(f);
      counter.increaseCount(desc);
    }
    return counter;
  }


  /**
   * Remove the keywords appearing times less than the specified threshold
   */
  public static class FilterKeywords {
    public final static String USAGE = "Usage: -d desc_file -o out_desc_file -t threshold";

    public static void main(String[] args) {
      // init configuration
      Configuration.init(args);

      try {

        // init the datacenter
        String desc_file = ReadArgu.getOption(
            'd', args,
            "groundtruth.desc"
            );

        // output desc file name
        String out_desc_file = ReadArgu.getOption(
            'o', args,
            "out.desc"
            );

        String t_str = ReadArgu.getOption(
            't', args,
            "-1"
            );
        int threshold = Integer.parseInt(t_str);


        ImgDescManager idm = new ImgDescManager();
        idm.readFromFile(desc_file);

        // count the occurance of each "keyword"
        CounterHashMap counter = idm.getLabelStat();

        LinkedList keywords = new LinkedList(counter.keySet());
        Collections.sort(keywords, counter);

        byte[] buf = new byte[128];
        for (Iterator i=keywords.iterator(); i.hasNext(); ) {
          String key = (String)i.next();
          int c = counter.getCount(key);
          System.out.println(key + " (" + c + ")");
          if (threshold>=0) {
            if (c < threshold) {
              idm.removeObj(key);
              System.out.println("delete " + key + " (" + c + ")");
            }
            continue;
          }
          System.out.println(key + " (" + c + "), keep(y)?");
          System.in.read(buf);
          char resp = (char)buf[0];
          if (resp == 'n' || resp == 'N') {
            System.out.println("delete " + key);
            idm.removeObj(key);
          }
        }

        idm.writeToFile(out_desc_file);
//        for (int i=1; i<=5000; i++) {
//          String f = Integer.toString(i);
//          System.out.print(f + ":: ");
//          ArrayList desc = new ArrayList(img_database.getDesc(f));
//          Collections.sort(desc);
//          int j_index =0;
//          for (Iterator j=desc.iterator(); j.hasNext(); j_index++) {
//            if (j_index != 0) System.out.print(", ");
//            System.out.print(j.next());
//          }
//          System.out.println();
//        }

      }
      catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  /**
   *
   */
  public static void main(String[] args) {
    ImgDescManager img_database = new ImgDescManager();
    img_database.readFromFile("/Shared/descriptions.txt");
    img_database.writeToFile("/Shared/descriptions.new.txt");
  }

  public synchronized void removeFileDescriptionListener(FileDescriptionListener l) {
    if (fileDescriptionListeners != null && fileDescriptionListeners.contains(l)) {
      Vector v = (Vector) fileDescriptionListeners.clone();
      v.removeElement(l);
      fileDescriptionListeners = v;
    }
  }
  public synchronized void addFileDescriptionListener(FileDescriptionListener l) {
    Vector v = fileDescriptionListeners == null ? new Vector(2) : (Vector) fileDescriptionListeners.clone();
    if (!v.contains(l)) {
      v.addElement(l);
      fileDescriptionListeners = v;
    }
  }
  protected void fireDescChanged(FileDescriptionEvent e) {
    if (fileDescriptionListeners != null) {
      Vector listeners = fileDescriptionListeners;
      int count = listeners.size();
      for (int i = 0; i < count; i++) {
        ((FileDescriptionListener) listeners.elementAt(i)).descChanged(e);
      }
    }
  }
  public synchronized void removeFileListListener(FileListListener l) {
    if (fileListListeners != null && fileListListeners.contains(l)) {
      Vector v = (Vector) fileListListeners.clone();
      v.removeElement(l);
      fileListListeners = v;
    }
  }
  public synchronized void addFileListListener(FileListListener l) {
    Vector v = fileListListeners == null ? new Vector(2) : (Vector) fileListListeners.clone();
    if (!v.contains(l)) {
      v.addElement(l);
      fileListListeners = v;
    }
  }
  protected void fireFileAdded(FileListEvent e) {
//    System.out.println("fireFileAdded");
    if (fileListListeners != null) {
      Vector listeners = fileListListeners;
      int count = listeners.size();
      System.out.println("to " + count + " listener");
      for (int i = 0; i < count; i++) {
        ((FileListListener) listeners.elementAt(i)).FileAdded(e);
      }
    }
  }
  protected void fireFileRemoved(FileListEvent e) {
    if (fileListListeners != null) {
      Vector listeners = fileListListeners;
      int count = listeners.size();
      for (int i = 0; i < count; i++) {
        ((FileListListener) listeners.elementAt(i)).FileRemoved(e);
      }
    }
  }
  public synchronized void removeObjListListener(ObjListListener l) {
    if (objListListeners != null && objListListeners.contains(l)) {
      Vector v = (Vector) objListListeners.clone();
      v.removeElement(l);
      objListListeners = v;
    }
  }
  public synchronized void addObjListListener(ObjListListener l) {
    Vector v = objListListeners == null ? new Vector(2) : (Vector) objListListeners.clone();
    if (!v.contains(l)) {
      v.addElement(l);
      objListListeners = v;
    }
  }
  protected void fireObjAdded(ObjListEvent e) {
    if (objListListeners != null) {
      Vector listeners = objListListeners;
      int count = listeners.size();
      for (int i = 0; i < count; i++) {
        ((ObjListListener) listeners.elementAt(i)).ObjAdded(e);
      }
    }
  }
  protected void fireObjRemoved(ObjListEvent e) {
    if (objListListeners != null) {
      Vector listeners = objListListeners;
      int count = listeners.size();
      for (int i = 0; i < count; i++) {
        ((ObjListListener) listeners.elementAt(i)).ObjRemoved(e);
      }
    }
  }

  public HashMap getObjCount(Object[] file_names) {
    HashMap the_desc = new HashMap();
    for (int i=0; i<file_names.length; i++) {
      String file_name = (String)file_names[i];
      HashSet desc = getDesc(file_name);

      Iterator iter = desc.iterator();
      while (iter.hasNext()) {
        String key = (String)iter.next();
        if (the_desc.containsKey(key)) {  // count ++
          Integer count = (Integer)the_desc.get(key);
          the_desc.put(key, new Integer(count.intValue() + 1));
        } else { // count =1
          the_desc.put(key, new Integer(1));
        }
      }
    }
    return the_desc;
  }
}