/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.incallui.answer.impl.classifier;

import android.content.Context;
import android.hardware.SensorEvent;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import com.android.dialer.common.ConfigProviderBindings;

/** An classifier trying to determine whether it is a human interacting with the phone or not. */
class HumanInteractionClassifier extends Classifier {

  private static final String CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED =
      "answer_false_touch_detection_enabled";

  private final StrokeClassifier[] mStrokeClassifiers;
  private final GestureClassifier[] mGestureClassifiers;
  private final HistoryEvaluator mHistoryEvaluator;
  private final boolean mEnabled;

  HumanInteractionClassifier(Context context) {
    DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();

    // If the phone is rotated to landscape, the calculations would be wrong if xdpi and ydpi
    // were to be used separately. Due negligible differences in xdpi and ydpi we can just
    // take the average.
    // Note that xdpi and ydpi are the physical pixels per inch and are not affected by scaling.
    float dpi = (displayMetrics.xdpi + displayMetrics.ydpi) / 2.0f;
    mClassifierData = new ClassifierData(dpi, displayMetrics.heightPixels);
    mHistoryEvaluator = new HistoryEvaluator();
    mEnabled =
        ConfigProviderBindings.get(context)
            .getBoolean(CONFIG_ANSWER_FALSE_TOUCH_DETECTION_ENABLED, true);

    mStrokeClassifiers =
        new StrokeClassifier[] {
          new AnglesClassifier(mClassifierData),
          new SpeedClassifier(mClassifierData),
          new DurationCountClassifier(mClassifierData),
          new EndPointRatioClassifier(mClassifierData),
          new EndPointLengthClassifier(mClassifierData),
          new AccelerationClassifier(mClassifierData),
          new SpeedAnglesClassifier(mClassifierData),
          new LengthCountClassifier(mClassifierData),
          new DirectionClassifier(mClassifierData)
        };

    mGestureClassifiers =
        new GestureClassifier[] {
          new PointerCountClassifier(mClassifierData), new ProximityClassifier(mClassifierData)
        };
  }

  @Override
  public void onTouchEvent(MotionEvent event) {

    // If the user is dragging down the notification, they might want to drag it down
    // enough to see the content, read it for a while and then lift the finger to open
    // the notification. This kind of motion scores very bad in the Classifier so the
    // MotionEvents which are close to the current position of the finger are not
    // sent to the classifiers until the finger moves far enough. When the finger if lifted
    // up, the last MotionEvent which was far enough from the finger is set as the final
    // MotionEvent and sent to the Classifiers.
    addTouchEvent(event);
  }

  private void addTouchEvent(MotionEvent event) {
    mClassifierData.update(event);

    for (StrokeClassifier c : mStrokeClassifiers) {
      c.onTouchEvent(event);
    }

    for (GestureClassifier c : mGestureClassifiers) {
      c.onTouchEvent(event);
    }

    int size = mClassifierData.getEndingStrokes().size();
    for (int i = 0; i < size; i++) {
      Stroke stroke = mClassifierData.getEndingStrokes().get(i);
      float evaluation = 0.0f;
      for (StrokeClassifier c : mStrokeClassifiers) {
        float e = c.getFalseTouchEvaluation(stroke);
        evaluation += e;
      }

      mHistoryEvaluator.addStroke(evaluation);
    }

    int action = event.getActionMasked();
    if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
      float evaluation = 0.0f;
      for (GestureClassifier c : mGestureClassifiers) {
        float e = c.getFalseTouchEvaluation();
        evaluation += e;
      }
      mHistoryEvaluator.addGesture(evaluation);
    }

    mClassifierData.cleanUp(event);
  }

  @Override
  public void onSensorChanged(SensorEvent event) {
    for (Classifier c : mStrokeClassifiers) {
      c.onSensorChanged(event);
    }

    for (Classifier c : mGestureClassifiers) {
      c.onSensorChanged(event);
    }
  }

  boolean isFalseTouch() {
    float evaluation = mHistoryEvaluator.getEvaluation();
    return evaluation >= 5.0f;
  }

  public boolean isEnabled() {
    return mEnabled;
  }

  @Override
  public String getTag() {
    return "HIC";
  }
}
