/*  
    This code is written by Davide Albanese <albanese@fbk.it>.
    (C) 2009 Fondazione Bruno Kessler - Via Santa Croce 77, 38100 Trento, ITALY.
    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 3 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.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/


#include <Python.h>
#include <numpy/arrayobject.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <float.h>



int 
maximum(double *x, int n)
{
  int i;
  int idx = 0;
 
  for (i=1; i<n; i++)
    if (x[i] > x[idx])
	idx = i;
  
  return idx;
}


static PyObject *peaksd_three_points_pd(PyObject *self, PyObject *args, PyObject *keywds)
{
  PyObject *x = NULL; PyObject *xa = NULL;
 
  PyObject *peaksidx = NULL;
  npy_intp peaksidx_dims[1];
  
  double *xa_v;
  int *peaksidx_v;

  int *peaks;
  int npeaks;
    
  int idxp, idx, idxf;

  npy_intp n;
  int i;

  PyErr_WarnEx(PyExc_DeprecationWarning, "use the new mlpy 2.0.7 function mlpy.span_pd(x, span) (span=3) instead", 1);

  static char *kwlist[] = {"x", NULL};
  if (!PyArg_ParseTupleAndKeywords(args, keywds, "O", kwlist, &x))
    return NULL;
  
  xa = PyArray_FROM_OTF(x, NPY_DOUBLE, NPY_IN_ARRAY);
  if (xa == NULL) return NULL;
  
  xa_v = (double *) PyArray_DATA(xa);
  n    = PyArray_DIM(xa, 0);
  
  peaks = (int *) malloc (n * sizeof(int));
  
  npeaks = 0;
  for(i = 0; i < (int) n-2; i++)
    {
      idxp = i; idx = i+1; idxf = i+2;

      if((xa_v[idxp] < xa_v[idx]) && (xa_v[idx] > xa_v[idxf]))
	{
	  peaks[npeaks] = idx;
	  npeaks++;
	}
    }
  
  peaksidx_dims[0] = (npy_intp) npeaks;
  peaksidx = PyArray_SimpleNew(1, peaksidx_dims, NPY_INT);
  peaksidx_v = (int *) PyArray_DATA(peaksidx);  

  for(i = 0; i < npeaks; i++)
    peaksidx_v[i] = peaks[i];

  free(peaks);
  Py_DECREF(xa);

  return Py_BuildValue("N", peaksidx);
}


static PyObject *peaksd_span_pd(PyObject *self, PyObject *args, PyObject *keywds)
{
  PyObject *x = NULL; PyObject *xa = NULL;
  int span=3;

  PyObject *peaksidx = NULL;
  npy_intp peaksidx_dims[1];
  
  double *xa_v, *xatmp_v;
  int *peaksidx_v;

  int *peaks;
  int npeaks;
    
  int n, ntmp;
  int i, j, mm, center;

  
  static char *kwlist[] = {"x", "span", NULL};
  if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i", kwlist, &x, &span))
    return NULL;
  
  xa = PyArray_FROM_OTF(x, NPY_DOUBLE, NPY_IN_ARRAY);
  if (xa == NULL) return NULL;

  if ((span % 2 == 0) || (span < 3))
    {
      PyErr_SetString(PyExc_ValueError, "span should be >= 3 and an odd number");
      return NULL;
    }
  
  xa_v = (double *) PyArray_DATA(xa);
  n    = (int) PyArray_DIM(xa, 0);
  
  center = (span - 1) / 2;

  xatmp_v = (double *) malloc ((n + span - 1) * sizeof(double));
  ntmp    = n + span - 1;
  
  for (i=center, j=0; i<ntmp-center; i++, j++)
    xatmp_v[i] = xa_v[j];
  
  for (i=0; i<center; i++)
    {
      xatmp_v[i] = -DBL_MAX;
      xatmp_v[ntmp-i-1] = -DBL_MAX;
    }
  
  peaks = (int *) malloc (n * sizeof(int));

  npeaks = 0;
  for(i=0; i<n; i++)
    {
      mm = maximum(xatmp_v + i, span);   

      if (mm == center)
	{
	  peaks[npeaks] = i;
	  npeaks++;
	}
    }
  
  free(xatmp_v);

  peaksidx_dims[0] = (npy_intp) npeaks;
  peaksidx = PyArray_SimpleNew(1, peaksidx_dims, NPY_INT);
  peaksidx_v = (int *) PyArray_DATA(peaksidx);  

  for(i=0; i<npeaks; i++)
    peaksidx_v[i] = peaks[i];

  free(peaks);
  Py_DECREF(xa);

  return Py_BuildValue("N", peaksidx);
}



static char module_doc[] = "Peaks detection module";

static char peaksd_three_points_pd_doc[] =
  "3 points peaks detection.\n\n"
  "Input\n\n"
  "  * *x* - [1D numpy array float] data\n\n"
  "Output\n\n"
  "  * *idx* - [1D numpy array integer] peaks indexes"
;

static char peaksd_span_pd_doc[] =
  "span peaks detection.\n\n"
  "Input\n\n"
  "  * *x* - [1D numpy array float] data\n"
  "  * *span* - [odd int] span\n\n"
  "Output\n\n"
  "  * *idx* - [1D numpy array integer] peaks indexes"
;


/* Method table */
static PyMethodDef peaksd_methods[] = {
  {"three_points_pd",
   (PyCFunction)peaksd_three_points_pd,
   METH_VARARGS | METH_KEYWORDS,
   peaksd_three_points_pd_doc},
  {"span_pd",
   (PyCFunction)peaksd_span_pd,
   METH_VARARGS | METH_KEYWORDS,
   peaksd_span_pd_doc},
  {NULL, NULL, 0, NULL}
};


/* Init */
void initpeaksd()
{
  Py_InitModule3("peaksd", peaksd_methods, module_doc);
  import_array();
}
