using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Linq;

namespace GenerateVDPCycle
{
	class Program
	{
		static List<GenCycle> tasks = new List<GenCycle>();
		static void Main(string[] args)
		{
			var r = new Random();
			for(var i = 1; i <= 48; i++)
			{
				var a = new GenCycle(i);
				a.Start();
				tasks.Add(a);
			}
			Task.Delay(1000 * 5).Wait();
			while (true)
      {
				for (var i = 0; i < tasks.Count; i++)
				{
					if (i % 10 == 0 && i != 0)
						Console.WriteLine();
					Console.Write(tasks[i].GetSmallStatus() + "|");
				}
				Console.WriteLine();
				Task.Delay(1000 * 60 * 10).Wait();
			}
		}
	}

	public class GenCycle
	{
		private int vdp = 0;
		
		private int curCycle = 0;
		private DateTime factStart = DateTime.Now;
		private DateTime factEnd = DateTime.Now;
		private DateTime thinkEnd = DateTime.Now;
		private bool cycle = false;
		private Task taskCycle = null;

		public GenCycle(int vdp)
		{
			this.vdp = vdp;
		}

		public bool GetCurrCycle()
		{
			try
			{
				using (var db = new DB.VdpDB())
				{
					var tmp = (from u in db.Cycles
										 where
											u.NumVdp == vdp
										 orderby u.FactStart descending
										 select u).FirstOrDefault();
					if (tmp == null)
					{
						curCycle = 0;
						return false;
					}
					curCycle = tmp.NumCycle;
					factStart = tmp.FactStart;
					factEnd = tmp.FactEnd;
				}
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				return false;
			}
			return true;
		}
		public void GetNextCycle()
		{
			var r = new Random();
			var a = r.Next(100);
			switch (curCycle)
			{
				case 0:  curCycle =
						 (a < 50) ? 14 : 2; break;
				case 1:  curCycle = 2;  break;
				case 2:  curCycle = 5;  break;
				case 5:  curCycle = 6;  break;
				case 6:  curCycle = 7;  break;
				case 7:  curCycle =
						  (a < 20) ? 5 : 8; break;
				case 8:  curCycle = 9;  break;
				case 9:  curCycle = 10; break;
				case 10: curCycle = 11; break;
				case 11: curCycle = 12; break;
				case 12: curCycle = 0;  break;
				case 14: curCycle = 15; break;
				case 15: curCycle = 16; break;
				case 16: curCycle = 1;  break;
				default: curCycle = 0;  break;
			}
		}
		public void GetTimeStart()
		{
			factStart = factEnd;
		}
		public void GetTimeThinkEnd()
		{
			switch (curCycle)
			{
				case 0: thinkEnd = factStart.AddMinutes(15); break;
				case 1: thinkEnd = factStart.AddMinutes(15); break;
				case 2: thinkEnd = factStart.AddMinutes(10); break;
				case 5: thinkEnd = factStart.AddMinutes(13); break;
				case 6: thinkEnd = factStart.AddMinutes(7); break;
				case 7: thinkEnd = factStart.AddMinutes(5); break;
				case 8: thinkEnd = factStart.AddMinutes(10); break;
				case 9: thinkEnd = factStart.AddMinutes(5); break;
				case 10: thinkEnd = factStart.AddMinutes(60); break;
				case 11: thinkEnd = factStart.AddMinutes(15); break;
				case 12: thinkEnd = factStart.AddMinutes(30); break;
				case 14: thinkEnd = factStart.AddMinutes(10); break;
				case 15: thinkEnd = factStart.AddMinutes(20); break;
				case 16: thinkEnd = factStart.AddMinutes(15); break;
				default: thinkEnd = factStart.AddMinutes(15); break;
			}
		}
		public void GetTimeFactEnd()
		{
			var r = new Random();
			var sec = 0;
			switch (curCycle)
			{
				case 0:
					sec = r.Next(7 * 60) - (5 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 1:
					sec = r.Next(3 * 60) - (2 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 2:
					sec = r.Next(11 * 60) - (1 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 5:
					sec = r.Next(4 * 60) - (3	* 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 6:
					sec = r.Next(4 * 60) - (3 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 7:
					sec = r.Next(7 * 60) - (2 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 8:
					sec = r.Next(11 * 60) - (1 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 9:
					sec = r.Next(2 * 60) - (1 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 10:
					sec = r.Next(40 * 60) - (30 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 11:
					sec = r.Next(5 * 60) - (3 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 12:
					sec = r.Next(20 * 60) - (10 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 14:
					sec = r.Next(11 * 60) - (1 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 15:
					sec = r.Next(4 * 60) - (3 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				case 16:
					sec = r.Next(4 * 60) - (3 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
				default:
					sec = r.Next(7 * 60) - (5 * 60);
					factEnd = thinkEnd.AddSeconds(sec);
					break;
			}
		}
		public bool SaveToDB()
    {
			try
			{
				using (var db = new DB.VdpDB())
				{
					var tmp = new DB.Cycle()
					{
						NumVdp = vdp,
						NumCycle = curCycle,
						FactStart = factStart,
						FactEnd = factEnd,
						ThinkEnd = thinkEnd
					};
					db.Cycles.Add(tmp);
					db.SaveChanges();
				}
			}
			catch (Exception e)
			{
				Console.WriteLine(e.Message);
				return false;
			}
			return true;
		}

		public void Start()
    {
			cycle = true;
			taskCycle = this.Cycle();
    }
		public void Stop()
    {
			cycle = false;
			taskCycle.Wait();
    }

		async public Task Cycle()
    {
      while (cycle)
      {
				if (GetCurrCycle())
        {
					if(DateTime.Now >= factEnd)
          {
						/*if (vdp == 1)
							Console.WriteLine("Generate next cycle.");*/
						GetNextCycle();
						GetTimeStart();
						GetTimeThinkEnd();
						GetTimeFactEnd();
						while (!SaveToDB())
						{
							Console.WriteLine("VDP " + vdp.ToString("D2") + ": Can't connect to DB.");
							await Task.Delay(5000);
						}
          }
        } else
        {
					/*if (vdp == 1)
						Console.WriteLine("Generate new cycle.");*/
					GetTimeStart();
					GetTimeThinkEnd();
					GetTimeFactEnd();
					while (!SaveToDB())
					{
						Console.WriteLine("VDP " + vdp.ToString("D2") + ": Can't connect to DB.");
						await Task.Delay(5000);
					}
				}

				while (cycle && (DateTime.Now < factEnd))
        {
					var secAwait = (factEnd - DateTime.Now).TotalSeconds;
					/*if(vdp == 1)
						Console.WriteLine("Time to cycle update " + secAwait.ToString());*/
					if (secAwait >= 5)
					{
						/*if (vdp == 1)
							Console.WriteLine("Delay 5 sec");*/
						await Task.Delay(5000);
					}
					else
					{
						/*if (vdp == 1)
							Console.WriteLine("Delay last secs");*/
						await Task.Delay(Convert.ToInt32(Math.Ceiling(secAwait)) * 1000);
					}
        }
      }
    }

		public string GetSmallStatus()
    {
			return vdp.ToString("D2") + "-" + curCycle.ToString("D2");
    }
	}
}
