//
//  PRMedian.m
//  PRICE
//
//  Created by Riccardo Mottola on Thu Mar 25 2004.
//  Copyright (c) 2004-2005 Carduus. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it under the terms of the version 2 of the GNU General Public License as published by the Free Software Foundation.
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

#import "PRMedian.h"
#import "PRGrayscaleFilter.h"


@implementation PRMedian

- (PRImage *)medianImage :(PRImage *)srcImage :(enum medianForms)form :(int)size :(BOOL)separable :(PRCProgress *)prPanel
{
    NSBitmapImageRep *srcImageRep;
    PRImage          *destImage;
    NSBitmapImageRep *destImageRep;
    int              w, h;
    int              x, y;
    int              i, j;
    unsigned char    *srcData;
    unsigned char    *destData;
    int              bytesPerPixel;
    int              pixNum;
    unsigned char    *filterMask; /* the median window */
    int              realSize;    /* real width of the median window */
    int              chNum;       /* channels to filter, grey or RGB */
    int              c;           /* current channel */
    
    progressSteps = 0;
    totalProgressSteps = 2;
    progPanel = prPanel;
    
    realSize = size*2 + 1;
    
    /* get source image representation and associated information */
    if (progPanel != nil)
    {
        [self setActivity:@"Get image size"];
        [self advanceProgress];
    }
    srcImageRep = [srcImage tiffRep];
    
    w = [srcImageRep pixelsWide];
    h = [srcImageRep pixelsHigh];
    pixNum = h * w;
    bytesPerPixel = [srcImageRep bitsPerPixel] /8;
    
    /* check bith depth and color/greyscale image */
    if ([srcImageRep hasAlpha])
    {
        if ([srcImageRep samplesPerPixel] == 2)
        {
            NSLog(@"Grayscale image with alpha");
            return srcImage;
        }
        else
        {
            NSLog(@"Color image with alpha");
            return srcImage;
        }
    }
    else
    {
        /* allocate destination image and its representation */
        destImage = [[PRImage alloc] initWithSize:NSMakeSize(w, h)];
        if ([srcImageRep samplesPerPixel] == 1)
        {
            destImageRep = [[NSBitmapImageRep alloc]
                    initWithBitmapDataPlanes:NULL
                                  pixelsWide:w
                                  pixelsHigh:h
                               bitsPerSample:8
                             samplesPerPixel:1
                                    hasAlpha:NO
                                    isPlanar:NO
                              colorSpaceName:NSCalibratedWhiteColorSpace
                                 bytesPerRow:0
                                bitsPerPixel:0];
            chNum = 1;
        }
        else
        {
            destImageRep = [[NSBitmapImageRep alloc]
                    initWithBitmapDataPlanes:NULL
                                  pixelsWide:w
                                  pixelsHigh:h
                               bitsPerSample:8
                             samplesPerPixel:3
                                    hasAlpha:NO
                                    isPlanar:NO
                              colorSpaceName:NSCalibratedRGBColorSpace
                                 bytesPerRow:0
                                bitsPerPixel:0];
            chNum = 3;
        }
    }
    
    destData = [destImageRep bitmapData];
    srcData = [srcImageRep bitmapData];
    
    if (progPanel != nil)
    {
        [self setActivity:@"Filter"];
        [self advanceProgress];
    }
    
    if (form == HORIZONTAL_F)
    {
        filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (realSize));
        if (filterMask == NULL)
            NSLog(@"Failed filterMask allocation in PRMedian");
        for (y = 0; y < h; y++)
        {
            for (x = 0 + size + 1; x < w - (size + 1); x++)
            {
                for (c = 0; c < chNum; c++)
                {
                    for (i = 0; i < realSize; i++)
                        filterMask[i] = srcData[(y * w + x - size + i)*chNum + c];
                    /* primitve sorting */
                    for (i = 0; i < realSize-1; i++)
                    {
                        for (j = 0; j < realSize-1-i; j++)
                        {
                            if (filterMask[j] > filterMask[j+1])
                            {
                                unsigned char temp;
                                temp = filterMask[j+1];
                                filterMask[j+1] = filterMask[j];
                                filterMask[j] = temp;
                            }
                        }
                    }
                    /* insert result in destination */
                    destData[(y * w + x)*chNum + c] = filterMask[size];
                }
            }
        }
        free(filterMask);
    } else if (form == VERTICAL_F)
    {
        filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (realSize));
        for (x = 0; x < w; x++)
        {
            for (y = 0 + size + 1; y < h - (size + 1); y++)
            {
                for (c = 0; c < chNum; c++)
                {
                    for (i = 0; i < realSize; i++)
                        filterMask[i] = srcData[((y - size + i) * w + x)*chNum + c];
                    /* primitve sorting */
                    for (i = 0; i < realSize-1; i++)
                    {
                        for (j = 0; j < realSize-1-i; j++)
                        {
                            if (filterMask[j] > filterMask[j+1])
                            {
                                unsigned char temp;
                                temp = filterMask[j+1];
                                filterMask[j+1] = filterMask[j];
                                filterMask[j] = temp;
                            }
                        }
                    }
                    /* insert result in destination */
                    destData[(y * w + x)*chNum + c] = filterMask[size];
                }
            }
        }
        free(filterMask);
    } else if (form == CROSS_F)
    {
        if (separable)
        {
            unsigned char tempResult; /* to store the result between the separable passes */
            filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (realSize));
            for (x = 0 + size + 1; x < w - (size + 1); x++)
            {
                for (y = 0 + size + 1; y < h - (size + 1); y++)
                {
                    for (c = 0; c < chNum; c++)
                    {
                        for (i = 0; i < realSize; i++)
                            filterMask[i] = srcData[(y * w + x - size + i)*chNum + c];
                            
                        /* primitve sorting */
                        for (i = 0; i < realSize-1; i++)
                        {
                            for (j = 0; j < realSize-1-i; j++)
                            {
                                if (filterMask[j] > filterMask[j+1])
                                {
                                    unsigned char temp;
                                    temp = filterMask[j+1];
                                    filterMask[j+1] = filterMask[j];
                                    filterMask[j] = temp;
                                }
                            }
                        }
                        tempResult = filterMask[size];
                        for (i = 0; i < realSize; i++)
                            filterMask[i] = srcData[((y - size + i) * w + x)*chNum + c];
                        filterMask[size] = tempResult;
                        /* primitve sorting */
                        for (i = 0; i < realSize-1; i++)
                        {
                            for (j = 0; j < realSize-1-i; j++)
                            {
                                if (filterMask[j] > filterMask[j+1])
                                {
                                    unsigned char temp;
                                    temp = filterMask[j+1];
                                    filterMask[j+1] = filterMask[j];
                                    filterMask[j] = temp;
                                }
                            }
                        }
                        /* insert result in destination */
                        destData[(y * w + x)*chNum + c] = filterMask[size];
                    }
                }
            }
            free(filterMask);
        } else /* not separable */
        {
            int totalSize; /* the total number of samples in the filter */
            int k;
            
            totalSize = 4 * size + 1;
            filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (totalSize));
            for (y = 0 + size + 1; y < h - (size + 1); y++)
            {
                for (x = 0 + size + 1; x < w - (size + 1); x++)
                {
                    for (c = 0; c < chNum; c++)
                    {
                        k = 0;
                        for (i = 0; i < realSize; i++)
                            filterMask[k++] = srcData[(y * w + x - size + i)*chNum + c];
                        for (i = 1; i <= size; i++)
                            filterMask[k++] = srcData[((y - i) * w + x)*chNum + c];
                        for (i = 1; i <= size; i++)
                            filterMask[k++] = srcData[((y + i) * w + x)*chNum + c];
                        
                        
                        /* primitve sorting */
                        for (i = 0; i < totalSize-1; i++)
                        {
                            for (j = 0; j < totalSize-1-i; j++)
                            {
                                if (filterMask[j] > filterMask[j+1])
                                {
                                    unsigned char temp;
                                    temp = filterMask[j+1];
                                    filterMask[j+1] = filterMask[j];
                                    filterMask[j] = temp;
                                }
                            }
                        }
                        /* insert result in destination */
                        destData[(y * w + x)*chNum + c] = filterMask[2*size];
                    }
                }
            }
            free(filterMask);
        }
    }  else if (form == BOX_F)
    {
        if (separable)
        {
            int k;
            unsigned char *tempResult;
            
            filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (realSize));
            tempResult = (unsigned char *) malloc(sizeof(unsigned char) * (realSize));
            for (y = 0 + size + 1; y < h - (size + 1); y++)
            {
                for (x = 0 + size + 1; x < w - (size + 1); x++)
                {
                    for (c = 0; c < chNum; c++)
                    {
                        for (i = 0; i < realSize; i++)
                        {
                            for (j = 0; j < realSize; j++)
                                filterMask[j] = srcData[((y - size + i) * w + x - size + j)*chNum + c];
                            
                            /* primitve sorting */
                            for (j = 0; j < realSize-1; j++)
                            {
                                for (k = 0; k < realSize-1-j; k++)
                                {
                                    if (filterMask[k] > filterMask[k+1])
                                    {
                                        unsigned char temp;
                                        temp = filterMask[k+1];
                                        filterMask[k+1] = filterMask[k];
                                        filterMask[k] = temp;
                                    }
                                }
                            }
                            /* insert result in destination */
                            tempResult[i] = filterMask[size];
                        }
    
                        
                        /* primitve sorting */
                        for (i = 0; i < realSize-1; i++)
                        {
                            for (j = 0; j < realSize-1-i; j++)
                            {
                                if (tempResult[j] > tempResult[j+1])
                                {
                                    unsigned char temp;
                                    temp = tempResult[j+1];
                                    tempResult[j+1] = tempResult[j];
                                    tempResult[j] = temp;
                                }
                            }
                        }
                        /* insert result in destination */
                        destData[(y * w + x)*chNum + c] = tempResult[size];
                    }
                }
            }
            free(filterMask);
            free(tempResult);
        } else /* not separable */
        {
            int totalSize; /* the total number of samples in the filter */
            int k;
            
            totalSize = realSize*realSize;
            
            filterMask = (unsigned char *) malloc(sizeof(unsigned char) * (totalSize));
            for (y = 0 + size + 1; y < h - (size + 1); y++)
            {
                for (x = 0 + size + 1; x < w - (size + 1); x++)
                {
                    for (c = 0; c < chNum; c++)
                    {
                        k = 0;
                        for (i = 0; i < realSize; i++)
                            for (j = 0; j < realSize; j++)
                                filterMask[k++] = srcData[((y - size + i) * w + x - size + j)*chNum + c];
    
                        /* primitve sorting */
                        for (i = 0; i < totalSize-1; i++)
                        {
                            for (j = 0; j < totalSize-1-i; j++)
                            {
                                if (filterMask[j] > filterMask[j+1])
                                {
                                    unsigned char temp;
                                    temp = filterMask[j+1];
                                    filterMask[j+1] = filterMask[j];
                                    filterMask[j] = temp;
                                }
                            }
                        }
                        
                        /* insert result in destination */
                        destData[(y * w + x)*chNum + c] = filterMask[size*(realSize) + size]; /* center */
                    }
                }
            }
            free(filterMask);
        }
    } else
        NSLog(@"Unrecognized median filter type.");
    
    if (progPanel != nil)
    {
        [self setActivity:@"Done"];
        [self showProgress];
    }
    
    [destImage addRepresentation:destImageRep];
    [destImageRep release];
    [destImage autorelease];
    return destImage;
}


@end
