package jogamp.opengl.util.pngj;

/**
 * Manages the writer strategy for selecting the internal png predictor filter
 */
class FilterWriteStrategy {
	private static final int COMPUTE_STATS_EVERY_N_LINES = 8;

	final ImageInfo imgInfo;
	public final FilterType configuredType; // can be negative (fin dout)
	private FilterType currentType; // 0-4
	private int lastRowTested = -1000000;
	// performance of each filter (less is better) (can be negative)
	private final double[] lastSums = new double[5];
	// performance of each filter (less is better) (can be negative)
	private final double[] lastEntropies = new double[5];
	// a priori preference (NONE SUB UP AVERAGE PAETH)
	private double[] preference = new double[] { 1.1, 1.1, 1.1, 1.1, 1.2 };
	private int discoverEachLines = -1;
	private final double[] histogram1 = new double[256];

	FilterWriteStrategy(final ImageInfo imgInfo, final FilterType configuredType) {
		this.imgInfo = imgInfo;
		this.configuredType = configuredType;
		if (configuredType.val < 0) { // first guess
			if ((imgInfo.rows < 8 && imgInfo.cols < 8) || imgInfo.indexed || imgInfo.bitDepth < 8)
				currentType = FilterType.FILTER_NONE;
			else
				currentType = FilterType.FILTER_PAETH;
		} else {
			currentType = configuredType;
		}
		if (configuredType == FilterType.FILTER_AGGRESSIVE)
			discoverEachLines = COMPUTE_STATS_EVERY_N_LINES;
		if (configuredType == FilterType.FILTER_VERYAGGRESSIVE)
			discoverEachLines = 1;
	}

	boolean shouldTestAll(final int rown) {
		if (discoverEachLines > 0 && lastRowTested + discoverEachLines <= rown) {
			currentType = null;
			return true;
		} else
			return false;
	}

	public void setPreference(final double none, final double sub, final double up, final double ave, final double paeth) {
		preference = new double[] { none, sub, up, ave, paeth };
	}

	public boolean computesStatistics() {
		return (discoverEachLines > 0);
	}

	void fillResultsForFilter(final int rown, final FilterType type, final double sum, final int[] histo, final boolean tentative) {
		lastRowTested = rown;
		lastSums[type.val] = sum;
		if (histo != null) {
			double v, alfa, beta, e;
			alfa = rown == 0 ? 0.0 : 0.3;
			beta = 1 - alfa;
			e = 0.0;
			for (int i = 0; i < 256; i++) {
				v = ((double) histo[i]) / imgInfo.cols;
				v = histogram1[i] * alfa + v * beta;
				if (tentative)
					e += v > 0.00000001 ? v * Math.log(v) : 0.0;
				else
					histogram1[i] = v;
			}
			lastEntropies[type.val] = (-e);
		}
	}

	FilterType gimmeFilterType(final int rown, final boolean useEntropy) {
		if (currentType == null) { // get better
			if (rown == 0)
				currentType = FilterType.FILTER_SUB;
			else {
				double bestval = Double.MAX_VALUE;
				double val;
				for (int i = 0; i < 5; i++) {
					val = useEntropy ? lastEntropies[i] : lastSums[i];
					val /= preference[i];
					if (val <= bestval) {
						bestval = val;
						currentType = FilterType.getByVal(i);
					}
				}
			}
		}
		if (configuredType == FilterType.FILTER_CYCLIC) {
			currentType = FilterType.getByVal((currentType.val + 1) % 5);
		}
		return currentType;
	}
}
