/// <summary>
        /// preprocess input data and transform to 1d feature vector for DTW 
        /// according to specific gesture model
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private ArrayList PreprocessGesture(Gesture input, int gid)
        {
            ArrayList dtw_data = new ArrayList();
            // each frame
            for(int i=0; i<input.data.Count; i++)
            {
                // Extract the coordinates of the points.
                List<Point> pts = new List<Point>();
                Point shoulderRight = new Point(
                    input.data[i].Joints[JointType.ShoulderRight].Position.X,
                    input.data[i].Joints[JointType.ShoulderRight].Position.Y);
                Point shoulderLeft = new Point(
                    input.data[i].Joints[JointType.ShoulderLeft].Position.X,
                    input.data[i].Joints[JointType.ShoulderLeft].Position.Y);

                foreach (Joint j in input.data[i].Joints)
                {
                    if (GESTURE_CONFIG[gid].jointWeights[j.JointType] > 0)
                    {
                        Point p = new Point(j.Position.X, j.Position.Y);
                        pts.Add(p);
                    }
                }

                // Center the data
                var center = new Point(
                    input.data[i].Joints[JointType.ShoulderCenter].Position.X,
                    input.data[i].Joints[JointType.ShoulderCenter].Position.Y);
                for (int k = 0; k < pts.Count; k++)
                    pts[k] = new Point(pts[k].X - center.X, pts[k].Y - center.Y);

                // Normalization of the coordinates
                double shoulderDist =
                    Math.Sqrt(Math.Pow((shoulderLeft.X - shoulderRight.X), 2) +
                              Math.Pow((shoulderLeft.Y - shoulderRight.Y), 2));
                for (int k = 0; k < pts.Count; k++)
                    pts[k] = new Point(pts[k].X / shoulderDist, pts[k].Y / shoulderDist);

                // save in 1d double array
                double[] feat_vec = new double[pts.Count * 2];
                for (int k = 0; k < pts.Count; k++)
                {
                    feat_vec[k * 2] = pts[k].X;
                    feat_vec[k * 2 + 1] = pts[k].Y;
                }

                dtw_data.Add(feat_vec);
            }

            return dtw_data;
        }
        /// <summary>
        /// read database data from files
        /// </summary>
        public bool LoadGestureDatabase(string database_dir)
        {
            // enumerate gesture directories
            if (!Directory.Exists(database_dir))
                return false;

            // clear
            GESTURE_LIST.Clear();
            GESTURE_CONFIG.Clear();
            GESTURE_DATABASE.Clear();

            // load all gesture config
            if (!LoadAllGestureConfig())
            {
                Debug.WriteLine("Fail to load gesture config.");
                return false;
            }

            // load actual gesture data for each type
            foreach (int gid in GESTURE_LIST.Keys)
            {
                string gdir = GESTURE_DATABASE_DIR + GESTURE_LIST[gid] + "\\";
                List<Gesture> cur_gestures = new List<Gesture>();
                IEnumerable<string> gesture_files = Directory.EnumerateFiles(gdir, "*.xml");
                foreach (string filename in gesture_files)
                {
                    Gesture gtemp = new Gesture();
                    gtemp.data = KinectRecorder.ReadFromSkeletonFile(filename);
                    gtemp.name = GESTURE_LIST[gid];

                    if (gtemp.data.Count > gesture_max_len)
                        gesture_max_len = gtemp.data.Count;
                    if (gtemp.data.Count < gesture_min_len)
                        gesture_min_len = gtemp.data.Count;

                    cur_gestures.Add(gtemp);
                }

                // add to database
                if (cur_gestures.Count > 0)
                    GESTURE_DATABASE.Add(gid, cur_gestures);
            }

            // no actual model data
            if (GESTURE_DATABASE.Count == 0)
                return false;

            return true;
        }
        /// <summary>
        /// match to each database template
        /// </summary>
        public double MatchToDatabase(Gesture input, out string res)
        {
            // find the most similar gesture in database to test gesture
            double mindist = double.PositiveInfinity;
            res = "Unknown";
            foreach (int gid in GESTURE_DATABASE.Keys)
            {
                foreach (Gesture temp in GESTURE_DATABASE[gid])
                {
                    double dist = GestureSimilarity(input, temp, gid);
                    if(dist < mindist)
                    {
                        mindist = dist;
                        res = GESTURE_LIST[gid];
                    }
                }
            }

            return mindist;
        }
        /// <summary>
        /// measure similarity between input gesture and a gesture template
        /// </summary>
        public double GestureSimilarity(Gesture input, Gesture template, int gid)
        {
            ArrayList input_data = PreprocessGesture(input, gid);
            ArrayList temp_data = PreprocessGesture(template, gid);

            double dist = DynamicTimeWarping(input_data, temp_data, GESTURE_CONFIG[gid].jointWeights);

            return dist;
        }