// Copyright 2013 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 <set>

#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_FINGER_MERGE_FILTER_INTERPRETER_H_
#define GESTURES_FINGER_MERGE_FILTER_INTERPRETER_H_

namespace gestures {

// This interpreter mainly detects finger merging and mark the merging
// finger(s) with a new flag GESTURE_FINGER_MERGE. The flag can be used
// in immediateinterpreter to suppress cursor jumps/moves caused by the
// merging/merged finger(s).

class FingerMergeFilterInterpreter : public FilterInterpreter {
  FRIEND_TEST(FingerMergeFilterInterpreterTest, SimpleTest);

 public:
  FingerMergeFilterInterpreter(PropRegistry* prop_reg, Interpreter* next,
                               Tracer* tracer);
  virtual ~FingerMergeFilterInterpreter() {}

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

 private:
  // Detects finger merge and appends GESTURE_FINGER_MERGE flag for a merged
  // finger or close fingers
  void UpdateFingerMergeState(const HardwareState& hwstate);

  bool IsSuspiciousAngle(const FingerState& fs) const;

  struct Start {
    float position_x;
    float position_y;
    stime_t start_time;
    bool operator==(const Start& that) const {
      return position_x == that.position_x &&
          position_y == that.position_y &&
          start_time == that.start_time;
    }
    bool operator!=(const Start& that) const {
      return !(*this == that);
    }
  };

  // Info about each contact's initial state
  std::map<short, Start> start_info_;

  std::set<short> merge_tracking_ids_;

  // Fingers that should never merge, as we've determined they aren't a merge
  std::set<short> never_merge_ids_;

  std::map<short, float> prev_x_displacement_;
  std::map<short, float> prev2_x_displacement_;

  // Flag to turn on/off the finger merge filter
  BoolProperty finger_merge_filter_enable_;

  // Distance threshold for close fingers
  DoubleProperty merge_distance_threshold_;

  // Maximum pressure value of a merged finger candidate
  DoubleProperty max_pressure_threshold_;

  // Min pressure of a merged finger candidate
  DoubleProperty min_pressure_threshold_;

  // Minimum touch major of a merged finger candidate
  DoubleProperty min_major_threshold_;

  // More criteria for filtering out false positives:
  // The touch major of a merged finger could be merged_major_pressure_ratio_
  // times of its pressure value at least or a merged finger could have a
  // very high touch major
  DoubleProperty merged_major_pressure_ratio_;
  DoubleProperty merged_major_threshold_;

  // We require that when a finger has displaced in the X direction more than
  // x_jump_min_displacement_, the next frame it must be at
  // x_jump_max_displacement_, else it's not considered a merged finger.
  DoubleProperty x_jump_min_displacement_;
  DoubleProperty x_jump_max_displacement_;

  // If a contact has displaced from the start position more than
  // suspicious_angle_min_displacement_, we require it to be at a particular
  // angle relative to the start position.
  DoubleProperty suspicious_angle_min_displacement_;

  // If a contact exceeds any of these (movement in a direction or age),
  // it is not marked as a merged contact.
  DoubleProperty max_x_move_;
  DoubleProperty max_y_move_;
  DoubleProperty max_age_;
};

}

#endif
