// Created with MonoDevelop
//
//    Copyright (C) 2022 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 Hiperscan.Unix;

namespace Hiperscan.Common
{
    public class ProcessDurationForecast
    {
        public ProcessDurationForecast(
            long total_counts, 
            long  intervall, 
            bool use_gc_collector, 
            bool post_increment, 
            bool quiet,
            int gc_sleeping_time = 2000)
        {
            this.Intervall = intervall;
            this.TotalCount = total_counts;
            this.CurrentCount = 0;
            this.Sync = new object();
            this.Quiet = quiet;
            this.UseGC = use_gc_collector;
            this.GCSleepingTime = gc_sleeping_time;
            this.WriteInitialForecast();
        }

        private void WriteInitialForecast()
        {
            lock (this.Sync)
            {
                if (this.UseGC)
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    System.Threading.Thread.Sleep(this.GCSleepingTime);
                }

                this.Timer = DateTime.Now;

                if (this.Quiet)
                    return;

                Console.WriteLine(Catalog.GetString("Process (after {0,5}%) started at: {1}:{2:D2}:{3:D2}"),
                                  0.0.ToString("f1"),
                              this.Timer.Hour,
                              this.Timer.Minute,
                              this.Timer.Second);
            }
        }

        public void Increment()
        {
            lock(this.Sync)
            {
                if (this.PostIncrement == false)
                    this.CurrentCount++;

                if ((this.CurrentCount % this.Intervall == 0) && this.CurrentCount != 0) 
                {
                    GC.Collect();
                    GC.WaitForPendingFinalizers();
                    System.Threading.Thread.Sleep(this.GCSleepingTime);

                    if (this.Quiet == false)
                        Console.WriteLine(this.GetExpectedDurationTime());
                }

                if (this.PostIncrement == true)
                    this.CurrentCount++;
            }
        }

        public void WriteSummary()
        {
            lock (this.Sync)
            {
                if (this.CurrentCount == this.TotalCount)
                {
                    if (this.Quiet == false)
                    {

                        TimeSpan diff = DateTime.Now - this.Timer;

                        Console.WriteLine(Catalog.GetString("Process (after {0,5}%) ran: {1}:{2:D2}:{3:D2} hours"),
                                          100.0.ToString("f1"),
                                      (long)diff.TotalHours,
                                     diff.Minutes,
                                     diff.Seconds);
                    }

                    if (this.UseGC)
                    {
                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                        System.Threading.Thread.Sleep(this.GCSleepingTime);
                    }
                }
            }
        }

        private long GetTicksPerUnit()
        {
            if (this.CurrentCount == 0)
                return 0;

            return (DateTime.Now - this.Timer).Ticks / this.CurrentCount;
        }

        private string GetExpectedDurationTime()
        {
            long expected_ticks = (this.TotalCount - this.CurrentCount) * this.GetTicksPerUnit();
            TimeSpan time = new TimeSpan(expected_ticks);

            double percent = (double)this.CurrentCount / this.TotalCount * 100;

            int hours = (int) time.TotalHours;

            return String.Format(Catalog.GetString("Process (after {0,5}%) is still running about: {1}:{2:D2}:{3:D2} hours"), 
                                 percent.ToString("f1"),
                                 hours,
                                 time.Minutes,
                                 time.Seconds);

        }

        private bool Quiet { get; set; }
        private long TotalCount { get; set; }
        private long Intervall { get; set; }
        private DateTime Timer { get; set; }
        private long CurrentCount { get; set; } 
        private object Sync { get; set; }
        private bool UseGC { get; set; }
        private bool PostIncrement { get; set; }
        public int GCSleepingTime { get; private set; } = 2000;
    }
}
