package yi.iu.processor.texture.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.Configuration;
import yi.util.io.ReadArgu;
import yi.iu.processor.ort.*;
import yi.iu.processor.color.reg.RgbColorRegion;
import yi.iu.region.Region;
import yi.util.config.*;
import java.io.*;
import yi.stat.hist.*;

public class DS_GaborTextureRegions implements java.io.Serializable, Csvable, Configurable, FeatureQuantizer{

  int m_imgWidth, m_imgHeight;
  /**
   * For texture processing, the image is scaled down and m_scale is the scale
   * rate
   */
  double m_scale = 1.0;
  /**
   * Gabor texture regions.
   */
  GaborTextureRegion[] m_gaborTextureRegions;
  /**
   * Gabor texture segmentation result: the labels for each pixel
   */
  int[] m_labels;

  public int GLOBALIZE_FTH1 = 1;

  public int GLOBALIZE_METHOD = GLOBALIZE_FTH1;

  public boolean REDO_QUANTIZE = false;

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

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

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

  public DS_GaborTextureRegions(int w, int h, double scale, GaborTextureRegion[] regions) {
    this();
    m_imgWidth  = w;
    m_imgHeight = h;
    m_scale = scale;
    m_gaborTextureRegions = regions;
  }

  public DS_GaborTextureRegions(int w, int h, double scale, GaborTextureRegion[] regions, int[] labels) {
    this(w,h,scale,regions);
    m_labels = labels;
  }

  public int getRegionNum() {
    return m_gaborTextureRegions.length;
  }

  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 double getScale() {
    return m_scale;
  }

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

  public GaborTextureRegion[] getGaborTextureRegions() {
    return m_gaborTextureRegions;
  }

  public void setGaborTextureRegions(GaborTextureRegion[] regions) {
    m_gaborTextureRegions = regions;
  }

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

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

  /**
   * List the regions (not RgbColorRegions)
   * @return
   */
  public Region[] getRegions() {
    Region[] regions = new Region[m_gaborTextureRegions.length];
    for (int i = 0; i < m_gaborTextureRegions.length; i++) {
        GaborTextureRegion l = (GaborTextureRegion)m_gaborTextureRegions[i];
        regions[i] = l.m_region;
    }
    return regions;
  }

  public int numRegions() {
    return m_gaborTextureRegions.length;
  }


  public double[][] toCsv() {
    double[][] csv = new double[m_gaborTextureRegions.length][];
    for (int i=0; i<m_gaborTextureRegions.length; i++) {
      GaborTextureRegion r = m_gaborTextureRegions[i];
      double[][] reg_csv = r.toCsv();
      csv[i] = reg_csv[0];
    }
    return csv;
  }

  public static DS_GaborTextureRegions fromCsv(double[][] csv) {
    GaborTextureRegion[] regions = new GaborTextureRegion[csv.length];
    for (int i=0; i<csv.length; i++) {
      regions[i] = GaborTextureRegion.fromCsv(csv[i]);
    }
    return new DS_GaborTextureRegions(-1, -1, -1, regions);
  }

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

  public static int FTH1_BIN_NUM_EACH_ATTR = 5;
  public static int GABOR_NUM = 30;
  public static int FTH1_BIN_NUM = FTH1_BIN_NUM_EACH_ATTR * GABOR_NUM;

  /**
   * Quantize the data useing logarithmic scale.  For example, if base = 2, then
   * the data value ranges are: [0, 1), [1, 2), [2, 4), [4, 8), etc.  The upper
   * limit of range-i is 2^i
   * @param data
   * @param base
   * @return
   */
  public static int logarithmicQuantize(double data, double base) {
    if (data <= 0) return 0;
    int level = (int)Math.floor(Math.log(data) / Math.log(base)) + 1;
    if (level < 0) level = 0;
    return level;
  }

  public GlobalHistogram getGlobalized_FTH1(){

//    // output the feature for considering the quantiz
//    for (int i = 0; i<m_gaborTextureRegions.length; i++) {
//      GaborTextureRegion region = m_gaborTextureRegions[i];
//      double[] gabor_data = region.getGabor();
//      for (int j=0; j<gabor_data.length; j++) System.out.print("\t" + gabor_data[j]);
//      System.out.println();
//    }

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

    // construct the histogram
    for (int i = 0; i<m_gaborTextureRegions.length; i++) {
      GaborTextureRegion region = m_gaborTextureRegions[i];
      double[] gabor_data = region.getGabor();
      for (int j=0; j<gabor_data.length; j++) {
        int level = logarithmicQuantize(gabor_data[j], 2);
        if (level >= FTH1_BIN_NUM_EACH_ATTR) {
          level = FTH1_BIN_NUM_EACH_ATTR - 1;
        }
        int bin_index = j * FTH1_BIN_NUM_EACH_ATTR + level;
        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);
      }

      img_name = img_name + ColorGuidedTextureSeg.ResizeFirst.RESIZED_FILE_SUFFIX +
        ColorGuidedTextureSeg.ResizeFirst.IMAGE_RESIZED;

      Configuration.setParameter("SOURCE_NAME", img_name);

      try {
        // get local features
        DS_GaborTextureRegions rst = (DS_GaborTextureRegions)DIU.in(
            DIU.INPUT_XML,
            "ColorGuidedTextureSeg",
            "DS_GaborTextureRegions",
            Class.forName("yi.iu.processor.texture.reg.DS_GaborTextureRegions"));
        // 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);
    }
  }
 */
  /**
   * Attribute number of each ORTLine: 4 (centroid_x, centroid_y, orientation,
   * length)
   * @return
   */
  public int getAttributeNum() {
    return ATTRIBUTE_NUM;
  }

  /**
   * Return the bin number of each attribute after quantization
   * @return
   */
  public int getQuantizedBinNum(int attr_index) {
    return QUANTIZED_BIN_NUMS[attr_index];
  }

  /**
   * Return the total bin number of all attributes after quantization
   * @return
   */
  public int getQuantizedBinNum() {
    return (int)yi.stat.StatUtil.sum(QUANTIZED_BIN_NUMS);
  }

  /**
   * Return the total bin number of all attributes after quantization
   * @return
   */
  public int[] getQuantizedBinNums() {
    return QUANTIZED_BIN_NUMS;
  }

  /**
   * Return the bin name of each attribute after quantization
   * @return
   */
  public String[] getQuantizedBinName(int attr_index) {
    int num = getQuantizedBinNum(attr_index);
    String[] names = new String[num];
    String prefix = QUANTIZED_BIN_NAME_PREFIX[attr_index];
    for (int i=0; i<num; i++) names[i] = prefix + '_' + i;
    return names;
  }

  /**
   * Return the bin name of all attributes after quantization
   * @return
   */
  public String[] getQuantizedBinName() {
    String[] names = new String[getQuantizedBinNum()];
    int attr_num = getAttributeNum();
    int count = 0;
    for (int i=0; i<attr_num; i++) {
      String[] attr_names = getQuantizedBinName(i);
      for (int j=0; j<attr_names.length; j++) names[count++] = attr_names[j];
    }
    return names;
  }

  /**
   * Return the feature quantizer name
   * @return
   */
  public String getQuantizerName() {
    return QUANTIZER_NAME;
  }

  public int[] QUANTIZED_BIN_NUMS = {
    5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,
    5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,
    5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,5 ,
    4 ,4 ,4 ,4 ,9};
  public int ATTRIBUTE_NUM = QUANTIZED_BIN_NUMS.length;
  public String QUANTIZER_NAME = "texture";
  public String[] QUANTIZED_BIN_NAME_PREFIX = {
    "g01", "g02", "g03", "g04", "g05", "g06", "g07", "g08", "g09", "g10",
    "g11", "g12", "g13", "g14", "g15", "g16", "g17", "g18", "g19", "g20",
    "g21", "g22", "g23", "g24", "g25", "g26", "g27", "g28", "g29", "g30",
    "x", "y", "l", "o", "s"};

  QuantizeTemplate m_quantizeTemplate = new QuantizeTemplate(
    // attribute num
    ATTRIBUTE_NUM,
    // quantized bin numbers
    QUANTIZED_BIN_NUMS,
    // bin name prefixs
    QUANTIZED_BIN_NAME_PREFIX,
    // quantizer short name
    QUANTIZER_NAME,
    // quantizer id name: quan
    "quan"
  );

  public QuantizeTemplate getQuantizeTemplate(String quantizer_id) {
    if (quantizer_id.compareToIgnoreCase("quan") == 0) {
      return m_quantizeTemplate;
    }
    System.err.println("quantize method is not defined in DS_GaborTextureRegions: " + quantizer_id);
    return null;
  }

  public QuantizedFeature getQuantized(String quantizer_id) {
    if (quantizer_id.compareToIgnoreCase("quan") == 0) {
      return getQuantized();
    }
    System.err.println("quantize method is not defined in DS_GaborTextureRegions: " + quantizer_id);
    return null;
  }

  /**
   * Return the quantized feature instances for one case.
   * @return
   */
  public QuantizedFeature getQuantized() {
    if (m_gaborTextureRegions == null) return new QuantizedFeature(0, ATTRIBUTE_NUM);

    Quantizer gaborQuantizer = new yi.stat.hist.LogarithmicQuantizer(2, QUANTIZED_BIN_NUMS[0]);

    Quantizer[] quantizers = new Quantizer[ATTRIBUTE_NUM];
    for (int i=0; i<30; i++) quantizers[i] = gaborQuantizer;  // for Gabor data
    quantizers[30] = new LinearQuantizer(
      0, m_imgWidth/QUANTIZED_BIN_NUMS[30], QUANTIZED_BIN_NUMS[30], true); // for centroid_x;
    quantizers[31] = new LinearQuantizer(
      0, m_imgHeight/QUANTIZED_BIN_NUMS[31], QUANTIZED_BIN_NUMS[31], true); // for centroid_y;
    quantizers[32] = new LinearQuantizer(
      0, Math.max(m_imgHeight, m_imgWidth)/QUANTIZED_BIN_NUMS[32], QUANTIZED_BIN_NUMS[32], true); // for elongation;
    quantizers[33] = new LinearQuantizer(
      -Math.PI/8, Math.PI/QUANTIZED_BIN_NUMS[33], QUANTIZED_BIN_NUMS[33], true, true); // for direction;
    quantizers[34] = new LinearQuantizer(
      0, m_imgHeight * m_imgWidth/QUANTIZED_BIN_NUMS[34], QUANTIZED_BIN_NUMS[34], true); // for size;

    // quantize each line
    QuantizedFeature quantized = new QuantizedFeature(m_gaborTextureRegions.length, ATTRIBUTE_NUM);
    for (int i = 0; i<m_gaborTextureRegions.length; i++) {
      GaborTextureRegion region = m_gaborTextureRegions[i];
      double[] gabor_data = region.getGabor();
      double[] values = new double[ATTRIBUTE_NUM];
      Region r = region.getRegion();
      for (int j=0; j<gabor_data.length; j++) values[j] = gabor_data[j];
      values[30] = r.m_centroidX;
      values[31] = r.m_centroidY;
      values[32] = r.m_majLength;
      values[33] = r.m_majAngle;
      values[34] = r.m_size;
      for (int j=0; j<values.length; j++) {
        quantized.setQuantized(i, j, quantizers[j].quantize(values[j]));
      }
    }
    return quantized;
  }

  public static class Quantize {
    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) {

        img_name = img_name + ".s256";

        Configuration.setParameter("SOURCE_NAME", img_name);

        String globalization_file_name = img_name + ".DS_GaborTextureRegions." + "quan";

        DS_GaborTextureRegions dummy = new DS_GaborTextureRegions();

        // check whether need redo
        if (!dummy.REDO_QUANTIZE) {
          File globalization_file = new File(globalization_file_name);
          if (globalization_file.exists()) {
            System.out.println(globalization_file_name + " exists.  Skip");
            System.exit(0);
          }
        }

        try {
          // get local features
          DS_GaborTextureRegions rst = (DS_GaborTextureRegions)DIU.in(
            DIU.INPUT_XML,
            "ColorGuidedTextureSeg",
            "DS_GaborTextureRegions",
            Class.forName("yi.iu.processor.texture.reg.DS_GaborTextureRegions"));
          // get global histogram
          QuantizedFeature quan = rst.getQuantized();

          ObjectOutputStream out = new ObjectOutputStream(
            new BufferedOutputStream(new FileOutputStream(globalization_file_name)));
          out.writeObject(quan);
          out.close();

//          System.out.println(quan);
        }
        catch (Exception e) {
          System.err.println("broken in generating " + globalization_file_name);
          e.printStackTrace();
        }
      }
      System.exit(0);
    }
  }
}
