/*************************************************************************************************
  Required Notice: Copyright (C) EPPlus Software AB. 
  This software is licensed under PolyForm Noncommercial License 1.0.0 
  and may only be used for noncommercial purposes 
  https://polyformproject.org/licenses/noncommercial/1.0.0/

  A commercial license to use this software can be purchased at https://epplussoftware.com
 *************************************************************************************************
  Date               Author                       Change
 *************************************************************************************************
  01/27/2020         EPPlus Software AB       Initial release EPPlus 5
 *************************************************************************************************/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Logical;
using OfficeOpenXml.FormulaParsing.Excel.Functions.MathFunctions;

namespace OfficeOpenXml.FormulaParsing.Logging
{
    internal class TextFileLogger : IFormulaParserLogger
    {
        private StreamWriter _sw;
        private const string Separator = "=================================";
        private int _count;
        private DateTime _startTime = DateTime.Now;
        private Dictionary<string, int> _funcs = new Dictionary<string, int>();
        private Dictionary<string, long> _funcPerformance = new Dictionary<string, long>();
        internal TextFileLogger(FileInfo fileInfo)
        {
#if (Core)
            _sw = new StreamWriter(new FileStream(fileInfo.FullName, FileMode.Append));
#else
            _sw = new StreamWriter(fileInfo.FullName);
#endif
            _sw.AutoFlush = true;
        }

        private void WriteSeparatorAndTimeStamp()
        {
            _sw.WriteLine(Separator);
            _sw.WriteLine("Timestamp: {0}", DateTime.Now);
            _sw.WriteLine();
        }

        private void WriteAddressInfo(ParsingContext context)
        {
            if (context.CurrentCell.Row > 0)
            {
                _sw.WriteLine("Worksheet: {0}", context.CurrentWorksheet?.Name ?? "<not specified>");
                _sw.WriteLine("Address: {0}", context.CurrentCell.Address ?? "<not available>");
            }
        }

        public void Log(ParsingContext context, Exception ex)
        {
            WriteSeparatorAndTimeStamp();
            WriteAddressInfo(context);
            _sw.WriteLine(ex);
            _sw.WriteLine();
        }

        public void Log(ParsingContext context, string message)
        {
            WriteSeparatorAndTimeStamp();
            WriteAddressInfo(context);
            _sw.WriteLine(message);
            _sw.WriteLine();
        }

        public void Log(string message)
        {
            WriteSeparatorAndTimeStamp();
            _sw.WriteLine(message);
            _sw.WriteLine();
        }

        public void LogCellCounted()
        {
            _count++;
            if (_count%500 == 0)
            {
                _sw.WriteLine(Separator);
                var timeEllapsed = DateTime.Now.Subtract(_startTime);
                _sw.WriteLine("{0} cells parsed, time {1} seconds", _count, timeEllapsed.TotalSeconds);

                var funcs = _funcs.Keys.OrderByDescending(x => _funcs[x]).ToList();
                foreach (var func in funcs)
                {
                    _sw.Write(func + "  - " + _funcs[func]);
                    if (_funcPerformance.ContainsKey(func))
                    {
                        _sw.Write(" - avg: " + _funcPerformance[func]/_funcs[func] + " milliseconds");
                    }
                    _sw.WriteLine();
                }
                _sw.WriteLine();
                _funcs.Clear();

            }
        }

        public void LogFunction(string func)
        {
            if (!_funcs.ContainsKey(func))
            {
                _funcs.Add(func, 0);
            }
            _funcs[func]++;
        }

        public void LogFunction(string func, long milliseconds)
        {
            if (!_funcPerformance.ContainsKey(func))
            {
                _funcPerformance[func] = 0;
            }
            _funcPerformance[func] += milliseconds;
        }

        public void Dispose()
        {
            _sw.Close(); 
            _sw.Dispose();
        }
    }
}
