/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */
          
using System;
using System.Drawing;

namespace HistogramTest
{
    /// <summary>
    /// Compares 2 images using normalized histogram equalization 
    /// compare method, see: http://en.wikipedia.org/wiki/Histogram_equalization    
    /// </summary>
    class ImageHistogram
    {
        Bitmap image = null;

        int[] mapR = null;
        int[] mapG = null;
        int[] mapB = null;

        double[] mapNormalR = null;
        double[] mapNormalG = null;
        double[] mapNormalB = null;

        protected Color[] histogramEqualizationTable = null;

        public ImageHistogram(Bitmap bitmapIn)
        {
            image = bitmapIn;

            calculateHistogram();
            calculateHistogramEqualizationTable();
        }

        public override bool Equals(Object other)
        {
            ImageHistogram ih = other as ImageHistogram;

            if (ih == null)
                return false;

            return (this.CalculateDifference(ih) == 0);
        }

        public override int GetHashCode()
        {
            if (image == null)
                return 0;

            return image.GetHashCode();
        }

        public double CalculateConfidencePercent(ImageHistogram other)
        {
            if (image == null)
                return 0.0;
            
            int difference = CalculateDifference(other);

            int maxPossibleError = 256 * 256;

            double confidence = 1.0 - ((double)difference / (double)maxPossibleError);

            return confidence;
        }

        public int CalculateDifference(ImageHistogram other)
        {
            if ((this.image == null) || (other.image == null))
                return 1000000;
            
            byte valueR;
            byte valueG;
            byte valueB;

            byte valueROther;
            byte valueGOther;
            byte valueBOther;

            int totalDistance = 0;

            for (int i = 0; i < 256; i++)
            {
                Color color = histogramEqualizationTable[i];

                valueR = color.R;
                valueG = color.G;
                valueB = color.B;

                Color colorOther = other.histogramEqualizationTable[i];
                valueROther = colorOther.R;
                valueGOther = colorOther.G;
                valueBOther = colorOther.B;

                int deltaR = Math.Abs((int)(valueR - valueROther));
                int deltaG = Math.Abs((int)(valueG - valueGOther));
                int deltaB = Math.Abs((int)(valueB - valueBOther));

                totalDistance += deltaR;
                totalDistance += deltaG;
                totalDistance += deltaB;
            }

            return totalDistance;
        }

        private void calculateHistogram()
        {
            byte valueR;
            byte valueG;
            byte valueB;
            byte valueAlpha;

            mapR = new int[256];
            mapG = new int[256];
            mapB = new int[256];

            for (int y = 0; y < image.Height; y++)
            {
                for (int x = 0; x < image.Width; x++)
                {
                    Color pixelColor = image.GetPixel(x, y);
                    valueR = pixelColor.R;
                    valueG = pixelColor.G;
                    valueB = pixelColor.B;
                    valueAlpha = pixelColor.A;

                    // Implementation Specific Change to Standard Histogram Compare Here:
                    if (((valueR == 255) && (valueG == 255) && (valueB == 255)) ||
                        (valueAlpha == 0))
                        // TRICKY: Don't count whites or transparents (background colors in this case)
                        // NOTE: this will affect the correctness if an image really has white/transparent in it
                        // since these are ignored
                        continue;

                    mapR[valueR] = mapR[valueR] + 1;
                    mapG[valueG] = mapG[valueG] + 1;
                    mapB[valueB] = mapB[valueB] + 1;
                }
            }

            // Normalize the histogram
            double size = (double)(image.Height * image.Width);

            mapNormalR = new double[256];
            mapNormalG = new double[256];
            mapNormalB = new double[256];

            for (int i = 0; i < 256; i++)
            {
                mapNormalR[i] = mapR[i] / size;
                mapNormalG[i] = mapG[i] / size;
                mapNormalB[i] = mapB[i] / size;
            }

        }

        private void calculateHistogramEqualizationTable()
        {
            histogramEqualizationTable =  new Color[256];

            double sumR = 0.0;
            double sumG = 0.0;
            double sumB = 0.0;

            // Compute the CDF
            for (int i = 0; i < 256; i++)
            {
                sumR += mapNormalR[i];
                sumG += mapNormalG[i];
                sumB += mapNormalB[i];

                histogramEqualizationTable[i] =
                 Color.FromArgb((int)(sumR * 255.0 + 0.5),
                      (int)(sumG * 255.0 + 0.5),
                      (int)(sumB * 255.0 + 0.5));
            }
        }

    }

}