// Created with MonoDevelop
//
//    Copyright (C) 2017 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.IO;
using System.Text;
using System.Text.RegularExpressions;


namespace Hiperscan.Spectroscopy.IO
{

    [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
    public class SubstanceDatabase
    {
        
        public class Lines             : List<List<string>> {}
        public class WorkingData       : Dictionary<int,List <string>> {}
        public class AliasData         : Dictionary<int,List<int>> {}
        public class Errors            : List<string>{}
        public class NotMeasurableData : Dictionary<string,List<string>> {}

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public class NameTable
        {
            public WorkingData DefaultNames {get;set;}
            public WorkingData LatinNames     {get;set;}
            public AliasData Aliase            {get;set;}
            
            public NameTable()
            {
                this.DefaultNames     = new SubstanceDatabase.WorkingData();
                this.LatinNames     = new SubstanceDatabase.WorkingData();
                this.Aliase            = new SubstanceDatabase.AliasData();
            }
        }
        
        public const string VERSION = "2.0";

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        private struct Parser
        {
            public const string UTF7Separator    = ";";
            public const string UnicodeSeparator = "\t";
        }

        private readonly Dictionary<string,string> names;

        private List<string> subset;
        private Dictionary<string,List<string>>.KeyCollection subset_keys;

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public SubstanceDatabase()
        {
            this.Substances = new Dictionary<string,List<string>>();
            this.names      = new Dictionary<string,string>();
        }
        
        private void UpdateNames()
        {
            this.names.Clear();
            Dictionary<string,List<string>> dict = new Dictionary<string,List<string>>();
            
            foreach (string key in this.Substances.Keys)
            {
                if (this.subset != null && !this.subset.Contains(key))
                    continue;
                
                dict.Add(key, null);
                
                List<string> substance = this.Substances[key];
                foreach (string name in substance)
                {
                    if (!this.names.ContainsKey(name))
                        this.names.Add(name, key);
                }
            }
            
            this.subset_keys = dict.Keys;
        }
        
        public string GetName(string key)
        {
            if (this.Substances.ContainsKey(key) && this.Substances[key].Count > 0)
                return this.Substances[key][0];
            else
                return key;
        }
        
        public List<string> GetNames(string key)
        {
            if (this.Substances.ContainsKey(key))
                return this.Substances[key];
            else
                return new List<string>(new string[] {key});
        }
        
        public bool ContainsKey(string key)
        {
            return this.Substances.ContainsKey(key);
        }
        
        public string[] GetKeysByName(string resolved_name)
        {
            List<string> keys = new List<string>();

            foreach (string id in this.Substances.Keys)
            {
                if (this.subset != null && !this.subset.Contains(id))
                    continue;
                
                foreach (string name in this.Substances[id])
                {
                    if (resolved_name == name && !keys.Contains(id))
                        keys.Add(id);
                }
            }
            
            return keys.ToArray();
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static SubstanceDatabase Read(string fname)
        {
            using (FileStream fstream = File.OpenRead(fname))
            {
                Encoding enc;
                Regex regex;
                
                if (Path.GetExtension(fname).ToLower() == ".csv")
                {
                    // UTF-7 encoded
                    enc = System.Text.Encoding.UTF7;
                    regex = new Regex(SubstanceDatabase.Parser.UTF7Separator);
                }
                else
                {
                    // Unicode encoded
                    enc = System.Text.Encoding.Default;
                    regex = new Regex(SubstanceDatabase.Parser.UnicodeSeparator);
                }
                
                using (StreamReader stream = new StreamReader(fstream, enc, true))
                {
                    SubstanceDatabase substancedb = new SubstanceDatabase();
                    string line;
                    int ix = 0;
                    
                    while ((line = stream.ReadLine()) != null)
                    {
                        if (line.StartsWith("#", StringComparison.InvariantCulture))
                            continue;
                        
                        ++ix;
                        string[] s = regex.Split(line);
                        
                        if (s.Length < 2)
                            continue;

                        if (substancedb.Substances.ContainsKey(s[0]))
                        {
                            Console.WriteLine("{0} ({1}): Entry already exists in dictionary: {2},{3}", fname, ix, s[0], s[1]);
                            continue;
                        }
                        
                        List<string> names = new List<string>();
                        for (int jx=1; jx < s.Length; ++jx)
                        {
                            string n = s[jx].Trim();
                            if (string.IsNullOrEmpty(n) == false)
                                names.Add(n);
                        }
                        
                        substancedb.Substances.Add(s[0], names);
                    }
                    stream.Close();
                    
                    substancedb.UpdateNames();
                    return substancedb;
                }
            }
        }
        
        public bool IsAlias(string name1, string name2)
        {            
            if (this.names.ContainsKey(name1) == false ||
                this.names.ContainsKey(name2) == false)
            {
                return false;
            }

            return (this.names[name1] == this.names[name2]);
        }

        public Dictionary<string, List<string>> Substances { get; }

        public List<string> Names
        {
            get { return new List<string>(this.names.Keys); }
        }
        
        public Dictionary<string,List<string>>.KeyCollection Keys
        {
            get 
            { 
                if (this.subset_keys != null)
                    return this.subset_keys;
                else
                    return this.Substances.Keys; 
            }
        }
        
        public List<string> Subset
        {
            get { return this.subset; }
            set
            { 
                this.subset = value;
                this.UpdateNames();
            }
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static Lines ReadSubstanceFile(string file_name)
        {
            Lines values = new Lines();
            string text = null;
            using (StreamReader sr = new StreamReader(file_name, Encoding.UTF8))
            {
                text = sr.ReadToEnd();
            }
            text = text.Replace("\"", string.Empty);
            text = text.Replace("\r", string.Empty);
            using (StreamWriter sw = new StreamWriter(file_name, false, Encoding.UTF8))
            {
                sw.Write(text);
            }
            
            string[] all_lines = text.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries);
            foreach(string line in all_lines)
            {
                if (line.StartsWith("#", StringComparison.InvariantCulture) == true)
                    continue;
                string[] line_data = line.Split(new char[]{'\t'}, StringSplitOptions.RemoveEmptyEntries);
                if (line_data.Length < 2)
                    continue;
                for (int i = 0; i < line_data.Length; i++)
                    line_data[i] = line_data[i].Trim();
                values.Add(new List<string>(line_data));  
            }
            return values;
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static WorkingData CheckLineData(Lines lines, Errors errors)
        {
            WorkingData values = new WorkingData();
            foreach (List<string> entry in lines)
            {
                int id;
                try
                {
                    id = Int32.Parse(entry[0]);
                }
                catch(Exception)
                {
                    errors.Add(string.Format("Ungueltige Substanz-ID: {0}", entry[0]));
                    continue;
                }
                
                if (values.ContainsKey(id) == true)
                {
                    errors.Add(string.Format("Substanz-ID {0} ist mehrmals vergeben", entry[0]));
                    continue;
                }
                
                bool success = true;
                for (int i = 1; i < entry.Count; i++)
                {
                    if (entry[i].Length > 75)
                    {
                        errors.Add(string.Format("Substanzname {0} ist zu lang (max 75 Zeichen).", entry[i]));
                        success = false;
                    }
                }
                
                if (success == false)
                    continue;
                
                values.Add(id, new List<string>(entry.GetRange(1, entry.Count - 1)));
            }
            
            return values;
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static void CrossCheck(WorkingData default_data, WorkingData latin_data, Errors errors)
        {
            foreach(int id in default_data.Keys)
            {
                if (latin_data.ContainsKey(id) == false)
                    errors.Add(string.Format("Latin-Namenslist enthält keinen Substanz-Schlüssel {0}.", id));
            }
            
            foreach(int id in latin_data.Keys)
            {
                if (default_data.ContainsKey(id) == false)
                    errors.Add(string.Format("Dafault-Namensliste enthält keinen Substanz-Schlüssel {0}.", id));
            }
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static NameTable CreateNameTable(WorkingData default_names, WorkingData latin_names, Errors errors)
        {
            NameTable name_table = new NameTable();
            foreach (int default_id in default_names.Keys)
            {
                int main_id = -1;
                
                foreach(string default_name in default_names[default_id])
                {
                    foreach(int compare_id in name_table.DefaultNames.Keys)
                    {
                        if (name_table.DefaultNames[compare_id].Contains(default_name) == true)
                        {
                            main_id = compare_id;
                            break;    
                        }
                    }
                }
                
                if (main_id == -1)
                {
                    name_table.DefaultNames.Add(default_id, new List<string>());
                    foreach( string default_name_1 in default_names[default_id])
                    {
                        bool found = false;
                        foreach( string default_name_2 in name_table.DefaultNames[default_id])
                        {
                            if (default_name_1.ToLower() == default_name_2.ToLower())
                                found = true; 
                        }
                        if (found == false)
                            name_table.DefaultNames[default_id].Add(default_name_1);
                    }
                    
                    name_table.LatinNames.Add(default_id, new List<string>());
                    foreach( string latin_name_1 in latin_names[default_id])
                    {
                        bool found = false;
                        foreach( string latin_name_2 in name_table.LatinNames[default_id])
                        {
                            if (latin_name_1.ToLower() == latin_name_2.ToLower())
                                found = true; 
                        }
                        if (found == false)
                            name_table.LatinNames[default_id].Add(latin_name_1);
                    }
                    
                    name_table.Aliase.Add(default_id, new List<int>());
                    name_table.Aliase[default_id].Add(default_id);
                }
                else
                {
                    name_table.Aliase[main_id].Add(default_id);
                    foreach( string default_name_1 in default_names[default_id])
                    {
                        bool found = false;
                        foreach( string default_name_2 in name_table.DefaultNames[main_id])
                        {
                            if (default_name_1.ToLower() == default_name_2.ToLower())
                                found = true; 
                        }
                        if (found == false)
                            name_table.DefaultNames[main_id].Add(default_name_1);
                    }
                    
                    foreach( string latin_name_1 in latin_names[default_id])
                    {
                        bool found = false;
                        foreach( string latin_name_2 in name_table.LatinNames[main_id])
                        {
                            if (latin_name_1.ToLower() == latin_name_2.ToLower())
                                found = true; 
                        }
                        if (found == false)
                            name_table.LatinNames[main_id].Add(latin_name_1);
                    }
                }
            }
            
            foreach (int latin_id1 in name_table.LatinNames.Keys)
            {
                foreach (int latin_id2 in name_table.LatinNames.Keys)
                {
                    if (latin_id1 == latin_id2)
                        continue;
                    
                    foreach (string latin_name in name_table.LatinNames[latin_id1])
                    {
                        if (name_table.LatinNames[latin_id2].Contains(latin_name) == true)
                            errors.Add(string.Format("Latin-Name {0}: {1} <-> {2}.",
                                       latin_name, latin_id1, latin_id2));
                    }
                }
            }
            
            return name_table;
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static NotMeasurableData GetNotMeausrableSubstances(string file_name, Errors errors)
        {
            NotMeasurableData values = new NotMeasurableData();
            if (File.Exists(file_name) == false)
            {
                Console.WriteLine("Keine Datei {0} gefunden.", file_name);
                return values;
            }
            
            try
            {
                using(StreamReader sr = new StreamReader(file_name, Encoding.UTF8))
                {
                    int line_counter = 1;
                    string group_name = string.Empty;
                    string line;
                    while((line = sr.ReadLine()) != null)
                    {
                        line = line.Trim();
                        line = line.Trim(new char[]{'\t'});
                        if (line == string.Empty)
                        {
                            line_counter++;
                            continue;
                        }
                        
                        if (line.StartsWith("Substance-Group") == true)
                        {
                            string[] group_data = line.Split(new string[]{"::"},StringSplitOptions.RemoveEmptyEntries);
                            if (group_data.Length != 2)
                            {
                                errors.Add(string.Format("Kann Substanzeintrag in Zeile {0} nicht auflösen", line_counter));
                                group_name = string.Empty;
                            }
                            else
                            {
                                group_name = group_data[1];
                                if (values.ContainsKey(group_name) == true)
                                {
                                    errors.Add(string.Format("Gruppe {0} ist bereits definiert worden", group_name));
                                    group_name = string.Empty;
                                }
                                else
                                {
                                    values.Add(group_name, new List<String>());
                                }
                            }
                        }
                        else
                        {
                            if (group_name == string.Empty)
                            {
                                errors.Add(string.Format("Eintrag in Zeile {0} kann keiner Gruppe zugeordent werden.", line_counter));
                            }
                            else
                            {
                                string[] substance_data = line.Split(new string[]{"::"},StringSplitOptions.RemoveEmptyEntries);
                                if (substance_data.Length != 2)
                                {
                                    errors.Add(string.Format("Ungültiger Substanzeintrag in Zeile {0}", line_counter));
                                }
                                else
                                {
                                    values[group_name].Add(line);
                                }
                            }
                        }
                        line_counter++;
                    }
                }
            }
            catch(Exception ex)
            {
                errors.Add(ex.Message);
                values = new NotMeasurableData();
            }
            
            List<string> remove_list = new List<string>();
            foreach(string entry in values.Keys)
            {
                if (values[entry].Count == 0)
                    remove_list.Add(entry);
            }
            
            foreach (string entry in remove_list)
                values.Remove(entry);
            
            return values;    
        }

        [Obsolete("Replaced by Hiperscan.CID.SubstanceStructrue.Structure")]
        public static void CheckNotMeasurableData(NotMeasurableData data, Errors errors)
        {
            foreach(string group_name in data.Keys)
            {
                Dictionary<string, int> substance_self_control_latin = new Dictionary<string, int>();
                Dictionary<string, int> substance_self_control_default = new Dictionary<string, int>();
                foreach(string entry in data[group_name])
                {
                    string[] substance_data = entry.Split(new string[]{"::"},StringSplitOptions.RemoveEmptyEntries);
                    string[] default_names = substance_data[0].Split(new string[]{"\t"},StringSplitOptions.RemoveEmptyEntries);
                    string[] latin_names   = substance_data[1].Split(new string[]{"\t"},StringSplitOptions.RemoveEmptyEntries);
                    foreach (string default_name in default_names)
                    {
                        if (substance_self_control_default.ContainsKey(default_name.ToLower()) == true)
                        {
                            errors.Add(string.Format("Der Default-Name {0} ist für die Gruppe {1} mehrmals definiert",
                                                         default_name, group_name));
                        }
                        else
                        {
                            substance_self_control_default.Add(default_name.ToLower(),0);
                        }
                    }
                    foreach (string latin_name in latin_names)
                    {
                        if (substance_self_control_latin.ContainsKey(latin_name.ToLower()) == true)
                        {
                            errors.Add(string.Format("Der Latin-Name {0} ist für die Gruppe {1} mehrmals definiert",
                                                         latin_name, group_name));
                        }
                        else
                        {
                            substance_self_control_latin.Add(latin_name.ToLower(),0);
                        }
                    }
                }
            }
        }
    }
}
