package yi.iu.processor.clc;

import java.awt.*;
import java.util.*;

import yi.ai.cluster.*;
import yi.ds.*;
import yi.iu.processor.ort.*;
import yi.util.config.*;
import yi.util.io.*;

/**
 * Construct Orientation Consistent Line Cluster.
 */
public class CLC_Orientation extends CLC_Clustering
{

  /**
   * The number of the bins of the orientation histogram
   */
  public int ORIENT_HIST_BIN_NUM = 180;

  /**
   * When doing histogram analysis (histogram based clustering), the histogram
   * will be first smoothed by Gussian.  This is the sigma used in Gussian
   * smoothing.
   */
  public double HIST_SMOOTH_GAUSSIAN_SIGMA = 4;

  /**
   * When finding the histogram peaks to clustering the data, we only pick up
   * the peaks having high quality.  This is the quality threshold
   */
  public double HIST_PEAK_QUALITY_THRESHOLD = 0;


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

  public boolean DEMO_ROUGHLY_ORIENTATION_CLC = false;
  public boolean DEMO_ORIENTATION_CLC = true;
  public boolean DEMO_ORIENTATION_CLC_REGION = false;

  /**
   * Do roughly orientation clustering first, then use perspective information
   * to filter the results
   */
  public static int TWO_STEP_APPROACH = 2;
  /**
   * Use perspective information to pick up the consistent line clusters, and
   * then do the same thing in the remaining lines until no line left
   */
  public static int RECURSIVE_APPROACH = 1;
  public int CLUSTERING_APPROACH = RECURSIVE_APPROACH;


  public String[] getParameterNames() {
    return super.getParameterNames(new String[] {
      "DEMO_ROUGHLY_ORIENTATION_CLC",
      "DEMO_ORIENTATION_CLC",
      "DEMO_ORIENTATION_CLC_REGION",
      "CLUSTERING_APPROACH",
    });
  }

  /**
   * Construct the orientation histogram of given lines
   * @param lines
   * @return
   */
  double[] constructOrientationHist(LineClusterSet lines) {

    // the histogram
    double[] hist = new double[ORIENT_HIST_BIN_NUM];

    Iterator i = lines.iterator();
    while (i.hasNext()) {
      ORTLine l = (ORTLine)i.next();

      // quatilize the angle of the line
      int angel =orientationHistBinNum(l);

      hist[angel] += CLCUtil.lineWeight(l.Length);
    }
    return hist;
  }


  /**
   * Return the bin number for the given line in the orientation histogram
   * @param l
   * @return
   */
  int orientationHistBinNum(ORTLine l) {

    int angel= (int)Math.round(l.Theta * ORIENT_HIST_BIN_NUM / Math.PI);
    if (angel < 0) angel += ORIENT_HIST_BIN_NUM;
    else if (angel >= ORIENT_HIST_BIN_NUM) angel -= ORIENT_HIST_BIN_NUM;

    return angel;
  }

  static int output_counter = 0;

  /**
   * Clustering the lines using their orientation information.  A histogram of
   * the orientation of the lines are constructed and histogram shape analysis
   * is used to cluster the lines to different group having similar orientation.
   * A further perspective information based filtering is performed to make the
   * lines in a cluster either parallel to each other or converging to a point.
   * @param lines
   * @param rgb
   * @param width
   * @param height
   * @return
   */
  public LineClusterSets getClusters(LineClusterSet lines, int[] rgb, int width, int height) {

    if (CLUSTERING_APPROACH == RECURSIVE_APPROACH) {
      return getClusters2(lines, rgb, width, height);
    }

    // new line cluster sets
    LineClusterSets line_clusters = new LineClusterSets(width, height);

    // construct the histogram of the orientation of the lines
    double[] hist = constructOrientationHist(lines);
    OneDClusters clusters = OneDHistBasedCluster.localMaximaGuidedHistClustering(
      hist, HIST_SMOOTH_GAUSSIAN_SIGMA, HIST_PEAK_QUALITY_THRESHOLD, true);
    DataRange[] ranges = clusters.getClusters();

    Iterator i = lines.iterator();
    while (i.hasNext()) {
      ORTLine l = (ORTLine)i.next();
      int angle = orientationHistBinNum(l);
      // which cluster the line belongs to
      int label = clusters.getLabel(angle);
      if (label < 0) continue;  // it doesn't belong to any cluster
      // put it in
      line_clusters.add(ranges[label], l);
    }

    // filter the result
    line_clusters.filter(
      CLCConfig.getMinQualifiedLineLen(),
      CLCConfig.getMinQualifiedCLCLineLenSum(width, height));

    // inherit the properties from the parent (clustered from)
    line_clusters.inheritProperties(lines);

    // demo the consistent line clusters before roughly orientation clustering
    if (DEMO_ROUGHLY_ORIENTATION_CLC) {
      int[] lineimg = lines.demo();
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      OutputResult.outputGIFImage(img_name + ".roughly_orientation_clcs.a", lineimg, width, height, true);
    }

    // demo the roughly orientation consistent line clusters
    if (DEMO_ROUGHLY_ORIENTATION_CLC) {
      int[] lineimg = line_clusters.demo(width, height);
      String img_name = Configuration.getStringParameter("SOURCE_NAME", "unknown");
      OutputResult.outputGIFImage(img_name + ".roughly_orientation_clcs.b", lineimg, width, height, true);
    }

    // filter the clusters using perspective information
    i = line_clusters.keySet().iterator();
    while (i.hasNext()) {
      Object key = i.next();
      LineClusterSet line_cluster = (LineClusterSet)line_clusters.get(key);
      line_clusters.put(key, line_cluster.perspectiveConsistentLines());
    }

    // filter the result
    line_clusters.filter(
      CLCConfig.getMinQualifiedLineLen(),
      CLCConfig.getMinQualifiedCLCLineLenSum(width, height));

    return line_clusters;
  }

  public LineClusterSets getClusters2(LineClusterSet lines, int[] rgb, int width, int height) {

    // new line cluster sets
    LineClusterSets line_clusters = new LineClusterSets(width, height);

    // filter the clusters using perspective information
    while (lines.size() > 0) {
      LineClusterSet consistent = lines.perspectiveConsistentLines();
      lines.removeAll(consistent);
      line_clusters.put(consistent, consistent);
    }

    // filter the result
    line_clusters.filter(
        CLCConfig.getMinQualifiedLineLen(),
        CLCConfig.getMinQualifiedCLCLineLenSum(width, height));

    // inherit the properties from the parent (clustered from)
    line_clusters.inheritProperties(lines);

    return line_clusters;
  }

  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);
    }

    // read in image
    Image image = null;
    Dimension d = new Dimension();
    int[] pixels = null;
    try {
      image = yi.util.io.ImageReader.readImageFile(img_name);
      pixels = yi.util.io.ImageReader.getPixels(image, d);
    }
    catch (java.io.IOException e) {
      System.err.println("Can't read image " + img_name);
      e.printStackTrace();
      System.exit(2);
    }

    // process
    Configuration.setParameter("SOURCE_NAME", img_name);
    long startTime = System.currentTimeMillis();

    //find all the lines in an image
    yi.iu.processor.ort.DPU_Fex dp = new DPU_Fex();
    Vector[] segs= dp.getLinesAndArcs(pixels, d.width, d.height);
    Vector lines= segs[0];
    LineClusterSet lcs = new LineClusterSet(lines, d.width, d.height);

    // clustering by color
    CLC_Color clc_color = new CLC_Color();
    LineClusterSets color_lcss = clc_color.getReusableClusteres(lcs, pixels, d.width, d.height);

    // clustering by orientation
    CLC_Orientation clc_orientation = new CLC_Orientation();
    LineClusterSets lcss = clc_orientation.getReusableClusteres(color_lcss, pixels, d.width, d.height);

    long endTime = System.currentTimeMillis();
    System.out.println("running time: " + (endTime-startTime));

//    // illustration each line cluster
//    {
//      Iterator i= lcss.values().iterator();
//      int label = 1;
//      while (i.hasNext()) {
//        LineClusterSet line_cluster = (LineClusterSet)i.next();
//        ColorPair cp = line_cluster.getColorPair();
//        // draw
//        int[] lineimg= yi.util.ImageUtil.makeBackground(pixels);
//        line_cluster.draw(lineimg, d.width, d.height, 0xffffffff);
//        // bounding box
//        LineClusterSet box = line_cluster.getPerspectiveBasedBoundingBox();
//        box.draw(lineimg, d.width, d.height, 0xffff0000);
//        // the perspective coordinates
//        ORTLine base_line = line_cluster.getBaseLine(true);
//        ORTLine overlapping_line = line_cluster.getOverlappingLine(true);
//        DrawUtil.ort2image(lineimg, d, base_line, 0xff00ff00, 1);
//        DrawUtil.ort2image(lineimg, d, overlapping_line, 0xff0000ff, 1);
//
//        // color spectrum
//        int[] colors = new int[] {cp.x, cp.y};
//        int color_w = 40, color_h = 50;
//        int[] spectrum = yi.iu.processor.color.ColorUtil.createSpectrum(colors, color_w, color_h);
//        for (int y=0; y<color_h; y++) {
//          for (int x=0;x<color_w*colors.length; x++) {
//            lineimg[y*d.width+x] = spectrum[y*color_w*colors.length+x];
//          }
//        }
//        // make a simple color index
//        OutputResult.outputColorImage(img_name + ".orientation_clc." + label + ".ppm", lineimg, d.width, d.height);
//
//        // output the region
//        lineimg= yi.util.ImageUtil.makeBackground(pixels);
//        Region r = line_cluster.getRegion();
//        Region.drawRegions(lineimg, d, r, 0xffffffff);
//        OutputResult.outputColorImage(img_name + ".orientation_clc" + label + ".region.ppm", lineimg, d.width, d.height);
//        label ++;
//      }
//    }


    // demo the line clusters
    if (clc_orientation.DEMO_ORIENTATION_CLC) {
      int[] lineimg = lcss.demo(d.width, d.height);
      OutputResult.outputGIFImage(img_name + ".orientation_clcs.gif", lineimg, d.width, d.height);
    }

//    // demo the line cluster regions
//    {
//      Iterator i= lcss.values().iterator();
//      int[] lineimg= new int[pixels.length];
//      int label = 1;
//      while (i.hasNext()) {
//        LineClusterSet line_cluster = (LineClusterSet)i.next();
//        // draw region
//        Region r = line_cluster.getRegion();
//        Region.drawRegions(lineimg, d, r, label++);
//      }
//      lineimg = yi.util.ImageUtil.makeNewImageFromLabel(lineimg);
//      OutputResult.outputColorImage(img_name + ".clc_regions.", lineimg, d.width, d.height, true);
//    }

    // demo the line cluster regions (only the biggest)
    if (clc_orientation.DEMO_ORIENTATION_CLC_REGION) {
      int[] lineimg = lcss.demoRegions(d.width, d.height);
      OutputResult.outputGIFImage(img_name + ".orientation_clcs_regions.gif", lineimg, d.width, d.height);
    }

    System.exit(0);
  }
}