package yi.iu.processor.ort;

import java.util.*;
import yi.iu.ft.local.*;
import yi.util.io.input.DIU;
import yi.util.io.output.*;
import yi.util.io.ReadArgu;
import yi.util.config.*;
import java.io.*;
import yi.stat.*;
import yi.stat.hist.*;

public class DS_ORTLines implements java.io.Serializable, Globalizable, Configurable, FeatureQuantizer{
  int m_imgWidth, m_imgHeight;
  Vector m_lines;

  public int GLOBALIZE_METHOD = GlobalHistogram.GLOBALIZE_FTH1;
  /**
   * Even the required globalization was done, redo it.
   */
  public boolean REDO_GLOBALIZATION = false;
  public boolean REDO_QUANTIZE = false;

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

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

  public DS_ORTLines(int w, int h, Vector lines) {
    this();
    m_imgWidth  = w;
    m_imgHeight = h;
    m_lines = lines;
  }

  public int getLineNum() {
    return m_lines.size();
  }

  public void setLineNum() {
  }

  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 getLines() {
    return m_lines;
  }

  public void setLines(Vector lines) {
    m_lines = lines;
  }

  public static String[] getGlobalizedAttributeNames(int globalize_method) {
    if (globalize_method == GlobalHistogram.GLOBALIZE_FTH1) return getGlobalizedAttributeNames_FTH1();
    else if (globalize_method == GlobalHistogram.GLOBALIZE_FTH2) return getGlobalizedAttributeNames_FTH2();
    else if (globalize_method == GlobalHistogram.GLOBALIZE_FTH3) return getGlobalizedAttributeNames_FTH3();
    else if (globalize_method == GlobalHistogram.GLOBALIZE_FTH4) return getGlobalizedAttributeNames_FTH4();
    else if (globalize_method == GlobalHistogram.GLOBALIZE_FTH5) return getGlobalizedAttributeNames_FTH5();
    else return null;
  }

  public GlobalHistogram getGlobalized(){
    if (GLOBALIZE_METHOD == GlobalHistogram.GLOBALIZE_FTH1) return getGlobalized_FTH1();
    else if (GLOBALIZE_METHOD == GlobalHistogram.GLOBALIZE_FTH2) return getGlobalized_FTH2();
    else if (GLOBALIZE_METHOD == GlobalHistogram.GLOBALIZE_FTH3) return getGlobalized_FTH3();
    else if (GLOBALIZE_METHOD == GlobalHistogram.GLOBALIZE_FTH4) return getGlobalized_FTH4();
    else if (GLOBALIZE_METHOD == GlobalHistogram.GLOBALIZE_FTH5) return getGlobalized_FTH5();
    else return null;
  }

  public static int FTH1_BIN_NUM = 72;
  /**
   * Feature globalization:
   * orientation -> bin index,
   * line number -> bin content.
   * @return
   */
  public GlobalHistogram getGlobalized_FTH1(){

    int bin_num = FTH1_BIN_NUM;
    double bin_width = Math.PI/bin_num;

    // store the histogram
    int[] line_num = new int[bin_num];

    // construct the histogram
    Iterator i = m_lines.iterator();
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double direction = line.getDirection();
//      int bin_index = (int)(direction / Math.PI * FTH1_BIN_NUM);
      int bin_index = yi.stat.hist.HistUtil.linearBinIndex(direction, 0, bin_width, bin_num, true);
      // direction = [0, Math.PI)
      line_num[bin_index] ++;
    }

    // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<bin_num; j++) {
      global_hist.add(new Integer(line_num[j]));
    }
    return global_hist;
  }

  public static String[] getGlobalizedAttributeNames_FTH1() {
    int bin_num = FTH1_BIN_NUM;
    double bin_width = 180.0/bin_num;
    String[] names = new String[bin_num];
    for (int i=0; i<bin_num; i++) {
      int index = (int)(i * bin_width);
      names[i] = "orientation" + "." + "num" + "." + index;
    }
    return names;
  }

  public static int FTH2_BIN_NUM = 72;
  /**
   * Feature globalization:
   * orientation -> bin index,
   * normalized total line lengthline number -> bin content
   * @return
   */
  public GlobalHistogram getGlobalized_FTH2(){

    int bin_num = FTH2_BIN_NUM;
    double bin_width = Math.PI/FTH2_BIN_NUM;

    // store the histogram
    double[] line_length_sum = new double[bin_num];

    // construct the histogram
    Iterator i = m_lines.iterator();
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double direction = line.getDirection();
      int bin_index = yi.stat.hist.HistUtil.linearBinIndex(direction, 0, bin_width, bin_num, true);
      // direction = [0, Math.PI)
      line_length_sum[bin_index] += line.getLength();
    }

    // for normalization
    double line_length_unit = Math.max(m_imgHeight, m_imgWidth);

    // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<bin_num; j++) {
      global_hist.add(new Double(line_length_sum[j] / line_length_unit));
    }
    return global_hist;
  }

  public static String[] getGlobalizedAttributeNames_FTH2() {
    int bin_num = FTH2_BIN_NUM;
    double bin_width = 180/bin_num;
    String[] names = new String[bin_num];
    for (int i=0; i<bin_num; i++) {
      int index = (int)(i * bin_width);
      names[i] = "orientation" + "." + "len" + "." + index;
    }
    return names;
  }

  public static int FTH3_BIN_NUM = 50;
  /**
   * Feature globalization:
   * line length -> bin index,
   * line number -> bin content.
   * [3,6),[6,9), ... , (total 50 bins)
   * @return
   */
  public GlobalHistogram getGlobalized_FTH3(){

    int bin_num = FTH3_BIN_NUM;
    double bin_width = 3;

    // store the histogram
    int[] hist = new int[bin_num];

    // construct the histogram
    Iterator i = m_lines.iterator();
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double data = line.getLength();
      int bin_index = yi.stat.hist.HistUtil.linearBinIndex(data, 3, bin_width, bin_num, true);
      hist[bin_index] ++;
    }

    // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<bin_num; j++) {
      global_hist.add(new Integer(hist[j]));
    }
    return global_hist;
  }

  public static String[] getGlobalizedAttributeNames_FTH3() {
    int bin_num = FTH3_BIN_NUM;
    double bin_width = 3;
    String[] names = new String[bin_num];
    for (int i=0; i<bin_num; i++) {
      int index = (int)(i * bin_width + 3);
      names[i] = "len" + "." + "num" + "." + index;
    }
    return names;
  }

  public static int FTH4_BIN_NUM = 50;
  /**
   * Feature globalization:
   * line length -> bin index,
   * line number -> normalized line length total.
   * [3,6),[6,9), ... , (total 50 bins)
   * @return
   */
  public GlobalHistogram getGlobalized_FTH4(){

    int bin_num = FTH4_BIN_NUM;
    double bin_width = 3;

    // store the histogram
    double[] hist = new double[bin_num];

    // construct the histogram
    Iterator i = m_lines.iterator();
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double data = line.getLength();
      int bin_index = yi.stat.hist.HistUtil.linearBinIndex(data, 3, bin_width, bin_num, true);
      hist[bin_index] += line.getLength();
    }

    // for normalization
    double unit = Math.max(m_imgHeight, m_imgWidth);

    // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<bin_num; j++) {
      global_hist.add(new Double(hist[j] / unit));
    }
    return global_hist;
  }

  public static String[] getGlobalizedAttributeNames_FTH4() {
    int bin_num = FTH4_BIN_NUM;
    double bin_width = 3;
    String[] names = new String[bin_num];
    for (int i=0; i<bin_num; i++) {
      int index = (int)(i * bin_width + 3);
      names[i] = "len" + "." + "len" + "." + index;
    }
    return names;
  }

  public static int FTH5_BIN_NUM = FTH1_BIN_NUM * FTH3_BIN_NUM;
  /**
   * Feature globalization:
   * (orienation, length) -> bin index,
   * line number -> bin content.
   * @return
   */
  public GlobalHistogram getGlobalized_FTH5(){

    int bin_num = FTH5_BIN_NUM;
    int bin_num_1 = FTH1_BIN_NUM;
    int bin_num_2 = FTH3_BIN_NUM;
    double bin_width_1 = Math.PI/FTH1_BIN_NUM;
    double bin_width_2 = 3;

    // store the histogram
    int[] hist = new int[bin_num];

    // construct the histogram
    Iterator i = m_lines.iterator();
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double direction = line.getDirection();
      int bin_index_1 = yi.stat.hist.HistUtil.linearBinIndex(direction, 0, bin_width_1, bin_num_1, true);
      double data = line.getLength();
      int bin_index_2 = yi.stat.hist.HistUtil.linearBinIndex(data, 3, bin_width_2, bin_num_2, true);
      int bin_index = bin_index_1 * bin_num_2 + bin_index_2;
      hist[bin_index] ++;
    }

   // construct the global histogram
    GlobalHistogram global_hist = new GlobalHistogram();
    for (int j=0; j<bin_num; j++) {
      global_hist.add(new Integer(hist[j]));
    }
    return global_hist;
  }

  public static String[] getGlobalizedAttributeNames_FTH5() {
    int bin_num = FTH5_BIN_NUM;
    int bin_num_1 = FTH1_BIN_NUM;
    int bin_num_2 = FTH3_BIN_NUM;
    String[] names = new String[bin_num];
    int count = 0;
    for (int i=0; i<bin_num_1; i++) {
      for (int j=0; j<bin_num_2; j++) {
        names[count++] = "ori_len" + "." + "num" + "." + (180 * i / bin_num_1) + "." + (3 * j + 3);
      }
    }
    return names;
  }

  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);
      // get the flag about whether to output the attribute name
      String output_attribute_names = ReadArgu.getOption('a', args);
      if (img_name == null && output_attribute_names == null) {
        System.err.println("Usage: -i image_name -a");
        System.exit(1);
      }

      DS_ORTLines dummy = new DS_ORTLines();

      if (output_attribute_names != null) {

        // output the attribute names
        try {
          if (output_attribute_names.compareToIgnoreCase("true") == 0) {
            String attribute_name_file = "DS_ORTLines." +
              GlobalHistogram.GLOBALIZE_SUFFIX[dummy.GLOBALIZE_METHOD] + ".aname";
            String[] attribute_names = DS_ORTLines.getGlobalizedAttributeNames(dummy.GLOBALIZE_METHOD);
            yi.util.io.OutputResult.print(attribute_name_file, attribute_names, false, null);
          }
        }
        catch (Exception e) {
          e.printStackTrace();
        }
      }

      if (img_name != null) {

        Configuration.setParameter("SOURCE_NAME", img_name);

        String globalization_file_name = img_name + ".DS_ORTLines." +
                                 GlobalHistogram.GLOBALIZE_SUFFIX[dummy.GLOBALIZE_METHOD];

        // check whether need redo
        if (!dummy.REDO_GLOBALIZATION) {
          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_ORTLines rst = (DS_ORTLines)DIU.in(
              DIU.INPUT_XML,
              "DPU_Fex",
              "DS_ORTLines",
              Class.forName("yi.iu.processor.ort.DS_ORTLines"));
          // get global histogram
          GlobalHistogram g = rst.getGlobalized();
          PrintStream out = new PrintStream (new FileOutputStream(globalization_file_name));

          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 final static int[] QUANTIZED_BIN_NUMS = {10, 10, 72, 50};
  public final static int ATTRIBUTE_NUM = QUANTIZED_BIN_NUMS.length;
  public final static String QUANTIZER_NAME = "line";
  public final static String[] QUANTIZED_BIN_NAME_PREFIX = {"x", "y", "o", "l"};

  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_ORTLines: " + 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_ORTLines: " + quantizer_id);
    return null;
  }

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

    Quantizer[] quantizers = {
      new LinearQuantizer(-m_imgWidth/2, m_imgWidth/QUANTIZED_BIN_NUMS[0], QUANTIZED_BIN_NUMS[0], true), // for centroid_x;
      new LinearQuantizer(-m_imgHeight/2, m_imgHeight/QUANTIZED_BIN_NUMS[1], QUANTIZED_BIN_NUMS[1], true), // for centroid_y;
      new LinearQuantizer(0, Math.PI/QUANTIZED_BIN_NUMS[2], QUANTIZED_BIN_NUMS[2], true),  // for orientation
      new LinearQuantizer(3, 3, QUANTIZED_BIN_NUMS[3], true),  // for length
    };

    // quantize each line
    QuantizedFeature quantized = new QuantizedFeature(m_lines.size(), ATTRIBUTE_NUM);
    Iterator i = m_lines.iterator();
    int case_index = 0;
    while (i.hasNext()){
      ORTLine line = (ORTLine)i.next();
      double[] values = new double[] {
        line.getCentroidX(),
        line.getCentroidY(),
        line.getDirection(),
        line.getLength(),
      };
      for (int j=0; j<values.length; j++) {
        quantized.setQuantized(case_index, j, quantizers[j].quantize(values[j]));
      }
      case_index ++;
    }
    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) {

        Configuration.setParameter("SOURCE_NAME", img_name);

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

        DS_ORTLines dummy = new DS_ORTLines();

        // 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_ORTLines rst = (DS_ORTLines)DIU.in(
              DIU.INPUT_XML,
              "DPU_Fex",
              "DS_ORTLines",
              Class.forName("yi.iu.processor.ort.DS_ORTLines"));
          // 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);
    }
  }
}