import { APIEvent } from 'homebridge';
import type { API, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig } from 'homebridge';
import { PLATFORM_NAME, PLUGIN_NAME } from './settings';
import { join } from 'path';
import { loadJson } from './magichome-interface/utils';
import { Switch } from './accessories/Switch';
import { DimmerStrip } from './accessories/DimmerStrip';
import { RGBStrip } from './accessories/RGBStrip';
import { GRBStrip } from './accessories/GRBStrip';
import { RGBWBulb } from './accessories/RGBWBulb';
import { RGBWWBulb } from './accessories/RGBWWBulb';
import { RGBWStrip } from './accessories/RGBWStrip';
import { RGBWWStrip } from './accessories/RGBWWStrip';
import { CCTStrip } from './accessories/CCTStrip';
import { cloneDeep } from 'lodash';
import { Logs } from './logs';
import { Discover } from './magichome-interface/Discover';
import { Transport } from './magichome-interface/Transport';
import { HomebridgeMagichomeDynamicPlatformAccessory } from './platformAccessory';
import { IDeviceProps, IDeviceDiscoveredProps, IDeviceQueriedProps, ILightParameters } from './magichome-interface/types';
import { getPrettyName as getUniqueIdName, lightTypesMap } from './magichome-interface/LightMap';
import { MagicHomeAccessory, ControllerTypes } from './magichome-interface/types';
//const NEW_COMMAND_QUERY_STATE: Uint8Array = Uint8Array.from([0x81, 0x8a, 0x8b]);
//const LEGACY_COMMAND_QUERY_STATE: Uint8Array = Uint8Array.from([0xEF, 0x01, 0x77]);

const accessoryType = {
  Switch,
  DimmerStrip,
  GRBStrip,
  RGBStrip,
  RGBWBulb,
  RGBWWBulb,
  RGBWStrip,
  RGBWWStrip,
  CCTStrip,
};

/**
 * HomebridgePlatform
 * This class is the main constructor for your plugin, this is where you should
 * parse the user config and discover/register accessories with Homebridge.
 */
export class HomebridgeMagichomeDynamicPlatform implements DynamicPlatformPlugin {
  public readonly Service = this.api.hap.Service;
  public readonly Characteristic = this.api.hap.Characteristic;
  // this is used to track restored cached accessories
  public readonly accessories: MagicHomeAccessory[] = [];
  public count = 1;
  private periodicDiscovery: NodeJS.Timeout | null = null;

  private logs: Logs;


  constructor(
    public readonly hbLogger: Logger,
    public readonly config: PlatformConfig,
    public readonly api: API,
  ) {
    if (this.config.advancedOptions.logLevel) {
      this.logs = new Logs(hbLogger, this.config.advancedOptions.logLevel);
    } else {
      this.logs = new Logs(hbLogger);
    }

    //this.logs = getLogger();
    this.logs.warn('Finished initializing homebridge-magichome-dynamic-platform %o', loadJson<any>(join(__dirname, '../package.json'), {}).version);
    this.logs.info('If this plugin brings you joy, consider visiting GitHub and giving it a ⭐.');

    // When this event is fired it means Homebridge has restored all cached accessories from disk.
    // Dynamic Platform plugins should only register new accessories after this event was fired,
    // in order to ensure they weren't added to homebridge already. This event can also be used
    // to start discovery of new accessories.
    this.api.on(APIEvent.DID_FINISH_LAUNCHING, () => {
      this.logs.debug('Executed didFinishLaunching callback');
      this.count = 1;
      // run the method to discover / register your devices as accessories
      this.discoverDevices(true);
      // Periodic scan for devices
      const shouldRediscover = this.config.advancedOptions?.periodicDiscovery ?? false;

      if (shouldRediscover) {
        this.periodicDiscovery = setInterval(() => this.discoverDevices(false), 30000);
      }
    });
  }

  /**
   * This function is invoked when homebridge restores cached accessories from disk at startup.
   * It should be used to setup event handlers for characteristics and update respective values.
   */
  configureAccessory(accessory: MagicHomeAccessory) {

    this.logs.debug('%o - Loading accessory from cache...', this.count++, accessory.context.device.displayName);
    // set cached accessory as not recently seen 
    // if found later to be a match with a discovered device, will change to true
    accessory.context.device.restartsSinceSeen++;
    accessory.context.pendingRegistration = true;
    // add the restored accessory to the accessories cache so we can track if it has already been registered
    this.accessories.push(accessory);
  }

  /**
   * Accessories are added by one of three Methods:
   * Method One: New devices that were seen after scanning the network and are registered for the first time
   * Method Two: Cached devices that were seen after scanning the network and are added while checking for ip discrepancies 
   * Method Three: Cached devices that were not seen after scanning the network but are still added with a warning to the user
   */
  async discoverDevices(dgb: boolean | null) {
    const { isValidDeviceModel } = HomebridgeMagichomeDynamicPlatform;
    const pendingUpdate = new Set();
    const recentlyRegisteredDevices = new Set();

    let registeredDevices = 0, newDevices = 0, unseenDevices = 0, scans = 0;
    let discover = new Discover(this.logs, this.config);



    let devicesDiscovered: IDeviceDiscoveredProps[] = await discover.scan(2000);

    while (devicesDiscovered.length === 0 && scans < 5) {
      this.logs.debug('( Scan: %o ) Discovered zero devices... rescanning...', scans + 1);
      devicesDiscovered = await discover.scan(2000);
      scans++;
    }

    discover = null;
    if (devicesDiscovered.length === 0) {
      this.logs.debug('\nDiscovered zero devices!\n');
    } else {
      this.logs.debug('\nDiscovered %o devices.\n', devicesDiscovered.length);
    }

    // loop over the discovered devices and register each one if it has not already been registered
    for (const deviceDiscovered of devicesDiscovered) {
      let existingAccessory: MagicHomeAccessory = null;
      try {
        // generate a unique id for the accessory this should be generated from
        const generatedUUID = this.api.hap.uuid.generate(deviceDiscovered.uniqueId);
        // check that the device has not already been registered by checking the
        // cached devices we stored in the `configureAccessory` method above
        existingAccessory = this.accessories.find(accessory => accessory.UUID === generatedUUID);

        if (!existingAccessory) {
          if (!this.createNewAccessory(deviceDiscovered, generatedUUID)) {
            continue;
          }
          recentlyRegisteredDevices.add(deviceDiscovered.uniqueId);
          registeredDevices++;
          newDevices++;

        } else {
          // This deviceDiscovered already exist in cache!

          // Check if cached device complies to the device model,
          if (!isValidDeviceModel(existingAccessory.context.device, null)) {
            //`Device "${uniqueId}" is online, but has outdated data model. Attempting to update it. 
            this.logs.debug(`The known device "${deviceDiscovered.uniqueId}" seen during discovery has outdated data model (pre v1.8.6). Rebuilding device. `, deviceDiscovered);


            // ****** RECONSTRUCT DEVICE - patch existingAccessory with updated data *****
            const initialState = await this.getInitialState(deviceDiscovered.ipAddress, 10000);
            const deviceQueryData: IDeviceQueriedProps = await this.determineController(deviceDiscovered);

            if (!initialState || !deviceQueryData) {
              this.logs.error('Warning! Device type could not be determined for device: %o, this is usually due to an unresponsive device.\n Please restart homebridge. If the problem persists, ensure the device works in the "Magichome Pro" app.\n file an issue on github with an uploaded log\n',
                deviceDiscovered.uniqueId);
              continue;
            }

            const oldName = existingAccessory.context.displayName ||
              existingAccessory.context.device?.lightParameters?.convenientName ||
              deviceQueryData.lightParameters.convenientName;

            const rootProps = { UUID: generatedUUID, cachedIPAddress: deviceDiscovered.ipAddress, restartsSinceSeen: 0, displayName: oldName, lastKnownState: initialState };
            const deviceData: IDeviceProps = Object.assign(rootProps, deviceDiscovered, deviceQueryData);
            existingAccessory.context.device = deviceData;
            // ****** RECONSTRUCT DEVICE *****

            if (isValidDeviceModel(existingAccessory.context.device, this.logs)) {
              this.logs.debug(`[discovered+cached] Device "${deviceDiscovered.uniqueId}" successfully repaired!`);
            } else {
              this.logs.error(`[discovered+cached] Device "${deviceDiscovered.uniqueId}" was not repaired successfully. Ensure it can be controlled in the MagicHome app then restart homebridge to try again while it is online.`);
              continue;
            }

          }
          if (!this.registerExistingAccessory(deviceDiscovered, existingAccessory)) {
            continue;
          }

          // add to list of registered devices, so we can show a summary in the end
          recentlyRegisteredDevices.add(deviceDiscovered.uniqueId);
          registeredDevices++;
        }

      } catch (error) {
        this.logs.debug('[discovered+cached] platform.ts discoverDevices() accessory creation has thrown the following error: %o', error);
        this.logs.debug('[discovered+cached] The existingAccessory object is: ', existingAccessory);
        this.logs.debug('[discovered+cached] The deviceDiscovered object is: ', deviceDiscovered);
      }
    }
    //=================================================
    // End Cached Devices //


    //***************** Device Pruning Start *****************//

    //if config settings are enabled, devices that are no longer seen
    //will be pruned, removing them from the cache. Usefull for removing
    //unplugged or unresponsive accessories

    for (const accessory of this.accessories) {
      try {

        if (!isValidDeviceModel(accessory.context.device, null)) {
          // only offline, cached devices, old data model, should trigger here.
          const { uniqueId } = accessory.context.device;
          this.logs.debug(`Device "${uniqueId}" was not seen during discovery. Ensure it can be controlled in the MagicHome app. Rescan in 30 seconds...`);
          pendingUpdate.add(uniqueId);
          continue;
        }

        if (accessory.context.device?.displayName && accessory.context.device.displayName.toString().toLowerCase().includes('delete')) {
          this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
          this.logs.warn('Successfully pruned accessory: ', accessory.context.device.displayName,
            'due to being marked for deletion\n');
          continue;

          //if the config parameters for pruning are set to true, prune any devices that haven't been seen
          //for more restarts than the accepted ammount
        } else if (this.config.pruning.pruneMissingCachedAccessories || this.config.pruning.pruneAllAccessoriesNextRestart) {
          if (accessory.context.device.restartsSinceSeen >= this.config.pruning.restartsBeforeMissingAccessoriesPruned || this.config.pruning.pruneAllAccessoriesNextRestart) {
            this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
            this.logs.warn('Successfully pruned accessory:', accessory.context.device.displayName,
              'which had not being seen for (', accessory.context.device.restartsSinceSeen, ') restart(s).\n');
            continue;
          }
        }
        //simple warning to notify user that their accessory hasn't been seen in n restarts
        if (accessory.context.device.restartsSinceSeen > 0) {
          //logic for removing blacklisted devices

          if (!this.isAllowed(accessory.context.device.uniqueId)) {
            this.logs.warn('Warning! Accessory: %o will be pruned as its Unique ID: %o is blacklisted or is not whitelisted.\n',
              accessory.context.device.displayName, accessory.context.device.uniqueId);
            try {
              this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
            } catch (err) {
              this.logs.debug('Accessory: %o count not be pruned. Likely it had never been registered.\n',
                accessory.context.device.displayName, accessory.context.device.uniqueId);
            }
            continue;
          }

          this.logs.debug(`Warning! Continuing to register cached accessory "${accessory.context.device.uniqueId}" despite not being seen for ${accessory.context.device.restartsSinceSeen} restarts.`);

          // create the accessory handler
          let lightAccessory: HomebridgeMagichomeDynamicPlatformAccessory = null;
          try {
            lightAccessory = new accessoryType[accessory.context.device.lightParameters.controllerLogicType](this, accessory, this.config);
          } catch (error) {
            this.logs.error('[1] The controllerLogicType does not exist in accessoryType list. Did you migrate this? controllerLogicType=', accessory.context.device?.lightParameters?.controllerLogicType);
            this.logs.error('device object: ', accessory.context.device);
            continue;
          }

          // udpate the accessory to platform
          this.api.updatePlatformAccessories([accessory]);
          registeredDevices++;
          unseenDevices++;

        }

      } catch (error) {
        this.logs.error('platform.ts discoverDevices() accessory pruning has thrown the following error: %o', error);
        this.logs.error('The context object is: ', accessory.context);
      }
    }

    this.logs.debug('\nRegistered %o Magichome device(s). \nNew devices: %o \nCached devices that were seen this restart: %o'
      + '\nCached devices that were not seen this restart: %o\n',
    registeredDevices,
    newDevices,
    registeredDevices - newDevices - unseenDevices,
    unseenDevices);



    // Discovery summary:
    if (recentlyRegisteredDevices.size > 0) {
      const found = recentlyRegisteredDevices.size;
      const pending = Array.from(pendingUpdate).length;
      const pendingStr = pending > 0 ? ` Pending update: ${pending} devices` : '';
      this.logs.debug(`Discovery summary:  Found ${found} devices.${pendingStr}`);
    }

    this.count = 1; // reset the device logging counter
  }//discoveredDevices


  isAllowed(uniqueId): boolean {

    const blacklistedUniqueIDs = this.config.deviceManagement.blacklistedUniqueIDs;
    let isAllowed = true;
    try {

      if (blacklistedUniqueIDs !== undefined
        && this.config.deviceManagement.blacklistOrWhitelist !== undefined) {
        if (((blacklistedUniqueIDs).includes(uniqueId)
          && (this.config.deviceManagement.blacklistOrWhitelist).includes('blacklist'))
          || (!(blacklistedUniqueIDs).includes(uniqueId))
          && (this.config.deviceManagement.blacklistOrWhitelist).includes('whitelist')) {
          isAllowed = false;
        }
      }
    } catch (error) {
      this.logs.debug(error);
    }

    return isAllowed;
  }

  async getInitialState(ipAddress, _timeout = 500) {

    const transport = new Transport(ipAddress, this.config);
    try {
      let scans = 0, data;

      while (data == null && scans < 5) {

        data = await transport.getState(_timeout);
        scans++;
      }

      return data;

    } catch (error) {
      this.logs.debug(error);
    }
  }

  async determineController(discoveredDevice): Promise<IDeviceQueriedProps | null> {
    const { ipAddress } = discoveredDevice || {};
    if (typeof ipAddress !== 'string') {
      this.logs.error('Cannot determine controller because invalid IP address. Device:', discoveredDevice);
      return null;
    }
    const initialState = await this.getInitialState(ipAddress, 10000);
    if (initialState == undefined) {
      this.logs.debug('Cannot determine controller. Device unreacheable.', discoveredDevice);
      return null;
    }

    let lightParameters: ILightParameters;
    const controllerHardwareVersion = initialState.controllerHardwareVersion;
    const controllerFirmwareVersion = initialState.controllerFirmwareVersion;

    this.logs.debug('Attempting to assign controller to new device: UniqueId: %o \nIpAddress %o \nModel: %o\nHardware Version: %o \nDevice Type: %o\n',
      discoveredDevice.uniqueId, discoveredDevice.ipAddress, discoveredDevice.modelNumber, initialState.controllerHardwareVersion.toString(16), initialState.controllerFirmwareVersion.toString(16));

    //set the lightVersion so that we can give the device a useful name and later know how which protocol to use

    if (lightTypesMap.has(controllerHardwareVersion)) {
      this.logs.debug('Device %o: Hardware Version: %o with Firmware Version: %o matches known device type records',
        discoveredDevice.uniqueId,
        controllerHardwareVersion.toString(16),
        controllerFirmwareVersion.toString(16));
      lightParameters = lightTypesMap.get(controllerHardwareVersion);
    } else {
      this.logs.warn('Unknown device version number: %o... unable to create accessory.', controllerHardwareVersion.toString(16));
      this.logs.warn('Please create an issue at https://github.com/Zacknetic/HomebridgeMagicHome-DynamicPlatform/issues and upload your homebridge.log');
      return null;
    }

    this.logs.debug('Controller Logic Type assigned to %o', lightParameters.controllerLogicType);

    return {
      lightParameters,
      controllerHardwareVersion: controllerHardwareVersion,
      controllerFirmwareVersion: controllerFirmwareVersion,
    };
  }

  /**
 * Accessory Generation Method One: UUID has not been seen before. Register new accessory.
 * Accessories must only be registered once, previously created accessories
 * must not be registered again to prevent "duplicate UUID" errors.
 * @param deviceDiscovered 
 * @param generatedUUID 
 */
  async createNewAccessory(deviceDiscovered: IDeviceDiscoveredProps, generatedUUID): Promise<boolean> {
    const unsupportedModels: string[] = ['000-0000']; //AK001-ZJ210 is suported... 

    const deviceQueryData: IDeviceQueriedProps = await this.determineController(deviceDiscovered);

    if (deviceQueryData == null) {
      if (unsupportedModels.includes(deviceDiscovered.modelNumber)) {
        this.logs.warn('Warning! Discovered device did not respond to query. Device is in the unsupported device list.\nFile an issue on github requesting support. Details:', deviceDiscovered);
      } else {
        this.logs.warn('Warning! Discovered device did not respond to query. This is usually due to an unresponsive device.\nPlease restart homebridge. If the problem persists, ensure the device works in the "Magichome Pro" app.\nFile an issue on github with an uploaded log.', deviceDiscovered);
      }
      return false;
    }
    //check if device is on blacklist or is not on whitelist
    if (!this.isAllowed(deviceDiscovered.uniqueId)) {
      this.logs.warn('Warning! New device with Unique ID: %o is blacklisted or is not whitelisted.\n',
        deviceDiscovered.uniqueId);

      return false;
    }
    // if user has oped, use unique name such as "Bulb AABBCCDD"
    if (this.config.advancedOptions && this.config.advancedOptions.namesWithMacAddress) {
      const uniqueIdName = getUniqueIdName(deviceDiscovered.uniqueId, deviceQueryData.lightParameters.controllerLogicType);
      deviceQueryData.lightParameters.convenientName = uniqueIdName;
    }

    const accessory = new this.api.platformAccessory(deviceQueryData.lightParameters.convenientName, generatedUUID) as MagicHomeAccessory;

    // set its restart prune counter to 0 as it has been seen this session
    const deviceData: IDeviceProps = Object.assign({ UUID: generatedUUID, cachedIPAddress: deviceDiscovered.ipAddress, restartsSinceSeen: 0, displayName: deviceQueryData.lightParameters.convenientName }, deviceDiscovered, deviceQueryData);
    accessory.context.device = deviceData;


    this.printDeviceInfo('Registering new accessory...!', accessory);
    // link the accessory to your platform
    this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);

    // create the accessory handler
    let lightAccessory: HomebridgeMagichomeDynamicPlatformAccessory = null;
    try {
      lightAccessory = new accessoryType[accessory.context.device.lightParameters.controllerLogicType](this, accessory, this.config);
    } catch (error) {
      this.logs.error('[2] The controllerLogicType does not exist in accessoryType list. controllerLogicType=', accessory.context.device?.lightParameters?.controllerLogicType);
      this.logs.error('device object: ', accessory.context.device);
      return false;
    }
    this.accessories.push(accessory);

    return true;
  }

  /**
 * Accessory Generation Method Two: UUID has been seen before. Load from cache.
 * Test if seen accessory "is allowed" and that the IP address is identical
 * @param deviceDiscovered 
 * @param existingAccessory 
 */
  registerExistingAccessory(deviceDiscovered, existingAccessory: MagicHomeAccessory): boolean {

    if (!this.isAllowed(existingAccessory.context.device.uniqueId)) {
      this.logs.warn('Warning! Accessory: %o will be pruned as its Unique ID: %o is blacklisted or is not whitelisted.\n',
        existingAccessory.context.device.displayName, existingAccessory.context.device.uniqueId);
      this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [existingAccessory]);
      return false;
    }

    // set its restart prune counter to 0 as it has been seen this session
    existingAccessory.context.device.restartsSinceSeen = 0;

    // test if the existing cached accessory ip address matches the discovered
    // accessory ip address if not, replace it
    const ipHasNotChanged = existingAccessory.context.device.cachedIPAddress === deviceDiscovered.ipAddress;
    const { pendingRegistration } = existingAccessory.context;
    const registrationComplete = !pendingRegistration;
    if (registrationComplete && ipHasNotChanged) {
      this.logs.debug(`Device ${existingAccessory.context.device.uniqueId} already registered. Registration update not required`);
      return false;
    }

    if (!ipHasNotChanged) {
      this.logs.warn('Ip address discrepancy found for accessory: %o\n Expected ip address: %o\n Discovered ip address: %o',
        existingAccessory.context.device.displayName, existingAccessory.context.device.cachedIPAddress, deviceDiscovered.ipAddress);

      // overwrite the ip address of the existing accessory to the newly disovered ip address
      existingAccessory.context.device.cachedIPAddress = deviceDiscovered.ipAddress;

      this.logs.warn('Ip address successfully reassigned to: %o\n ', existingAccessory.context.device.cachedIPAddress);
    }

    this.printDeviceInfo('Registering existing accessory: ', existingAccessory);

    // create the accessory handler
    let lightAccessory: HomebridgeMagichomeDynamicPlatformAccessory = null;
    try {
      if (!existingAccessory.context?.device?.lightParameters?.controllerLogicType || accessoryType[existingAccessory.context?.device?.lightParameters?.controllerLogicType] === undefined) {
        this.logs.error('[registerExistingAccessory] The controllerLogicType does not exist in accessoryType list. controllerLogicType=', existingAccessory.context.device?.lightParameters?.controllerLogicType);
        this.logs.error('[registerExistingAccessory] device object: ', existingAccessory.context.device);
        return false;
      }
      lightAccessory = new accessoryType[existingAccessory.context.device.lightParameters.controllerLogicType](this, existingAccessory, this.config);
    } catch (error) {
      this.logs.error('[registerExistingAccessory] The controllerLogicType does not exist in accessoryType list. controllerLogicType=', existingAccessory.context.device?.lightParameters?.controllerLogicType);
      this.logs.error('[registerExistingAccessory] device object: ', existingAccessory.context.device);
      this.logs.error(error);

      return false;
    }

    // udpate the accessory to your platform
    this.api.updatePlatformAccessories([existingAccessory]);
    existingAccessory.context.pendingRegistration = false;
    return true;
  }

  printDeviceInfo(message: string, accessory: MagicHomeAccessory) {
    const device = accessory.context.device;
    this.logs.info(`${this.count++} - ${message}[ ${device.displayName} ]`);
    this.logs.debug(`
    Controller Logic Type: ${device.lightParameters?.controllerLogicType}
    Model: ${device.modelNumber}
    Unique ID: ${device.uniqueId} 
    IP-Address: ${device.ipAddress} 
    Hardware Version: ${device.controllerHardwareVersion?.toString(16)}
    Firmware Version: ${device.controllerHardwareVersion?.toString(16)}\n`);
  }

  async send(transport, command: number[], useChecksum = true, _timeout = 200) {
    const buffer = Buffer.from(command);

    const output = await transport.send(buffer, useChecksum, _timeout);
    this.logs.debug('Recived the following response', output);

  } //send

  static isValidDeviceModel(_device: IDeviceProps, logger): boolean {
    const device = cloneDeep(_device);
    try {
      const { lightParameters } = device || {};

      const rootProps = ['UUID', 'cachedIPAddress', 'restartsSinceSeen', 'displayName', 'ipAddress', 'uniqueId', 'modelNumber', 'lightParameters', 'controllerHardwareVersion', 'controllerFirmwareVersion'];
      const lightProps = ['controllerLogicType', 'convenientName', 'simultaneousCCT', 'hasColor', 'hasBrightness'];

      const missingRootProps = rootProps.filter(k => device[k] === undefined || device[k] == null);
      const missingLightProps = lightProps.filter(k => lightParameters[k] === undefined || lightParameters[k] == null);

      const missingProps = [...missingRootProps, ...missingLightProps];

      // special case: props that can be null: 'lastKnownState'
      if (device.lastKnownState === undefined) {
        missingProps.push('lastKnownState');
      }

      if (!Object.values(ControllerTypes).includes(lightParameters.controllerLogicType)) {
        if (logger) {
          logger.error(`[isValidDeviceModel] The ContollerLogicType "${lightParameters.controllerLogicType}" is unknown.`);
        }
        return false;
      }


      if (missingProps.length > 0) {
        if (logger) {
          logger.error('[isValidDeviceModel] unable to validate device model. Missing properties: ', missingProps);
          logger.debug('\nThree things are certain:\nDeath, taxes and lost data.\nGuess which has occurred.');
        }
        return false;
      }

      return true;
    } catch (err) {
      return false;
    }

  }

}//ZackneticMagichomePlatform class