context: using System;
  using System.Collections.Generic;
  using System.ComponentModel.Composition;
  using System.ComponentModel.Composition.Hosting;
  using System.Configuration;
  using System.IO;
  using System.Net;
  using System.Reflection;
  using System.Threading;
  using System.Threading.Tasks;
  using System.Timers;
  using System.Web.Http;
  using System.Web.Http.SelfHost;
  using Hack.TheHub.Core;
  using Hack.TheHub.Nodes;
  using TheHub.Configuration;
  using TheHub.Nodes;
  using log4net.Config;
  
  namespace Hack.TheHub
  {
      public class Hub : IDisposable
      {
          [ImportMany]
          private IEnumerable<Lazy<INode, INodeData>> _nodes;
  
          private int _failedRequestCounter = 0;
  
          private System.Timers.Timer _timer;
  
          private CompositionContainer _container;
  
          private HttpSelfHostServer _server;
          private HttpSelfHostConfiguration _config;
  
          private string _serviceAddress = String.Format("http://localhost:{0}", Properties.Settings.Default.SelfHostPort);
  
          private Thread _polling = null;
          private Thread _httpRequesting = null;
  
          private Queue<HttpWebRequest> _httpRequestQueue = new Queue<HttpWebRequest>();
  
          internal void Start()
          {
              BasicConfigurator.Configure();
  
              StartServer();
  
              Compose();
  
              LoadConfigurations();
  
              _polling = new Thread(DoPolling);
              _polling.Start();
  
              _httpRequesting = new Thread(DoRequesting);
              _httpRequesting.Start();
  
              StartTimers();
          }
  
          private void StartTimers()
          {
              double interval = 29 * 1000; // milliseconds to almost halve a minute
  
              _timer = new System.Timers.Timer(interval);
              _timer.Elapsed += TimerElapsed;
              _timer.Enabled = true;
          }
  
          private void TimerElapsed(object sender, ElapsedEventArgs e)
          {
              // TODO: Change from hard-coded code for one switch to a more generic method
  
              TimerData timerData1 = new TimerData(
                 new TimeSpan(17, 0, 0),
                 new TimeSpan(23, 59, 0),
                 "http://192.168.0.242/control?cmd=set_state_actuator&number=2&function=1",
                 "http://192.168.0.242/control?cmd=set_state_actuator&number=2&function=2",
                 e
             );
  
              TimerData timerData2 = new TimerData(
                  new TimeSpan(17, 0, 0),
                  new TimeSpan(23, 59, 0),
                  "http://192.168.0.242/control?cmd=set_state_actuator&number=4&function=1",
                  "http://192.168.0.242/control?cmd=set_state_actuator&number=4&function=2",
                  e
              );
  
              TimerData timerData3 = new TimerData(
                  new TimeSpan(17, 0, 0),
                  new TimeSpan(23, 59, 0),
                  "http://192.168.0.242/control?cmd=set_state_actuator&number=5&function=1",
                  "http://192.168.0.242/control?cmd=set_state_actuator&number=5&function=2",
                  e
              );
  
              Task.Factory.StartNew(Timer, timerData1);
              Task.Factory.StartNew(Timer, timerData2);
              Task.Factory.StartNew(Timer, timerData3);
          }
  
          private void Timer(object state)
          {
              TimerData data = (TimerData) state;
  
              HttpWebRequest request = null;
  
              if (data.EventArgs.SignalTime.Hour == data.OnTime.Hours && data.EventArgs.SignalTime.Minute == data.OnTime.Minutes)
              {
                  request = (HttpWebRequest)WebRequest.Create(data.OnUri);
              }
              else if (data.EventArgs.SignalTime.Hour == data.OffTime.Hours && data.EventArgs.SignalTime.Minute == data.OffTime.Minutes)
              {
                   request = (HttpWebRequest)WebRequest.Create(data.OffUri);
              }
  
              if (request != null)
              {
                  _httpRequestQueue.Enqueue(request);
              }
  
              if (_failedRequestCounter > 5)
              {
                  ErrorLogger.LogException(LogLevel.Warn, "Restarting program due to maximum failed HTTP requests.");
                  Program.Restart();
              }
          }
  
          internal  class TimerData
          {
              public TimerData(TimeSpan onTime, TimeSpan offTime, string onUri, string offUri, ElapsedEventArgs eventArgs)
              {
                  OnTime = onTime;
                  OffTime = offTime;
                  OnUri = onUri;
                  OffUri = offUri;
                  EventArgs = eventArgs;
              }
  
              internal ElapsedEventArgs EventArgs { get; set; }
              internal String OnUri { get; set; }
              internal String OffUri { get; set; }
              internal TimeSpan OnTime { get; set; }
              internal TimeSpan OffTime { get; set; }
          }
  
          private void StartServer()
          {
              _config = new HttpSelfHostConfiguration(_serviceAddress);
  
              _config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}", new { id = RouteParameter.Optional });
              _config.Routes.MapHttpRoute("Default", "{controller}/{path}", new { controller = "Index", path = RouteParameter.Optional });
  
              _server = new HttpSelfHostServer(_config);
              _server.OpenAsync().Wait();
          }
  
          private void DoRequesting()
          {
              while (true)
              {
  
                  if (_httpRequestQueue.Count > 0)
                  {
                      HttpWebRequest request = _httpRequestQueue.Dequeue();
  
                      try
                      {
                          HttpWebResponse response = (HttpWebResponse) request.GetResponse();
                          
                          if (response.StatusCode != HttpStatusCode.OK)
                          {
                              ErrorLogger.LogException(LogLevel.Warn, string.Format("Response to {0} returned status code {1}.", request.RequestUri, response.StatusCode));
  
                              Thread.Sleep(1000);
  
                              response = (HttpWebResponse) request.GetResponse();
  
                              ErrorLogger.LogException(LogLevel.Warn, string.Format("Request failed second time. Response to {0} returned status code {1}.", request.RequestUri, response.StatusCode));
                          }
                      }
                      catch (Exception ex)
                      {
                          string msg = string.Format("Failed to send timed request to {0}", request.RequestUri);
                          ErrorLogger.LogException(LogLevel.Info, msg, ex);
                          _failedRequestCounter++;
                      }
                  }
  
                  Thread.Sleep(1000);
              }
          }
  
          private void DoPolling()
          {
              while (true)
              {
                  Context.Clear();
  
                  foreach (var node in _nodes)
                  {
                      string msg = string.Format("Device {0} is {1}", node.Metadata.Description,
                                                  node.Value.Status ? "online" : "offline");
                      ErrorLogger.LogException(LogLevel.Debug, msg);
  
                      node.Value.Update();
  
                      Context.Add(node.Value);
  
                      var items = node.Value.Items;
  
                      if (items != null)
                      {
                          foreach (var nodeItem in items)
                          {
                              Context.Add(nodeItem);
                              Context.DbConnection.AddValue(nodeItem);
  
                              ErrorLogger.LogException(LogLevel.Debug, "\t" + nodeItem);
                          }
                      }
                  }
  
  #if DEBUG
                  Thread.Sleep(30000);
  #else
                  Thread.Sleep(60000);
  #endif
              }
          }
  
          private void LoadConfigurations()
          {
              HubSection config = ConfigurationManager.GetSection("theHub") as HubSection;
          }
  
          private void Compose()
          {
              AggregateCatalog catalog = new AggregateCatalog();
  
              catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
              catalog.Catalogs.Add(new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)));
  
  
gt:             _container = new CompositionContainer(catalog);