/*
 *                            COPYRIGHT
 *
 *  Copyright (C) 2014 Exstrom Laboratories LLC
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  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.
 *
 *  A copy of the GNU General Public License is available on the internet at:
 *  http://www.gnu.org/copyleft/gpl.html
 *
 *  or you can write to:
 *
 *  The Free Software Foundation, Inc.
 *  675 Mass Ave
 *  Cambridge, MA 02139, USA
 *
 *  Exstrom Laboratories LLC contact:
 *  stefan(AT)exstrom.com
 *
 *  Exstrom Laboratories LLC
 *  Longmont, CO 80503, USA
 *
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "butterworthdesign.h"
#include "filter.html.h"

static filterDesign sp_Filters[MAXCHANNELS] = {{0}};

#define PI 3.14159265

// channel starts at 0
filterDesign *createFilter(filterType type, int order, int sampleRate, int cutOffFrequency, int channel)
{
  filterDesign *filter = sp_Filters + channel;
  if(filter->initialized == 2654646132)
  freeFilter(filter);
  filter->type = type;
  filter->order = order;
  filter->sampleRate = sampleRate;
  filter->cutOffFrequency = cutOffFrequency;
  filter->noPoles = order / 2;
  int n = filter->noPoles;

  float a = tan(PI * (float)cutOffFrequency / (float)sampleRate);
  float a2 = a * a;
  filter->A = (float *)malloc(n * sizeof(float));
  filter->d1 = (float *)malloc(n * sizeof(float));
  filter->d2 = (float *)malloc(n * sizeof(float));
  filter->w0 = (float *)calloc(n, sizeof(float));
  filter->w1 = (float *)calloc(n, sizeof(float));
  filter->w2 = (float *)calloc(n, sizeof(float));
  float r;
  float s;

  for (int i = 0; i < n; ++i)
  {
    r = sin(PI * (2.0 * i + 1.0) / (4.0 * n));
    s = a2 + 2.0 * a * r + 1.0;
    if (filter->type == filter_lpf)
      filter->A[i] = a2 / s;
    else
      filter->A[i] = 1.0 / s;
    filter->d1[i] = 2.0 * (1 - a2) / s;
    filter->d2[i] = -(a2 - 2.0 * a * r + 1.0) / s;
  }
  filter->initialized = 2654646132;
  return filter;
}

void IRAM_ATTR runFilter(float *input, float *output, int noSamples, void *data)
{
  filterDesign *filter = (filterDesign*)data;

  float lpf = filter->type == filter_lpf ? 1.0 : -1.0;
  for (int j = 0; j < noSamples; ++j)
  {
    float x = input[j];
    for (int i = 0; i < filter->noPoles; ++i)
    {

      filter->w0[i] = filter->d1[i] * filter->w1[i] + filter->d2[i] * filter->w2[i] + x;
      x = filter->A[i] * (filter->w0[i] + lpf * 2.0 * filter->w1[i] + filter->w2[i]);
      filter->w2[i] = filter->w1[i];
      filter->w1[i] = filter->w0[i];
    }
    output[j] = x;
  }
}

void freeFilter(filterDesign *filter)
{
  free(filter->A);
  free(filter->d1);
  free(filter->d2);
  free(filter->w0);
  free(filter->w1);
  free(filter->w2);
}

char *getFilterJson(int channel, char *buffer, int bufferSize)
{
  filterDesign *filter = sp_Filters + channel;
  if (filter->order == 0)
    snprintf(buffer, bufferSize, "{\"order\":0,\"frequency\":0,\"filtertype\":\"0\"}");
  else
    snprintf(buffer, bufferSize, "{\"order\":%d,\"frequency\":%d,\"filtertype\":\"%d\"}", filter->order, filter->cutOffFrequency, filter->type);

  return buffer;
}

esp_err_t http_getFilter(httpd_req_t *req)
{
  char buffer[100] = "Invalid channel";
  int uriLen = strlen(req->uri);
  char channelNo = req->uri[uriLen - 1] - 0x31;
  if (channelNo < MAXCHANNELS)
    getFilterJson(channelNo, buffer, 100);
  httpd_resp_send(req, buffer, HTTPD_RESP_USE_STRLEN);
  return ESP_OK;
}

esp_err_t http_filter(httpd_req_t *req)
{
  httpd_resp_send(req, filter_HTML_String, HTTPD_RESP_USE_STRLEN);
  return ESP_OK;
}

esp_err_t http_filter_submit(httpd_req_t *req)
{
  char buffer[100];
    if (httpd_req_get_url_query_str(req, buffer, 100) == ESP_OK)
    {
        char param[20];
        httpd_query_key_value(buffer, "channel", param, 10);
        int channelNo = atoi(param);
        if(channelNo > MAXCHANNELS)
        {
          httpd_resp_send(req, "Invalid channel", HTTPD_RESP_USE_STRLEN);
          return ESP_OK;
        }
        httpd_query_key_value(req->uri, "order", param, 10);
        int order = atoi(param);
        httpd_query_key_value(req->uri, "frequency", param, 10);
        int frequency = atoi(param);
        httpd_query_key_value(req->uri, "filter", param, 10);
        int filter = atoi(param);
        createFilter(filter,order,44100,frequency,channelNo);
    }

    /* Send a simple response */
    const char resp[] = "<meta http-equiv=\"refresh\" content=\"1; URL=/filter\" />Filter Saved ";
    httpd_resp_send(req, resp, HTTPD_RESP_USE_STRLEN);
    return ESP_OK;
}

const httpd_uri_t uri_filter_get = {
    .uri = "/filter/channel*",
    .method = HTTP_GET,
    .handler = http_getFilter,
    .user_ctx = NULL};

const httpd_uri_t uri_filter_submit = {
    .uri = "/filter/submit",
    .method = HTTP_GET,
    .handler = http_filter_submit,
    .user_ctx = NULL};

const httpd_uri_t uri_filter = {
    .uri = "/filter",
    .method = HTTP_GET,
    .handler = http_filter,
    .user_ctx = NULL};

void filter_registerHTTPHandlers(httpd_handle_t *server)
{
  httpd_register_uri_handler(*server, &uri_filter_get);
  httpd_register_uri_handler(*server, &uri_filter);
  httpd_register_uri_handler(*server, &uri_filter_submit);
}
