using System;
using System.Collections;

namespace MarketData.Numerical
{
// The column is the key, the row is the value.
// For Time series elements put the date in the column and the value in the row
  public class Element
  {
    private double row;
    private double column;

    public Element()
    {
    }
    public Element(double column, double row)
    {
      this.column = column;
      this.row = row;
    }
    public double Row
    {
      get { return row; }
      set { row = value; }
    }
    public double Column
    {
      get { return column; }
      set { column = value; }
    }
  }
  public class CatmullRom
  {
    private CatmullRom()
    {
    }
    public static bool PerformSpline(Element[] sourcePairs, Element[] destPairs)
    {
      double a0, a1, a2, a3;
      double dx, dx1, dx2;
      double dy, dy1, dy2;
      double endPointOne;
      double endPointTwo;
      double resamplingPos;
      double xPoint;
      int clampOne, clampTwo;
      int direction;
      int destSize = destPairs.Length;
      int sourceSize = sourcePairs.Length;
      int inputIndex, index;

      a0 = a1 = a2 = a3 = endPointOne = 0;
      if (sourceSize < 2 || destSize < 1) return false;
      if (((Element)sourcePairs[0]).Column < ((Element)sourcePairs[1]).Column)
      {
        if (((Element)destPairs[0]).Column < ((Element)sourcePairs[0]).Column ||
          ((Element)destPairs[destSize - 1]).Column > ((Element)sourcePairs[sourceSize - 1]).Column)
          direction = 0;
        else direction = 1;
      }
      else
      {
        if (((Element)destPairs[0]).Column > ((Element)sourcePairs[0]).Column ||
          ((Element)destPairs[destSize - 1]).Column < ((Element)sourcePairs[sourceSize - 1]).Column)
          direction = 0;
        else direction = -1;
      }
      if (0 == direction) return false;
      if (1 == direction) endPointTwo = ((Element)destPairs[0]).Column - 1;
      else endPointTwo = ((Element)destPairs[0]).Column + 1;
      for (index = 0; index < destSize; index++)
      {
        resamplingPos = ((Element)destPairs[index]).Column;
        if ((1 == direction && resamplingPos > endPointTwo) ||
          (-1 == direction && resamplingPos < endPointTwo))
        {
          for (inputIndex = 0; inputIndex < sourceSize && resamplingPos > ((Element)sourcePairs[inputIndex]).Column; inputIndex++) ;
          if (resamplingPos < ((Element)sourcePairs[inputIndex]).Column) inputIndex--;
          if (inputIndex < 0) inputIndex = 0;
          else if (inputIndex == sourceSize-1) inputIndex--;
          endPointOne = ((Element)sourcePairs[inputIndex]).Column;
          endPointTwo = ((Element)sourcePairs[inputIndex + 1]).Column;
          clampOne = Math.Max(inputIndex - 1, 0);
          clampTwo = Math.Min(inputIndex + 2, sourceSize - 1);
          double clampOneValue = ((Element)sourcePairs[clampOne]).Column;
          double clampTwoValue = ((Element)sourcePairs[clampTwo]).Column;
          dx = 1.0 / ((endPointTwo - endPointOne)==0?1:(endPointTwo-endPointOne));
          dx1 = 1.0 / ((endPointTwo - clampOneValue) == 0 ? 1 : (endPointTwo - clampOneValue));
          dx2 = 1.0 / (clampTwoValue-endPointOne == 0 ? 1 : (clampTwoValue - endPointOne));
          dy = (((Element)sourcePairs[inputIndex + 1]).Row - ((Element)sourcePairs[inputIndex]).Row) * dx;
          dy1 = (((Element)sourcePairs[inputIndex + 1]).Row - ((Element)sourcePairs[clampOne]).Row) * dx1;
          dy2 = (((Element)sourcePairs[clampTwo]).Row - ((Element)sourcePairs[inputIndex]).Row) * dx2;
          a0 = ((Element)sourcePairs[inputIndex]).Row;
          a1 = dy1;
          a2 = dx * (3 * dy - 2 * dy1 - dy2);
          a3 = dx * dx * (-2 * dy + dy1 + dy2);
        }
        xPoint = resamplingPos - endPointOne;
        ((Element)destPairs[index]).Row = ((a3 * xPoint + a2) * xPoint + a1) * xPoint + a0;
      }
      return true;
    }
    public static Element[] CreateSplineSourceElements(double[] columns, double[] rows)
    {
      Element[] elements = new Element[columns.Length];
      if (columns.Length != rows.Length) return null;
      for (int index = 0; index < columns.Length; index++)
      {
        elements[index] = new Element(columns[index], rows[index]);
      }
      return elements;
    }
  }
} // namespace
