/*
 DatasetReader application (C#, .NET Framework)
 for the DATASET ID #MI-GESTURES-WGR-2022.

 	  Radu-Daniel Vatavu, Ph.D.
      MintViz Lab, MANSiD Research Center
      University Stefan cel Mare of Suceava
      Suceava 720229, Romania
      radu.vatavu @usm.ro
      http://www.eed.usv.ro/~vatavu

 	  Ovidiu-Ciprian Ungurean, Ph.D.
      MintViz Lab, MANSiD Research Center
	  University Stefan cel Mare of Suceava
	  Suceava 720229, Romania
	  ungurean.ovidiu@gmail.com 

 The academic publication for this dataset, and what should be used to cite it, is:
 
	  Vatavu, R.-D. and Ungurean, O.-C. (2022).Understanding Gesture Input 
      Articulation with Upper-Body Wearables for Users with Upper-Body Motor Impairments. 
      In CHI Conference on Human Factors in Computing Systems (CHI '22), April 29-May 5, 2022,
      New Orleans, LA, USA. ACM, New York, NY, USA, 16 pages. 
	  https://doi.org/10.1145/3491102.3501964

 This software is distributed under the "New BSD License" agreement:

 Copyright (c) 2022, Radu-Daniel Vatavu and Ovidiu-Ciprian Ungurean. All rights reserved.

 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
    *Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the University Stefan cel Mare of Suceava, nor 
      the names of its contributors may be used to endorse or promote products 
      derived from this software without specific prior written permission.

 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Radu-Daniel Vatavu OR 
 Ovidiu-Ciprian Ungurean BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 SUCH DAMAGE.
*/

using System;
using Geometry;

namespace GestureSharpToolkit
{
    /// <summary>
    /// Implements a processor for the points of a gesture.
    /// Some processors have arguments, which can be specified using the *args* parameter
    /// when instantiating the corresponding delegate function.
    /// </summary>
    public class GestureProcessor
    {
        /// <summary>
        /// Specifies a processor for the points of a gesture
        /// </summary>
        public delegate Point[] GesturePointProcessor(Point[] points, double[] args);

        private readonly GesturePointProcessor m_processor = null;
        private readonly double[] m_args = null;

        #region constructor

        /// <summary>
        /// Creates a new gesture processor
        /// </summary>
        public GestureProcessor(GesturePointProcessor processor, params double[] args)
        {
            this.m_processor = processor;
            this.m_args = new double[args.Length];
            for (int i = 0; i < args.Length; i++)
                this.m_args[i] = args[i];
        }

        #endregion

        #region point processors

        /// <summary>
        /// Applies the processor to the corresponding points
        /// </summary>
        public Point[] Apply(Point[] points)
        {
            return m_processor(points, m_args);
        }

        /// <summary>
        /// Resamples the set of points
        /// </summary>
        public static Point[] ResampleUniformLength(Point[] points, double[] args = null)
        {
            int samplingRate = ((args == null) || (args.Length < 1)) ? GestureConstants.DEFAULT_SAMPLING_RATE : (int)args[0];
            if (samplingRate < 2) throw new ArgumentException();
            return PointSet.Resample(points, samplingRate, false);
        }

        /// <summary>
        /// Recales the set of points
        /// </summary>
        public static Point[] ScaleToUnitBox(Point[] points, double[] args = null)
        {
            return PointSet.Scale(points);
        }

        /// <summary>
        /// Translates the set of points so that the new centroid becomes 0
        /// </summary>
        public static Point[] TranslateToOrigin(Point[] points, double[] args = null)
        {
            return PointSet.TranslateBy(points, PointSet.Centroid(points));
        }

        /// <summary>
        /// Rotates the set of points counterclockwise with *angle* (in radians) 
        /// around the centroid
        /// </summary>
        public static Point[] RotateBy(Point[] points, double[] args = null)
        {
            return PointSet.RotateBy(points, args[0]);
        }

        /// <summary>
        /// For accelerated motion, this filter removes the contribution of gravity:
        /// https://developer.android.com/reference/android/hardware/SensorEvent.html
        /// </summary>
        public static Point[] GravityRemoval(Point[] points, double[] args = null)
        {
            double alpha = ((args == null) || (args.Length < 1)) ? 0.8 : args[0];
            
            Point[] newPoints = new Point[points.Length];
            Point gravity = new Point(0, 0, 0);
            for (int i = 0; i < points.Length; i++)
            {
                gravity = new Point(
                    alpha * gravity.X + (1 - alpha) * points[i].X,
                    alpha * gravity.Y + (1 - alpha) * points[i].Y,
                    alpha * gravity.Z + (1 - alpha) * points[i].Z,
                    points[i].T,
                    points[i].StrokeID
                );
                newPoints[i] = points[i] - gravity;
            }
            return newPoints;
        }

        #endregion
    }
}
