context: /*
      This file is part of HomeGenie Project source code.
  
      HomeGenie 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.
  
      HomeGenie 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 HomeGenie.  If not, see <http://www.gnu.org/licenses/>.  
  */
  
  /*
   *     Author: Generoso Martello <gene@homegenie.it>
   *     Project Homepage: http://homegenie.it
   */
  
  
  using System;
  using System.Collections.Generic;
  using System.IO;
  using System.IO.Packaging;
  using System.Linq;
  using System.Net;
  using System.Text;
  using System.Timers;
  using System.Xml.Serialization;
  
  using System.Diagnostics;
  using System.Net.Sockets;
  using System.Threading;
  
  using HomeGenie.Automation;
  using HomeGenie.Data;
  using HomeGenie.Service.Constants;
  using HomeGenie.Service.Logging;
  using HomeGenie.Automation.Scheduler;
  
  using MIG;
  using MIG.Interfaces.HomeAutomation.Commons;
  using MIG.Interfaces.HomeAutomation;
  
  using Newtonsoft.Json;
  using Newtonsoft.Json.Linq;
  
  using OpenSource.UPnP;
  
  namespace HomeGenie.Service
  {
      [Serializable]
      public class HomeGenieService
      {
          #region Private Fields declaration
  
          private const string HOMEGENIE_MASTERNODE = "0";
          private MIGService migService;
          private ProgramEngine masterControlProgram;
          private VirtualMeter virtualMeter;
          private UpdateChecker updateChecker;
          private StatisticsLogger statisticsLogger;
          // Internal data structures
          private TsList<Module> systemModules = new HomeGenie.Service.TsList<Module>();
          private TsList<Module> modulesGarbage = new HomeGenie.Service.TsList<Module>();
          private TsList<VirtualModule> virtualModules = new TsList<VirtualModule>();
          private List<Group> automationGroups = new List<Group>();
          private List<Group> controlGroups = new List<Group>();
          //
          private TsList<LogEntry> recentEventsLog;
          //
          private SystemConfiguration systemConfiguration;
          //
          // public events
          public event Action<LogEntry> LogEventAction;
  
          public class RoutedEvent
          {
              public object Sender;
              public Module Module;
              public ModuleParameter Parameter;
          }
  
          #endregion
  
          #region Web Service Handlers declaration
  
          private Handlers.Config wshConfig;
          private Handlers.Automation wshAutomation;
          private Handlers.Interconnection wshInterconnection;
          private Handlers.Statistics wshStatistics;
          private Handlers.Logging wshLogging;
  
          #endregion
  
          #region Lifecycle
  
          public HomeGenieService()
          {
              Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
  
              // TODO: all the following initialization stuff should go async
              //
              // initialize logging
              SetupLogging();
  
              #region MIG Service initialization and startup
  
              //
              // initialize MIGService, interfaces (hw controllers drivers), webservice
              migService = new MIG.MIGService();
              migService.InterfaceModulesChanged += migService_InterfaceModulesChanged;
              migService.InterfacePropertyChanged += migService_InterfacePropertyChanged;
              migService.ServiceRequestPreProcess += migService_ServiceRequestPreProcess;
              migService.ServiceRequestPostProcess += migService_ServiceRequestPostProcess;
              //
              // load system configuration
              systemConfiguration = new SystemConfiguration();
              systemConfiguration.HomeGenie.ServiceHost = "+";
              systemConfiguration.HomeGenie.ServicePort = 8080;
              systemConfiguration.OnUpdate += systemConfiguration_OnUpdate;
              LoadSystemConfig();
              //
              // setup web service handlers
              wshConfig = new Handlers.Config(this);
              wshAutomation = new Handlers.Automation(this);
              wshInterconnection = new Handlers.Interconnection(this);
              wshStatistics = new Handlers.Statistics(this);
              wshLogging = new Handlers.Logging(this);
              //
              // Try to start WebGateway, if default HTTP port is busy, then it will try from 8080 to 8090
              bool serviceStarted = false;
              int bindAttempts = 0;
              string address = systemConfiguration.HomeGenie.ServiceHost;
              int port = systemConfiguration.HomeGenie.ServicePort;
              while (!serviceStarted && bindAttempts <= 10)
              {
                  // TODO: this should be done like this _services.Gateways["WebService"].Configure(....)
                  migService.ConfigureWebGateway(
                      address,
                      port,
                      Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "html"),
                      "/hg/html",
                      systemConfiguration.HomeGenie.UserPassword
                  );
                  if (migService.StartGateways())
                  {
                      serviceStarted = true;
                  }
                  else
                  {
                      if (port < 8080) port = 8080;
                      else port++;
                      bindAttempts++;
                  }
              }
  
              #endregion MIG Service initialization and startup
  
              //
              // If we successfully bound to port, then initialize the database.
              if (serviceStarted)
              {
                  LogBroadcastEvent(
                      Domains.HomeAutomation_HomeGenie,
                      HOMEGENIE_MASTERNODE,
                      "HomeGenie service ready",
                      Properties.SYSTEMINFO_HTTPADDRESS,
                      systemConfiguration.HomeGenie.ServiceHost + ":" + port
                  );
                  InitializeSystem();
                  // Update system configuration with the HTTP port the service succeed to bind on
                  systemConfiguration.HomeGenie.ServicePort = port;
              }
              else
              {
                  LogBroadcastEvent(
                      Domains.HomeAutomation_HomeGenie,
                      HOMEGENIE_MASTERNODE,
                      "HTTP binding failed.",
                      Properties.SYSTEMINFO_HTTPADDRESS,
                      systemConfiguration.HomeGenie.ServiceHost + ":" + systemConfiguration.HomeGenie.ServicePort
                  );
                  Program.Quit(false);
              }
  
              updateChecker = new UpdateChecker(this);
              updateChecker.ArchiveDownloadUpdate += (object sender, ArchiveDownloadEventArgs args) =>
              {
                  LogBroadcastEvent(
                      Domains.HomeGenie_UpdateChecker,
                      HOMEGENIE_MASTERNODE,
                      "HomeGenie Update Checker",
                      Properties.INSTALLPROGRESS_MESSAGE,
                      "= " + args.Status + ": " + args.ReleaseInfo.DownloadUrl
                  );
              };
              updateChecker.UpdateProgress += (object sender, UpdateProgressEventArgs args) =>
              {
                  LogBroadcastEvent(
                      Domains.HomeGenie_UpdateChecker,
                      HOMEGENIE_MASTERNODE,
                      "HomeGenie Update Checker",
                      Properties.INSTALLPROGRESS_UPDATE,
                      args.Status.ToString()
                  );
              };
              updateChecker.InstallProgressMessage += (object sender, string message) =>
              {
                  LogBroadcastEvent(
                      Domains.HomeGenie_UpdateChecker,
                      HOMEGENIE_MASTERNODE,
                      "HomeGenie Update Checker",
                      Properties.INSTALLPROGRESS_MESSAGE,
                      message
                  );
              };
              //
              statisticsLogger = new StatisticsLogger(this);
              statisticsLogger.Start();
              //
              // Setup local UPnP device
              SetupUpnp();
              //
              // it will check every 24 hours
              updateChecker.Start();
              //
              Start();
          }
  
          public void Start()
          {
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "STARTED");
              //
              // Signal "SystemStarted" event to listeners
              for (int p = 0; p < masterControlProgram.Programs.Count; p++)
              {
                  try
                  {
                      var pb = masterControlProgram.Programs[p];
                      if (pb.IsEnabled)
                      {
                          if (pb.SystemStarted != null)
                          {
                              if (!pb.SystemStarted())
                              // stop routing this event to other listeners
                              break;
                          }
                      }
                  }
                  catch 
                  {
                      // TODO: log error
                  }
              }
          }
  
          public void Stop()
          {
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "STOPPING");
              //
              // Signal "SystemStopping" event to listeners
              for (int p = 0; p < masterControlProgram.Programs.Count; p++)
              {
                  try
                  {
                      var pb = masterControlProgram.Programs[p];
                      if (pb.IsEnabled)
                      {
                          if (pb.SystemStopping != null && !pb.SystemStopping())
                          {
                              // stop routing this event to other listeners
                              break;
                          }
                      }
                  }
                  catch 
                  {
                      // TODO: log error
                  }
              }
              //
              // save system data before quitting
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "SAVING DATA");
              UpdateModulesDatabase();
              systemConfiguration.Update();
              //
              // Stop HG helper services
              updateChecker.Stop();
              statisticsLogger.Stop();
              //
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "VirtualMeter STOPPING");
              if (virtualMeter != null) virtualMeter.Stop();
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "VirtualMeter STOPPED");
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "MIG Service STOPPING");
              if (migService != null) migService.StopService();
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "MIG Service STOPPED");
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "ProgramEngine STOPPING");
              if (masterControlProgram != null) masterControlProgram.StopEngine();
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "ProgramEngine STOPPED");
              //
              LogBroadcastEvent(Domains.HomeGenie_System, HOMEGENIE_MASTERNODE, "HomeGenie System", Properties.HOMEGENIE_STATUS, "STOPPED");
              //
              SystemLogger.Instance.Dispose();
          }
  
          ~HomeGenieService()
          {
              Stop();
          }
  
          #endregion
  
          #region Data Wrappers - Public Members
  
          // Control groups (i.e. rooms, Outside, Housewide)
          public List<Group> Groups
          {
  
gt:             get { return controlGroups; }