package com.tibbo.aggregate.common.expression.function.math;

import org.apache.commons.math3.stat.descriptive.moment.Mean;

import com.tibbo.aggregate.common.Cres;
import com.tibbo.aggregate.common.datatable.DataRecord;
import com.tibbo.aggregate.common.datatable.DataTable;
import com.tibbo.aggregate.common.datatable.FieldFormat;
import com.tibbo.aggregate.common.datatable.SimpleDataTable;
import com.tibbo.aggregate.common.datatable.TableFormat;
import com.tibbo.aggregate.common.expression.EvaluationEnvironment;
import com.tibbo.aggregate.common.expression.EvaluationException;
import com.tibbo.aggregate.common.expression.Evaluator;
import com.tibbo.aggregate.common.expression.Function;
import com.tibbo.aggregate.common.expression.function.AbstractFunction;

public class SimpleMovingAverageFunction extends AbstractFunction
{
  public SimpleMovingAverageFunction()
  {
    super("sma", Function.GROUP_MATH, "DataTable table, String field, Integer deepness", "DataTable", Cres.get().getString("fDescSimpleMovingAverage"));
  }
  
  @Override
  public Object execute(Evaluator evaluator, EvaluationEnvironment environment, Object... parameters) throws EvaluationException
  {
    checkParameters(3, false, parameters);
    checkParameterType(0, parameters[0], DataTable.class);
    DataTable table = (DataTable) parameters[0];
    int deepness = ((Number) parameters[2]).intValue();
    if (deepness <= 0 && deepness > table.getRecordCount())
    {
      throw new EvaluationException("Deepness can't be 0");
    }
    if (deepness > table.getRecordCount())
    {
      throw new EvaluationException("Deepness can't be larger than the table size");
    }

    FieldFormat ff = checkAndGetNumericTypeField(table, parameters[1]);
    String field = ff.getName();
    
    TableFormat resultTableFormat = new TableFormat();
    resultTableFormat.addField(ff);
    resultTableFormat.addField(FieldFormat.create("average", FieldFormat.DOUBLE_FIELD, "Average").setNullable(true));
    DataTable resultTable = new SimpleDataTable(resultTableFormat);
    double[] seriesData = new double[deepness];
    Mean mean = new Mean();
    int count = 0;

    for (DataRecord rec : table)
    {
      Object value = rec.getValue(field);
      if (value instanceof Number)
      {
        System.arraycopy(seriesData, 1, seriesData, 0, seriesData.length - 1);
        seriesData[deepness - 1] = ((Number) value).doubleValue();
        if (deepness - 1 <= count)
        {
          resultTable.addRecord(rec.getValue(field), mean.evaluate(seriesData));
        }
        else
        {
          resultTable.addRecord(rec.getValue(field), null);
        }
        count++;
      }
    }
    
    return resultTable;
  }
}
