/*
 * filter.c: Filter class for the Atmolight-plugin
 *
 * See the README file for copyright information and how to reach the author.
 *
 * $Id$
 */

#include <stdlib.h>
#include <vdr/tools.h>
#include <vdr/thread.h>
#include "math.h"

#include "defs.h"
#include "setup.h"
#include "filter.h"

// --- cAtmoFilter-----------------------------------------------------------

cAtmoFilter::cAtmoFilter()
{
  // Initialization
  ResetFilter();
}

cAtmoFilter::~cAtmoFilter()
{
}

void cAtmoFilter::ResetFilter(void)
{
  // reset filter values
  MeanFilter(true);
  PercentFilter(true);
}

tColorPacket cAtmoFilter::Filtering(tColorPacket ColorPacket)
{
  filter_input = ColorPacket;

  // apply filter
  switch (AtmoSetup.Filter_Mode)
  {
    case combined:
         MeanFilter(false);
    break;

    case percent:
         PercentFilter(false);
    break;

    case no_filter:
    default:
         filter_output = filter_input;
    break;
  }

  return filter_output;
}

void cAtmoFilter::PercentFilter(bool init)
{
  static tColorPacket filter_output_old; // needed for the percentage filter

  if (init) // Initialization
  {
    memset(&filter_output_old, 0, sizeof(filter_output_old));
    return;
  }

  for (int ch = 0; ch < 5; ch++)
  {
	filter_output.channel[ch].r = (filter_input.channel[ch].r * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].r * AtmoSetup.Filter_PercentNew) / 100;
	filter_output.channel[ch].g = (filter_input.channel[ch].g * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].g * AtmoSetup.Filter_PercentNew) / 100;
	filter_output.channel[ch].b = (filter_input.channel[ch].b * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].b * AtmoSetup.Filter_PercentNew) / 100;
  }
  filter_output_old = filter_output;
}

void cAtmoFilter::MeanFilter(bool init)
{
  static tColorPacketLongInt mean_sums;  // needed vor the running mean value filter
  static tColorPacket mean_values;
  static tColorPacket filter_output_old; // needed for the percentage filter
  static int filter_length_old;
  char reinitialize = 0;

  if (init) // Initialization
  {
    memset(&filter_output_old, 0, sizeof(filter_output_old));
    memset(&mean_sums, 0, sizeof(mean_sums));
    memset(&mean_values, 0, sizeof(mean_values));
    return;
  }

  if (filter_length_old != AtmoSetup.Filter_MeanLength) // if filter_length has changed
  {
    reinitialize = 1; // force reinitialization of the filter
  }
  filter_length_old = AtmoSetup.Filter_MeanLength;

  if (filter_length_old < 20) filter_length_old = 20; // avoid division by 0

  for (int ch = 0; ch < 5; ch++)
  {
    // calculate the mean-value filters
    mean_sums.channel[ch].r += (long int)(filter_input.channel[ch].r - mean_values.channel[ch].r); // red
    mean_values.channel[ch].r = mean_sums.channel[ch].r / ((long int)filter_length_old / 20);

    mean_sums.channel[ch].g += (long int)(filter_input.channel[ch].g - mean_values.channel[ch].g); // green
    mean_values.channel[ch].g = mean_sums.channel[ch].g / ((long int)filter_length_old / 20);

    mean_sums.channel[ch].b += (long int)(filter_input.channel[ch].b - mean_values.channel[ch].b); // blue
    mean_values.channel[ch].b = mean_sums.channel[ch].b / ((long int)filter_length_old / 20);

    // check, if there is a jump -> check if differences between actual values and filter values are too big

    long int dist; // distance between the two colors in the 3D RGB space
    dist = (mean_values.channel[ch].r - filter_input.channel[ch].r)*(mean_values.channel[ch].r - filter_input.channel[ch].r)+
           (mean_values.channel[ch].g - filter_input.channel[ch].g)*(mean_values.channel[ch].g - filter_input.channel[ch].g)+
           (mean_values.channel[ch].b - filter_input.channel[ch].b)*(mean_values.channel[ch].b - filter_input.channel[ch].b);

    if (dist > 0) { dist = (long int)sqrt((double)dist); } // avoid sqrt(0) (TODO: necessary?)

    // compare calculated distance with the filter threshold
	if ((dist > (long int)((double)AtmoSetup.Filter_MeanThreshold * 3.6f)) || ( reinitialize == 1))
    {
      // filter jump detected -> set the long filters to the result of the short filters
      filter_output.channel[ch] = mean_values.channel[ch] = filter_input.channel[ch];

      mean_sums.channel[ch].r = filter_input.channel[ch].r * (filter_length_old / 20);
      mean_sums.channel[ch].g = filter_input.channel[ch].g * (filter_length_old / 20);
      mean_sums.channel[ch].b = filter_input.channel[ch].b * (filter_length_old / 20);
    }
    else
    {
      // apply an additional percent filter and return calculated values
	  filter_output.channel[ch].r = (mean_values.channel[ch].r * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].r * AtmoSetup.Filter_PercentNew) / 100;
	  filter_output.channel[ch].g = (mean_values.channel[ch].g * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].g * AtmoSetup.Filter_PercentNew) / 100;
	  filter_output.channel[ch].b = (mean_values.channel[ch].b * (100-AtmoSetup.Filter_PercentNew) + filter_output_old.channel[ch].b * AtmoSetup.Filter_PercentNew) / 100;
    }
  }
  filter_output_old = filter_output;
}
