package yi.iu.processor.color.reg.merge;

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

/**
 * TwoWayNeighborMerger merges the regions if they are taking the labels as
 * specified.  It is called two way because if a pixel is masked, then it
 * wouldn't be merged to others and others wouldn't be merged to it.
 */

public class TwoWayNeighborMerger implements NeighborMerger {

  /**
   * Constructs the  Partially neighour matrix.  It is called "two
   * way" because if the neighour of a pixel is masked (not considered) then that
   * relationship will not be counted for neither of labels.  For example, if the
   * pixels of one label is surrounded by only masked pixels, then all its
   * neighour count will be 0.  The matrix will be symmetrical
   */
  public int[][] getNeighborMatrix(int[] labels, Dimension d, int[] mask) {
    int width = d.width, height = d.height;

    // init the neighour matrix
    int max_label = yi.stat.Util.maximum(labels);
    int[][] neighour_matrix = new int[max_label + 1][max_label + 1];

    // only consider 4-connection neighours
    for (int y=0; y<height-1; y++) {
      for (int x=0; x<width-1; x++) {
        if (mask[y*width + x] == 1) continue;
        int label = labels[y*width + x];
        // to the right
        if (mask[y*width + x + 1] != 1) {
          int right_label = labels[y*width + x + 1];
          // put into the neighour matrix
          putIntoNeighourMatrix(neighour_matrix, label, right_label);
        }
        // to the bottom
        if (mask[(y+1)*width + x] != 1) {
          int down_label = labels[(y+1)*width + x];
          // put into the neighour matrix
          putIntoNeighourMatrix(neighour_matrix, label, down_label);
        }
      }
    }

    return neighour_matrix;
  }

  private void putIntoNeighourMatrix(int[][] matrix, int label1, int label2) {
    matrix[label1][label2] ++;
    matrix[label2][label1] ++;
  }

  public void mergeLabels(int[] cluster_map, Dimension d, int from, int to, int[] cc_labels, int[] mask) {
    int width = d.width, height = d.height;

    HashSet cc_labels_to_merge = new HashSet();

    // only consider 4-connection neighours
    for (int y=0; y<height-1; y++) {
      for (int x=0; x<width-1; x++) {
        if (mask[y*width + x] == 1) continue;
        int label = cluster_map[y*width + x];
        // put into the neighour matrix
        if (mask[y*width + x + 1] != 1) {
          int right_label = cluster_map[y*width + x + 1];
          if (label == from && right_label == to) cc_labels_to_merge.add(new Integer(cc_labels[y*width + x]));
          else if (label == to && right_label == from) cc_labels_to_merge.add(new Integer(cc_labels[y*width + x + 1]));
        }
        if (mask[(y+1)*width + x] != 1) {
          int down_label = cluster_map[(y+1)*width + x];
          if (label == from && down_label == to) cc_labels_to_merge.add(new Integer(cc_labels[y*width + x]));
          else if (label == to && down_label == from) cc_labels_to_merge.add(new Integer(cc_labels[(y+1)*width + x]));
        }
      }
    }

    for (int i=0; i<cluster_map.length; i++) {
      if (cluster_map[i] != from) continue;
      if (cc_labels_to_merge.contains(new Integer(cc_labels[i]))) cluster_map[i] = to;
    }
  }

}