// Created with MonoDevelop
//
//    QuickStep: Acquire, view and identify spectra 
//    Copyright (C) 2017 Thomas Klose & Torsten Schache, Hiperscan GmbH Dresden, Germany
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.


using System;
using System.Collections.Generic;
using System.Linq;
using Hiperscan.Spectroscopy;
using Hiperscan.Spectroscopy.IdentModule;

namespace Hiperscan.Extensions.ApoIdent
{
	public class FuzzyNameSet : IComparable<FuzzyNameSet>
	{
        private static class EditDistance
        {
            private struct JaroMetrics
            {
                public int Matches;
                public int Transpositions;
            }

            private static EditDistance.JaroMetrics Matches(string s1, string s2)
            {
                string text;
                string text2;
                if (s1.Length > s2.Length)
                {
                    text = s1;
                    text2 = s2;
                }
                else
                {
                    text = s2;
                    text2 = s1;
                }
                int num = Math.Max(text.Length / 2 - 1, 0);
                int[] array = new int[text2.Length];
                int i;
                for (i = 0; i < array.Length; i++)
                {
                    array[i] = -1;
                }
                bool[] array2 = new bool[text.Length];
                int num2 = 0;
                for (int j = 0; j < text2.Length; j++)
                {
                    char c = text2[j];
                    int k = Math.Max(j - num, 0);
                    int num3 = Math.Min(j + num + 1, text.Length);
                    while (k < num3)
                    {
                        if (!array2[k] && c == text[k])
                        {
                            array[j] = k;
                            array2[k] = true;
                            num2++;
                            break;
                        }
                        k++;
                    }
                }
                char[] array3 = new char[num2];
                char[] ms2 = new char[num2];
                i = 0;
                int num4 = 0;
                while (i < text2.Length)
                {
                    if (array[i] != -1)
                    {
                        array3[num4] = text2[i];
                        num4++;
                    }
                    i++;
                }
                i = 0;
                num4 = 0;
                while (i < text.Length)
                {
                    if (array2[i])
                    {
                        ms2[num4] = text[i];
                        num4++;
                    }
                    i++;
                }
                int num5 = array3.Where((char t, int mi) => t != ms2[mi]).Count<char>();
                EditDistance.JaroMetrics result;
                result.Matches = num2;
                result.Transpositions = num5 / 2;
                return result;
            }

            public static float Jaro(string s1, string s2)
            {
                EditDistance.JaroMetrics jaroMetrics = EditDistance.Matches(s1, s2);
                float num = (float)jaroMetrics.Matches;
                int transpositions = jaroMetrics.Transpositions;
                float result;
                if (num < 0.01f)
                {
                    result = 0f;
                }
                else
                {
                    float num2 = (num / (float)s1.Length + num / (float)s2.Length + (num - (float)transpositions) / num) / 3f;
                    result = num2;
                }
                return result;
            }
        }


        public const double SCORE_THRESHOLD = 0.8;

        public FuzzyNameSet (string classifier_name, string substance_name, bool over_id_resolved,
                             SubstanceInfo   substance_info)
        {
            this.Score              = 0;
            this.SubstanceName      = substance_name;
            this.ModelClassName     = classifier_name;
            this.ResolvedOverId     = over_id_resolved;
            this.SubstanceInfo      = substance_info;
            this.FindBestSubstanceName();
        }

        private void FindBestSubstanceName()
        {
            string best_substance_name = String.Empty;
            foreach (string substance_name in this.SubstanceInfo.AllNames)
            {
                double score = this.CalcJaroScore(this.SubstanceName, substance_name);

                if (score > this.Score)
                {
                    this.Score = score;
                    best_substance_name = substance_name;
                }
            }

            this.SubstanceName = best_substance_name;
        }

        private double CalcJaroScore(string reference_substance_name, string substance_name)
        {
            string s1 = reference_substance_name.ToLower().Replace(" ", String.Empty);
            string s2 = substance_name.ToLower().Replace(" ", String.Empty);

            //s1 = "Natriumcitrat-Dihydrat";
            //s2 = "Natriumcitrat";

            return EditDistance.Jaro(s1, s2);
        }


		#region IComparable implementation

		public int CompareTo (FuzzyNameSet other)
		{
			return other.Score.CompareTo(this.Score);
		}

		#endregion

        public double Score 				{get; private set;}
		public string SubstanceName			{get; private set;}
		public string ModelClassName		{get; private set;}
        public bool ResolvedOverId          {get; private set;}
        public SubstanceInfo SubstanceInfo  {get; private set;}
	}
}

