'use strict';

angular.module('copayApp.services').service('scannerService', function($log, $timeout, platformInfo, $rootScope, $window) {

  var isDesktop = !platformInfo.isCordova;
  var QRScanner = $window.QRScanner;
  var lightEnabled = false;
  var backCamera = true; // the plugin defaults to the back camera

  // Initalize known capabilities
  var isAvailable = isDesktop? false: true; // assume camera exists on mobile
  var hasPermission = isDesktop? true: false; // assume desktop has permission
  var isDenied = false;
  var isRestricted = false;
  var canEnableLight = false;
  var canChangeCamera = false;
  var canOpenSettings = false;

  function _checkCapabilities(status){
    $log.debug('scannerService is reviewing platform capabilities...');
    // Permission can be assumed on the desktop builds
    hasPermission = (isDesktop || status.authorized)? true: false;
    isDenied = status.denied? true : false;
    isRestricted = status.restricted? true : false;
    canEnableLight = status.canEnableLight? true : false;
    canChangeCamera = status.canChangeCamera? true : false;
    canOpenSettings = status.canOpenSettings? true : false;
    _logCapabilities();
  }

  function _logCapabilities(){
    function _orIsNot(bool){
      return bool? '' : 'not ';
    }
    $log.debug('A camera is ' + _orIsNot(isAvailable) + 'available to this app.');
    var access = 'not authorized';
    if(hasPermission) access = 'authorized';
    if(isDenied) access = 'denied';
    if(isRestricted) access = 'restricted';
    $log.debug('Camera access is ' + access + '.');
    $log.debug('Support for opening device settings is ' + _orIsNot(canOpenSettings) + 'available on this platform.');
    $log.debug('A light is ' + _orIsNot(canEnableLight) + 'available on this platform.');
    $log.debug('A second camera is ' + _orIsNot(canChangeCamera) + 'available on this platform.');
  }

  /**
   * Immediately return known capabilities of the current platform.
   */
  this.getCapabilities = function(){
    return {
      isAvailable: isAvailable,
      hasPermission: hasPermission,
      isDenied: isDenied,
      isRestricted: isRestricted,
      canEnableLight: canEnableLight,
      canChangeCamera: canChangeCamera,
      canOpenSettings: canOpenSettings
    };
  };

  var initializeStarted = false;
  /**
   * If camera access has been granted, pre-initialize the QRScanner. This method
   * can be safely called before the scanner is visible to improve perceived
   * scanner loading times.
   *
   * The `status` of QRScanner is returned to the callback.
   */
  this.gentleInitialize = function(callback) {
    if(initializeStarted){
      QRScanner.getStatus(function(status){
        _completeInitialization(status, callback);
      });
      return;
    }
    initializeStarted = true;
    $log.debug('Trying to pre-initialize QRScanner.');
    if(!isDesktop){
      QRScanner.getStatus(function(status){
        _checkCapabilities(status);
        if(status.authorized){
          $log.debug('Camera permission already granted.');
          initialize(callback);
        } else {
          $log.debug('QRScanner not authorized, waiting to initalize.');
          _completeInitialization(status, callback);
        }
      });
    } else {
      $log.debug('Camera permission assumed on desktop.');
      initialize(callback);
    }
  };

  function initialize(callback){
    $log.debug('Initializing scanner...');
    QRScanner.prepare(function(err, status){
      if(err){
        $log.error(err);
        // does not return `status` if there is an error
        QRScanner.getStatus(function(status){
          _completeInitialization(status, callback);

        });
      } else {
        isAvailable = true;
        _completeInitialization(status, callback);
      }
    });
  }
  this.initialize = initialize;

  // This could be much cleaner with a Promise API
  // (needs a polyfill for some platforms)
  var initializeCompleted = false;
  function _completeInitialization(status, callback){
    _checkCapabilities(status);
    initializeCompleted = true;
    $rootScope.$emit('scannerServiceInitialized');
    if(typeof callback === "function"){
      callback(status);
    }
  }
  this.isInitialized = function(){
    return initializeCompleted;
  };
  this.initializeStarted = function(){
    return initializeStarted;
  };

  var nextHide = null;
  var nextDestroy = null;
  var hideAfterSeconds = 10;
  var destroyAfterSeconds = 5 * 60;

  /**
   * (Re)activate the QRScanner, and cancel the timeouts if present.
   *
   * The `status` of QRScanner is passed to the callback when activation
   * is complete.
   */
  this.activate = function(callback) {
    $log.debug('Activating scanner...');
      QRScanner.show(function(status){
        _checkCapabilities(status);
        if(typeof callback === "function"){
          callback(status);
        }
      });
      if(nextHide !== null){
        $timeout.cancel(nextHide);
        nextHide = null;
      }
      if(nextDestroy !== null){
        $timeout.cancel(nextDestroy);
        nextDestroy = null;
      }
  };

  /**
   * Start a new scan.
   *
   * The callback receives: (err, contents)
   */
  this.scan = function(callback) {
    $log.debug('Scanning...');
    QRScanner.scan(callback);
  };

  this.pausePreview = function() {
    QRScanner.pausePreview();
  };

  this.resumePreview = function() {
    QRScanner.resumePreview();
  };

  /**
   * Deactivate the QRScanner. To balance user-perceived performance and power
   * consumption, this kicks off a countdown which will "sleep" the scanner
   * after a certain amount of time.
   *
   * The `status` of QRScanner is passed to the callback when deactivation
   * is complete.
   */
  this.deactivate = function(callback) {
    $log.debug('Deactivating scanner...');
    QRScanner.cancelScan();
    nextHide = $timeout(_hide, hideAfterSeconds * 1000);
    nextDestroy = $timeout(_destroy, destroyAfterSeconds * 1000);
  };

  // Natively hide the QRScanner's preview
  // On mobile platforms, this can reduce GPU/power usage
  // On desktop, this fully turns off the camera (and any associated privacy lights)
  function _hide(){
    $log.debug('Scanner not in use for ' + hideAfterSeconds + ' seconds, hiding...');
    QRScanner.hide();
  }

  // Reduce QRScanner power/processing consumption by the maximum amount
  function _destroy(){
    $log.debug('Scanner not in use for ' + destroyAfterSeconds + ' seconds, destroying...');
    QRScanner.destroy();
  }

  this.reinitialize = function(callback){
    initializeCompleted = false;
    QRScanner.destroy();
    initialize(callback);
  };

  /**
   * Toggle the device light (if available).
   *
   * The callback receives a boolean which is `true` if the light is enabled.
   */
  this.toggleLight = function(callback) {
    $log.debug('Toggling light...');
    if(lightEnabled){
      QRScanner.disableLight(_handleResponse);
    } else {
      QRScanner.enableLight(_handleResponse);
    }
    function _handleResponse(err, status){
      if(err){
        $log.error(err);
      } else {
        lightEnabled = status.lightEnabled;
        var state = lightEnabled? 'enabled' : 'disabled';
        $log.debug('Light ' + state + '.');
      }
      callback(lightEnabled);
    }
  };

  /**
   * Switch cameras (if a second camera is available).
   *
   * The `status` of QRScanner is passed to the callback when activation
   * is complete.
   */
  this.toggleCamera = function(callback) {
    var nextCamera = backCamera? 1 : 0;
    function cameraToString(index){
      return index === 1? 'front' : 'back'; // front = 1, back = 0
    }
    $log.debug('Toggling to the ' + cameraToString(nextCamera) + ' camera...');
    QRScanner.useCamera(nextCamera, function(err, status){
      if(err){
        $log.error(err);
      }
      backCamera = status.currentCamera === 1? false : true;
      $log.debug('Camera toggled. Now using the ' + cameraToString(backCamera) + ' camera.');
      callback(status);
    });
  };

  this.openSettings = function() {
    $log.debug('Attempting to open device settings...');
    QRScanner.openSettings();
  };
});
