
package skyview.util;

import java.util.ArrayList;
import java.util.List;

/**
 * Use this class to rescale a set of byte arrays to have the
 * the same values at two points in the histogram of values.
 * 
 * Generate the HistMatcher with the values (0-1) at which
 * you want the histograms to mach.
 * 
 * Add in the arrays with calls to addList.
 * 
 * Call calc to generate the average values.
 * 
 * call rescale(n) to get a rescaled array.
 * 
 * @author tmcglynn
 */
public class HistoMatcher {
    
    private List<byte[]> arrays = new ArrayList<byte[]>();
    private double[][] matches;
    private double[] avg;
    private double bot, top;
    
    public HistoMatcher(double bot, double top) {
        System.err.println("HistoMatcher:"+bot+"  to "+top);
        if (bot >= top || bot < 0 || top > 1) {
            throw new IllegalArgumentException("Invalid histogram matching points");
        }
        this.bot = bot;
        this.top = top;
    }
    
    public void addList(byte[] pixels) {
        arrays.add(pixels);
    }
    private int[] histogram(byte[] array) {
        int[] count = new int[256];
        for (int i=0; i<array.length;i += 1) {
            int val = array[i];
            if (val < 0) {
                val += 256;                
            }
            count[val] += 1;
        }
        return count;
    }
    
    private double matchPoint(int n, int[] hist, double frac) {
        double have = 0;
        double need = frac*n;
        for (int i=0; i<hist.length; i += 1) {
            double curr = hist[i];
            if (have+curr > need) {
                return i + (need-have)/curr;
            }
            have += curr;
        }
        return hist.length;
    }
        
    
    public void calc() {
        matchPoints();
        avg();
    }
    
    public void matchPoints() {        
        matches = new double[arrays.size()][2];
        for (int i=0; i<arrays.size(); i += 1) {
            byte[] arr = arrays.get(i);
            int[] hist = histogram(arr);
            matches[i][0] = matchPoint(arr.length, hist, bot);
            matches[i][1] = matchPoint(arr.length, hist, top);
        }
    }
    
    public void avg() {
        avg = new double[2];
        for (int j=0; j<2; j += 1) {
            for (int i=0; i<matches.length; i += 1) {
                avg[j] += matches[i][j];
            }
            avg[j] /= arrays.size();
            System.out.println("Looking at avg:"+j+" "+avg[j]);            
        }
    }
    
    public double[] scale(int i, double[] matchPoints, double[] avg) {
        double m = (avg[1]-avg[0])/(matchPoints[1]-matchPoints[0]);
        double b = avg[0] - m*matchPoints[0];
        return new double[]{m,b};
    }
    
    public byte[] rescale(int n) {
        double[] scale = scale(n, matches[n], avg);
        System.err.println("Looking at rescale:"+n+" "+scale[0]+" "+scale[1]);
        return rescale(n, scale[0], scale[1]);
    }
    
    public byte[] rescale(int n, double m, double b) {
        byte[] arr = arrays.get(n);
        for (int i=0; i<arr.length; i += 1) {
            double val = arr[i];
            if (val < 0) {
                val += 256;
            }
            val = m*val + b + 0.5;
            if (val > 255) {
                val = 255;
            } else if (val < 0) {
                val = 0;            
            }
            arr[i] = (byte) val;
        }
        return arr;
    }    
}
