// Created with MonoDevelop
//
//    Copyright (C) 2018 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;


namespace Hiperscan.Spectroscopy.IO
{
    public static class PlainCSV
    {
        private const string MASKED_SEPARATOR = "<<MASKED_SEPARATOR>>";
        private const string MASKED_NEWLINE   = "<<MASKED_NEWLINE>>";

        public static char Separator = ';';

        private static string RecognizeMaskedSemikolonAndNewLine(string content)
        {
            bool is_masked = false;
            StringBuilder sb = new  StringBuilder();
            foreach (char c in content)
            {
                if (c == '\"')
                    is_masked = !is_masked;
                if (c == PlainCSV.Separator && is_masked == true)
                    sb.Append(PlainCSV.MASKED_SEPARATOR);
                else if (c == '\n' && is_masked == true)
                    sb.Append(PlainCSV.MASKED_NEWLINE);
                else
                    sb.Append(c);
            }
            return sb.ToString();
        }

        private static string RecognizeMaskedQuotes(string text)
        {
            StringBuilder corrected_text = new StringBuilder(text);
            if (text.Contains("\"") == true)
            {
                corrected_text = new StringBuilder();
                text = text.Substring(1, text.Length - 2);
                bool correct = false;
                foreach(char c in text)
                {
                    if (c == '\"')
                        correct = !correct;
                    if (c != '\"' || correct == false)
                        corrected_text.Append(c);
                }
            }
            return corrected_text.ToString();
        }

        private static string[] GetCellData(string line)
        {
            char[] trim = new char[]{' ','\n', '\r', '\t'};
            string[] cell_data = line.Split(new char[]{PlainCSV.Separator}, StringSplitOptions.None);

            for (int i = 0; i < cell_data.Length; i++)
            {
                cell_data[i] = PlainCSV.RecognizeMaskedQuotes(cell_data[i]);
                cell_data[i] = cell_data[i].Replace(PlainCSV.MASKED_SEPARATOR, PlainCSV.Separator.ToString());
                cell_data[i] = cell_data[i].Replace(PlainCSV.MASKED_NEWLINE, "}n");
                cell_data[i] = cell_data[i].Trim(trim);
            }
            return cell_data;
        }

        private static string MaskQuotes(string text)
        {
            StringBuilder corrected_text = new StringBuilder();
            foreach (char c in text)
            {
                if (c == '\"')
                    corrected_text.Append("\"\"");
                else
                    corrected_text.Append(c.ToString());
            }
            return "\"" + corrected_text.ToString() + "\"";
        }

        public static Dictionary<int,List<string>> Read(string file)
        {            
            using (Stream stream = File.Open(file, FileMode.Open, FileAccess.Read))
            {
                return PlainCSV.Read(stream);
            }
        }

        public static Dictionary<int,List<string>> Read(Stream stream)
        {
            Dictionary<int,List<string>> data = new Dictionary<int,List<string>>();
            int line_count = 0;

            using (StreamReader sr = new StreamReader(stream, System.Text.Encoding.UTF8))
            {
                string text = PlainCSV.RecognizeMaskedSemikolonAndNewLine(sr.ReadToEnd());

                foreach (string line in text.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries))
                    data.Add(++line_count, new List<string>(PlainCSV.GetCellData(line)));
            }

            return data;
        }

        public static void Write(Dictionary<int,List<string>> data, string file, bool append = false)
        {
            StringBuilder sb = new StringBuilder();
            List<List<string>> tmp_cell_data = new List<List<string>>(data.Values);

            if (File.Exists(file) == true && append == true)
                sb.Append("\n");

            for (int i = 0; i < tmp_cell_data.Count; i++)
            {
                List<string> cell_row = tmp_cell_data[i];

                for (int j = 0; j < cell_row.Count; j++)
                {
                    string cell = PlainCSV.MaskQuotes(cell_row[j]);
                    sb.Append(cell);

                    if (j < cell_row.Count - 1)
                        sb.Append(PlainCSV.Separator);
                }

                if (i < tmp_cell_data.Count - 1)
                    sb.Append("\n");
            }

            using (StreamWriter sw = new StreamWriter(file, append, Encoding.UTF8))
            {
                sw.Write(sb.ToString());
            }
        }
    }
}
