package yi.iu.processor.color.reg;

import java.util.*;
import java.awt.Dimension;
import yi.iu.ft.local.*;
import yi.util.io.input.DIU;
import yi.util.io.output.*;
import yi.util.config.*;
import yi.util.io.ReadArgu;
import yi.iu.processor.ort.*;
import yi.iu.processor.color.reg.RgbColorRegion;
import yi.iu.region.Region;
import yi.iu.processor.color.quan.*;
import java.io.*;
import yi.ds.*;

public class DS_RgbColorRegions implements java.io.Serializable, Csvable, Globalizable, Regionalizable, Configurable{

  int m_imgWidth, m_imgHeight;
  Vector m_regions;
  int[] m_labels;

  /**
   * For quick processing, the image may be scaled down and m_scale is the scale
   * rate
   */
  double m_scale = 1.0;

  public int GLOBALIZE_FTH1 = 1;

  public int GLOBALIZE_METHOD = GLOBALIZE_FTH1;

  /**
   * To decide what to return from method toCsv().  It is useful when testing
   * different learning schema based on different feature properties.
   */
  public int CSV_SCHEME = 0;

  public String[] GLOBALIZE_SUFFIX = new String[] {
    new String("FTH0"),
    new String("FTH1")
  };

  public String[] getParameterNames() {
    return new String[] {
      "GLOBALIZE_METHOD",
      "CSV_SCHEME"
    };
  }

  public DS_RgbColorRegions() {
    Configuration.configParameters(this);
  }

  public DS_RgbColorRegions(int w, int h, Vector regions) {
    this();
    m_imgWidth  = w;
    m_imgHeight = h;
    m_regions = regions;
  }

  public DS_RgbColorRegions(int w, int h, Vector regions, int[] labels) {
    this(w,h,regions);
    m_labels = labels;
  }

  public int getRegionNum() {
    return m_regions.size();
  }

  public void setRegionNum() {
  }

  public int getImageWidth() {
    return m_imgWidth;
  }

  public void setImageWidth(int w) {
    m_imgWidth = w;
  }

  public int getImageHeight() {
    return m_imgHeight;
  }

  public void setImageHeight(int h) {
    m_imgHeight = h;
  }

  public Vector getRegions() {
    return m_regions;
  }

  public void setRegions(Vector regions) {
    m_regions = regions;
  }

  public int[] getLabels() {
    return m_labels;
  }

  public void setLabels(int[] label) {
    m_labels = label;
  }

  public double getScale() {
    return m_scale;
  }

  public void setScale(double scale) {
    m_scale = scale;
  }


  /**
   * List the regions (not RgbColorRegions)
   * @return
   */
  public Iterator listRegions() {
    Vector regions = new Vector();
    for (Iterator i = m_regions.iterator(); i.hasNext();) {
        RgbColorRegion l = (RgbColorRegion)i.next();
        Region r = l.m_region;
        regions.add(r);
    }
    return regions.iterator();
  }

  public int numRegions() {
    return m_regions.size();
  }


  public double[][] toCsv() {
    double[][] csv = new double[m_regions.size()][];
    for (int i=0; i<m_regions.size(); i++) {
      RgbColorRegion r = (RgbColorRegion)m_regions.elementAt(i);
      switch (CSV_SCHEME) {
        case 0: // full description
          double[][] reg_csv = r.toCsv();
          csv[i] = reg_csv[0];
          break;
        case 1: // only H,S,B
          float[] hsbvals = new float[3];
          java.awt.Color.RGBtoHSB(r.r, r.g, r.b, hsbvals);
          csv[i] = new double[]{hsbvals[0], hsbvals[1], hsbvals[2]};

          break;
      }
    }

    return csv;
  }

  public static DS_RgbColorRegions fromCsv(double[][] csv) {
    Vector rgb_color_regions = new Vector();
    for (int i=0; i<csv.length; i++) {
      rgb_color_regions.add(RgbColorRegion.fromCsv(csv[i]));
    }
    return new DS_RgbColorRegions(-1, -1, rgb_color_regions);
  }

  public GlobalHistogram getGlobalized(){
    if (GLOBALIZE_METHOD == GLOBALIZE_FTH1) return getGlobalized_FTH1();
    else return null;
  }

  public static int FTH1_BIN_NUM = 166;
  public GlobalHistogram getGlobalized_FTH1(){

    // store the histogram
    int[] region_num = new int[FTH1_BIN_NUM];
    double[] region_area_sum = new double[FTH1_BIN_NUM];

    // construct the histogram
    Iterator i = m_regions.iterator();
    while (i.hasNext()){
      RgbColorRegion region = (RgbColorRegion)i.next();
      int r = region.r, b = region.b, g = region.g;

      int bin_index = (int)SmithColorQuantization.quantize(r,g,b);
        // direction = [0, Math.PI)
      region_num[bin_index] ++;
      region_area_sum[bin_index] += region.getRegion().m_size;
    }

    // for normalization
    double img_area = m_imgHeight * m_imgWidth;

    // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<FTH1_BIN_NUM; j++) {
      global_hist.add(new Integer(region_num[j]));
    }
    for (int j=0; j<FTH1_BIN_NUM; j++) {
      global_hist.add(new Double(region_area_sum[j] / img_area));
    }
    return global_hist;
  }

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

      // get the image name
      String img_name = ReadArgu.getOption('i', args);
      if (img_name == null) {
        System.err.println("Usage: -i image_name");
        System.exit(1);
      }
      Configuration.setParameter("SOURCE_NAME", img_name);

      try {
        // get local features
        DS_RgbColorRegions rst = (DS_RgbColorRegions)DIU.in(
            DIU.INPUT_XML,
            "DPU_ColorRegion",
            "DS_RgbColorRegions",
            Class.forName("yi.iu.processor.color.reg.DS_RgbColorRegions"));
        // get global histogram
        GlobalHistogram g = rst.getGlobalized();
        PrintStream out = new PrintStream (
          new FileOutputStream(img_name + ".DS_GaborTextureRegions." +
                               rst.GLOBALIZE_SUFFIX[rst.GLOBALIZE_METHOD]));
        g.output(out);
        out.close();
      }
      catch (Exception e) {
        e.printStackTrace();
      }
      System.exit(0);
    }
  }




  /**
   * Use the color of the color regions to draw the regions in the given image
   * pixel array
   * @param pixels
   * @param width
   * @param height
   * @return
   */
  public int[] getDemo(int[] pixels, int width, int height) {
    int[] colors = new int[m_regions.size()];
    Region[] regions = new Region[m_regions.size()];
    for (int i=0;i<m_regions.size();i++) {
      RgbColorRegion r = (RgbColorRegion)m_regions.elementAt(i);
      colors[i] = 0xff000000 | (r.r << 16) | (r.g << 8) | r.b;
      regions[i] = r.m_region;
    }
    return Region.drawRegions(null, new Dimension(width, height), regions, colors);

  }

  public Globalizable spatialRelations(Regionalizable r) throws GlobalizationException {
    LocalFeatureSet s = new LocalFeatureSet("yi.iu.ft.local.RegionSpatialRelation");
    Iterator i = listRegions();
    while (i.hasNext()) {
      Iterator j = r.listRegions();
      RegionSpatialRelation f = new RegionSpatialRelation((RegionBased)i.next(), (RegionBased)j.next());
      s.addInstance(f);
    }
    return s;
  }

  /**
   * Output the regions (one region per image).  The region's boundary will be
   * marked with the given boundary color (the boundary will be outside the
   * region to show)
   * @param file_name the root file name
   * @param pixels the pixels to display
   * @param size_threshold the minimum size of the region to output
   * @param background_c the background color
   * @param boundary_c the boundary color
   */
  public void demoRegions(String file_name, int[] pixels, int size_threshold, int background_c, int boundary_c) {
    int max_label = yi.stat.StatUtil.maximum(m_labels);
    int min_label = yi.stat.StatUtil.minimum(m_labels);

    // the filtered image
    int[] filtered = new int[m_labels.length];
    for (int i=min_label; i<= max_label; i++) {
      int count = 0;
      for (int j=0; j<m_labels.length; j++) {
        filtered[j] = (m_labels[j] == i) ? pixels[j] : background_c;
        count ++;
      }
      // output the result
      if (count >= size_threshold) {
        int[] boundaried = boundaryRegion(filtered, m_imgWidth, m_imgHeight, background_c, boundary_c);
        yi.util.io.OutputResult.outputColorImage(
          file_name + ".region." + i + ".ppm", boundaried, m_imgWidth, m_imgHeight);
      }
    }
  }

  /**
   * Mark out a region with the boundary color (boundary is in the region)
   * @param pixels the image to mark
   * @param width
   * @param height
   * @param region_color the color of the region to mark
   * @param boundary_color the boundary color
   * @return the marked image
   */
  int[] boundaryRegion(int[] pixels, int width, int height, int region_color, int boundary_color) {
    int count = 0;
    int[] boundaried  = new int[pixels.length];
    for (int h=0; h<height; h++) {
      for (int w=0; w<width; w++, count++) {
        if ((pixels[count] == region_color) &&
            ( h > 0 && pixels[count - width] != region_color ||
              h < height - 1 && pixels[count + width] != region_color ||
              w > 0 && pixels[count - 1] != region_color ||
              w < width - 1 && pixels[count + 1] != region_color)) {
                boundaried[count] = boundary_color;
        } else {
          boundaried[count] = pixels[count];
        }
      }
    }
    return boundaried;
  }

  /**
   * Calculate mean colors for regions
   * return rgb colors
   *
   * @param rgb
   * @param labels
   * @param maxLabel
   * @return
   */
  public static int[][] getMeanColors(int[] rgb, int[] labels, int maxLabel)
  {
    IntArrayCumulator[] cumulators = new IntArrayCumulator[maxLabel+1];
    for (int i=0; i<=maxLabel; i++) cumulators[i] = new IntArrayCumulator();


    // calculate the sum and count for every region
    int[] colors = new int[3];
    for (int i=0; i<rgb.length; i++) {
      if (labels[i]<0) continue;
        colors[0] = (rgb[i]>>16) & 0xff;
        colors[1] = (rgb[i]>> 8) & 0xff;
        colors[2] = rgb[i] & 0xff;
        cumulators[labels[i]].add(colors);
    }

    int[][] means = new int[maxLabel+1][];
    for (int i=0; i<=maxLabel; i++) {
      means[i] = cumulators[i].getMeans ();
      if (means[i].length == 0) { // => the count = 0
        means[i] = new int[3];
      }
    }

    return means;
  }

  final static int BACKGROUND = 0;

}
