// Copyright 2012 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <map>

#include <gtest/gtest.h>  // for FRIEND_TEST

#include "include/filter_interpreter.h"
#include "include/finger_metrics.h"
#include "include/gestures.h"
#include "include/prop_registry.h"
#include "include/tracer.h"

#ifndef GESTURES_IIR_FILTER_INTERPRETER_H_
#define GESTURES_IIR_FILTER_INTERPRETER_H_

namespace gestures {

// This filter interpreter applies a low-pass infinite impulse response (iir)
// filter to each incoming finger. The default filter is a low-pass 2nd order
// Butterworth IIR filter with a normalized cutoff frequency of 0.2. It can be
// configured via properties to use other formulae or
// different coefficients for the Butterworth filter.

class IirFilterInterpreter : public FilterInterpreter, public PropertyDelegate {
  FRIEND_TEST(IirFilterInterpreterTest, DisableIIRTest);
 public:
  // We'll maintain one IOHistory record per active finger
  class IoHistory {
   public:
    IoHistory() : in_head(0), out_head(0) {}
    explicit IoHistory(const FingerState& fs) : in_head(0), out_head(0) {
      for (size_t i = 0; i < kInSize; i++)
        in[i] = fs;
      for (size_t i = 0; i < kOutSize; i++)
        out[i] = fs;
    }
    // Note: NextOut() and the oldest PrevOut() point to the same object.
    FingerState* NextOut() { return &out[NextOutHead()]; }
    FingerState* PrevOut(size_t idx) {
      return &out[(out_head + idx) % kOutSize];
    }
    // Note: NextIn() and the oldest PrevIn() point to the same object.
    FingerState* NextIn() { return &in[NextInHead()]; }
    FingerState* PrevIn(size_t idx) { return &in[(in_head + idx) % kInSize]; }
    void Increment();

    bool operator==(const IoHistory& that) const;
    bool operator!=(const IoHistory& that) const { return !(*this == that); }

    void WarpBy(float dx, float dy);

   private:
    size_t NextOutHead() const {
      return (out_head + kOutSize - 1) % kOutSize;
    }
    size_t NextInHead() const {
      return (in_head + kInSize - 1) % kInSize;
    }

    static const size_t kInSize = 3;
    static const size_t kOutSize = 2;
    FingerState in[kInSize];  // previous input values
    size_t in_head;
    FingerState out[kOutSize];  // previous output values
    size_t out_head;
  };

  // Takes ownership of |next|:
  IirFilterInterpreter(PropRegistry* prop_reg, Interpreter* next,
                       Tracer* tracer);
  virtual ~IirFilterInterpreter() {}

 protected:
  virtual void SyncInterpretImpl(HardwareState& hwstate, stime_t* timeout);

 public:
  virtual void DoubleWasWritten(DoubleProperty* prop);

 private:
  // Whether IIR filter should be used. Put as a member variable for
  // unittest purpose.
  bool using_iir_;

  // Sync state history information
  std::map<short, IoHistory> histories_;

  // y[0] = b[0]*x[0] + b[1]*x[1] + b[2]*x[2] + b[3]*x[3]
  //        - (a[1]*y[1] + a[2]*y[2])
  DoubleProperty b0_, b1_, b2_, b3_, a1_, a2_;

  // If position change between 2 frames is less than iir_dist_thresh_,
  // IIR filter is applied, otherwise rolling average is applied.
  DoubleProperty iir_dist_thresh_;
  // Whether to adjust the IIR history when finger WARP is detected.
  BoolProperty adjust_iir_on_warp_;
};

}  // namespace gestures

#endif  // GESTURES_IIR_FILTER_INTERPRETER_H_
