#include <stdlib.h>
#include <stdio.h>
#include <assert.h>

#include "def.h"
#include "filter.h"
#include "bitmap_8bit.h"
   
Filter *createFilter(Point2D origin, int width, int height, 
		     int isAverage, double *mask) {
  Filter *aFilter;
  int i;

  if( (width<0) || (height<0) ) {
    return NULL;
  }

  if( (origin.x < 0 || origin.x > width-1) ||
      (origin.y < 0 || origin.y > height-1) ) {
    return NULL;
  }

  aFilter = malloc( sizeof(Filter) );
  if(aFilter == NULL) 
    return NULL;

  aFilter->mask = calloc( width*height, sizeof(double) );
  if(aFilter->mask == NULL) {
    free(aFilter);
    return NULL;
  }

  aFilter->origin.x = origin.x;
  aFilter->origin.y = origin.y;
  aFilter->width  = width;
  aFilter->height = height;
  aFilter->isAverage = isAverage;

  for(i=0; i<width*height; i++) {
    aFilter->mask[i] = mask[i];
  }
  
  return aFilter;
}


void printFilter(Filter *f) {
  int x, y;

  for(y=0; y< f->height; y++) {
    for(x=0; x< f->width; x++) {
      printf("%+3.1f ", f->mask[ y* f->width + x ]);
    }
    printf("\n");
  }
  printf("isAverage=%d   Size= %d X %d   Origin=(%d,%d)\n", 
	 f->isAverage, f->width, f->height, f->origin.x, f->origin.y);  
}




static void applyFilterToPixel(Bitmap_8bit *source, Bitmap_8bit *result, 
			       Filter *filter, int x, int y) {
  int startX, startY;
  int endX, endY;
  int ix, iy;
  int count;

  unsigned char value;
  double totalValue = 0;
  int maskX, maskY;
  unsigned char finalValue;

  startX = x - filter->origin.x;
  startY = y - filter->origin.y;
  endX = startX + filter->width  -1;
  endY = startY + filter->height -1;
  
  if(startX < 0) startX = 0;
  if(startY < 0) startY = 0;

  if(endX > source->width - 1) endX = source->width - 1;
  if(endY > source->height -1) endY = source->height -1;

  count = 0;
  for( iy=startY ; iy<=endY ; iy++) {
    for( ix=startX ; ix<=endX ; ix++) {
      maskX = ix-startX;
      maskY = iy-startY;
      value = getPixel_bmp8bit(source, ix, iy);
      totalValue += value * filter->mask[maskX + maskY*filter->width];
      count++;
    }
  }

  if(totalValue < 0) 
    totalValue = -totalValue;

  assert(  filter->isAverage ||
	  (0<=totalValue  && totalValue<=255));
  assert( !filter->isAverage || 
	  (0<=totalValue/count && totalValue/count<=255));

  if( filter->isAverage ) {
    finalValue = (unsigned char) (totalValue/count);
    if( (totalValue/count) - finalValue >= 0.5)
      finalValue++;
  }
  else
    finalValue = (unsigned char) totalValue;

  setPixel_bmp8bit(result, x, y, finalValue);
} 


Bitmap_8bit * applyFilter(Bitmap_8bit *source, Filter *filter) {

  Bitmap_8bit *result;
  int x1, y1;

  result = create_bmp8bit(source->width, source->height);

  
  for( y1=0; y1 < source->height; y1++ ) {
    for( x1=0; x1 < source->width; x1++ ) {
      
      applyFilterToPixel(source, result, filter, x1, y1);

    }
  }
  
  return result;
}

// ---------- MEDIAN FILTERING -------------------

static unsigned char pixelMedian8Neighbour(Bitmap_8bit *source, int x, int y){
  unsigned char p[9], temp;
  int k, l;
  int w = source->width;
  unsigned char *in = source->image;

  /* this section loads the center pixel and its surrounding neighbors 
   * into an array  
   */          
  p[0] = in[(y-1)*w +x-1]; p[1] = in[(y-1)*w +x]; p[2] = in[(y-1)*w +x  ]; 
  p[3] = in[(y  )*w +x-1]; p[4] = in[(y  )*w +x]; p[5] = in[(y  )*w +x+1];
  p[6] = in[(y+1)*w +x-1]; p[7] = in[(y+1)*w +x]; p[8] = in[(y+1)*w +x+1];

  /* PENDING parempi sorttaus tähän */
  /* we will use a simple bubble sort to sort the values */

  for(k=0; k<8; k++){
    for(l=0; l<(8-k); l++)
      if(p[l] > p[l+1]) {
	temp = p[l]; p[l]=p[l+1]; p[l+1]=temp;
      }
  }
  /* return the middle value to use as the answer */
  return p[4];
}




Bitmap_8bit *applyMedianFilter(Bitmap_8bit * source) {
  int y,x;
  Bitmap_8bit *result;
  
  result = create_bmp8bit(source->width, source->height);
  if(result == NULL) {
    return NULL;
  }

  /* PENDING ei käsittele reunoja */
  for(y=1; y<source->height-1; y++) {
    for(x=1; x<source->width-1; x++) {
      setPixel_bmp8bit(result, x,y,pixelMedian8Neighbour(source, x, y));
    }
  }
  return result;
}
