/*
 * == BSD2 LICENSE ==
 * Copyright (c) 2019, Tidepool Project
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the associated License, which is identical to the BSD 2-Clause
 * License as published by the Open Source Initiative at opensource.org.
 *
 * 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 License for more details.
 *
 * You should have received a copy of the License along with this program; if
 * not, you can obtain one from Tidepool Project at tidepool.org.
 * == BSD2 LICENSE ==
 */

import _ from 'lodash';
import sundial from 'sundial';

import TZOUtil from '../../TimezoneOffsetUtil';
import annotate from '../../eventAnnotations';

const isBrowser = typeof window !== 'undefined';
// eslint-disable-next-line no-console
const debug = isBrowser ? require('bows')('BluetoothLEDriver') : console.log;

module.exports = (config) => {
  const cfg = _.clone(config);
  cfg.deviceTags = ['bgm'];

  // With no date & time settings changes available,
  // timezone is applied across-the-board
  cfg.tzoUtil = new TZOUtil(cfg.timezone, new Date().toISOString(), []);

  return {
   /* eslint no-param-reassign:
      [ "error", { "props": true, "ignorePropertyModificationsFor": ["data"] } ] */

   detect(deviceInfo, cb) {
     debug('no detect function needed', deviceInfo);
     cb(null, deviceInfo);
   },

   setup(deviceInfo, progress, cb) {
     debug('in setup!');
     progress(100);
     cb(null, { deviceInfo });
   },

   async connect(progress, data, cb) {
     try {
       debug('in connect!');
       await cfg.deviceComms.ble.connect();
     } catch (error) {
       return cb(error, null);
     }
     return cb(null, data);
   },

   async getConfigInfo(progress, data, cb) {
     debug('in getConfigInfo', data);
     progress(0);

     try {
       _.assign(cfg.deviceInfo , await cfg.deviceComms.ble.getDeviceInfo());
       cfg.deviceInfo.deviceId = `${[cfg.deviceInfo.manufacturers]}-${cfg.deviceInfo.model}-${cfg.deviceInfo.serialNumber}`;
       cfg.deviceTags = ['bgm'];

       // TODO: cfg.deviceTime

       cfg.deviceComms.ble.on('numberOfRecords', (result) => {
         debug('Number of records:', result);
       });
       await cfg.deviceComms.ble.getNumberOfRecords();
     } catch (error) {
       return cb(error, null);
     }

     return cb(null, data);
   },

   async fetchData(progress, data, cb) {
     debug('in fetchData', data);

     cfg.deviceComms.ble.on('data', (result) => {
       debug('Records:', result);
       data.records = result;
       return cb(null, data);
     });

     try {
       await cfg.deviceComms.ble.getAllRecords();
     } catch (error) {
       return cb (error, null);
     }
   },

   processData(progress, data, cb) {
     progress(0);
     cfg.builder.setDefaults({ deviceId: cfg.deviceInfo.deviceId });
     data.postRecords = [];

     _.forEach(data.records, (result) => {
       const record = result;
       let annotation = null;

       if (record.value === Number.POSITIVE_INFINITY) {
         record.value = 0;
         annotation = {
           code: 'bg/out-of-range',
           value: 'high',
         };
       } else if (record.value === Number.NEGATIVE_INFINITY) {
         record.value = 0;
         annotation = {
           code: 'bg/out-of-range',
           value: 'low',
         };
       }

       if (annotation) {
         annotate.annotateEvent(postRecord, annotation);
         annotate.annotateEvent(postRecord, { code: 'bg/unknown-value' });
       }

       const postRecord = cfg.builder.makeSMBG()
         .with_value(record.value)
         .with_units(record.units)
         .with_deviceTime(sundial.formatDeviceTime(record.timestamp))
         .set('index', record.seqNum);

       cfg.tzoUtil.fillInUTCInfo(postRecord, record.timestamp);
       delete postRecord.index;

       data.postRecords.push(postRecord.done());
     });

     debug('POST records:', data.postRecords);

     if (data.postRecords.length === 0) {
       debug('Device has no records to upload');
       return cb(new Error('Device has no records to upload'), null);
     }

     progress(100);
     return cb(null, data);
   },

   uploadData(progress, data, cb) {
     progress(0);

     const sessionInfo = {
       deviceTags: cfg.deviceTags,
       deviceManufacturers: cfg.deviceInfo.manufacturers,
       deviceModel: cfg.deviceInfo.model,
       deviceSerialNumber: cfg.deviceInfo.serial,
       deviceId: cfg.deviceInfo.deviceId,
       start: sundial.utcDateString(),
       timeProcessing: cfg.tzoUtil.type,
       tzName: cfg.timezone,
       version: cfg.version,
     };

     cfg.api.upload.toPlatform(
       data.postRecords, sessionInfo, progress, cfg.groupId,
       (err, result) => {
         progress(100);

         if (err) {
           debug(err);
           debug(result);
           return cb(err, data);
         }
         data.cleanup = true;
         return cb(null, data);
       },
       'dataservices',
     );
   },

   disconnect(progress, data, cb) {
     debug('in disconnect');
     cfg.deviceComms.ble.disconnect();
     data.disconnect = true;
     cb(null, data);
   },

   cleanup(progress, data, cb) {
     debug('in cleanup');
     progress(100);
     data.cleanup = true;
     cb();
   },
  };
};

@@ -2049,133 +2049,8 @@
 ));%0A
-       cfg.deviceInfo.deviceId = %60$%7B%5Bcfg.deviceInfo.manufacturers%5D%7D-$%7Bcfg.deviceInfo.model%7D-$%7Bcfg.deviceInfo.serialNumber%7D%60;%0A
     
@@ -2113,24 +2113,27 @@
 Time%0A%0A      
+ //
  cfg.deviceC
@@ -2179,24 +2179,27 @@
  =%3E %7B%0A      
+ //
    debug('Nu
@@ -2228,24 +2228,27 @@
 ult);%0A      
+ //
  %7D);%0A       
@@ -2238,32 +2238,35 @@
    // %7D);%0A      
+ //
  await cfg.devic
@@ -2814,77 +2814,8 @@
 0);%0A
-     cfg.builder.setDefaults(%7B deviceId: cfg.deviceInfo.deviceId %7D);%0A
     
@@ -2971,36 +2971,13 @@
 lue 
-=== Number.POSITIVE_INFINITY
+%3E 600
 ) %7B%0A
@@ -2992,33 +2992,35 @@
  record.value = 
-0
+601
 ;%0A         annot
@@ -3138,36 +3138,12 @@
 lue 
-=== Number.NEGATIVE_INFINITY
+%3C 20
 ) %7B%0A
@@ -3166,17 +3166,18 @@
 value = 
-0
+19
 ;%0A      
@@ -3363,83 +3363,8 @@
 n);%0A
-         annotate.annotateEvent(postRecord, %7B code: 'bg/unknown-value' %7D);%0A
     
@@ -4297,50 +4297,8 @@
 al,%0A
-       deviceId: cfg.deviceInfo.deviceId,%0A
     

handle out of range that's not set to infinity