'use strict';

angular.module('copayApp.controllers').controller('indexController', function($rootScope, $scope, $log, $filter, $timeout, $ionicScrollDelegate, $ionicPopup, $ionicSideMenuDelegate, $httpBackend, latestReleaseService, feeService, bwcService, pushNotificationsService, lodash, go, profileService, configService, rateService, storageService, addressService, gettext, gettextCatalog, amMoment, addonManager, bwcError, txFormatService, uxLanguage, glideraService, coinbaseService, platformInfo, addressbookService, openURLService, ongoingProcess) {

  var self = this;
  var SOFT_CONFIRMATION_LIMIT = 12;
  var errors = bwcService.getErrors();
  var historyUpdateInProgress = {};
  var isChromeApp = platformInfo.isChromeApp;
  var isCordova = platformInfo.isCordova;
  var isNW = platformInfo.isNW;

  var ret = {};
  ret.isCordova = isCordova;
  ret.isChromeApp = isChromeApp;
  ret.isSafari = platformInfo.isSafari;
  ret.isWindowsPhoneApp = platformInfo.isWP;
  ret.historyShowLimit = 10;
  ret.historyShowMoreLimit = 10;
  ret.isSearching = false;
  ret.prevState = 'walletHome';
  ret.physicalScreenWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);

  ret.appConfig = window.appConfig;

  // Only for testing
  //storageService.checkQuota();

  ret.menu = [{
    'title': gettext('Receive'),
    'icon': {
      false: 'icon-receive',
      true: 'icon-receive-active'
    },
    'link': 'receive'
  }, {
    'title': gettext('Activity'),
    'icon': {
      false: 'icon-activity',
      true: 'icon-activity-active'
    },
    'link': 'walletHome'
  }, {
    'title': gettext('Send'),
    'icon': {
      false: 'icon-send',
      true: 'icon-send-active'
    },
    'link': 'send'
  }];

  ret.addonViews = addonManager.addonViews();
  ret.txTemplateUrl = addonManager.txTemplateUrl() || 'views/includes/transaction.html';

  ret.tab = 'walletHome';
  var vanillaScope = ret;

  if (isNW) {
    latestReleaseService.checkLatestRelease(function(err, newRelease) {
      if (err) {
        $log.warn(err);
        return;
      }

      if (newRelease)
        $scope.newRelease = gettext('There is a new version of Copay. Please update');
    });
  }

  function strip(number) {
    return (parseFloat(number.toPrecision(12)));
  };

  self.goHome = function() {
    go.walletHome();
  };

  self.allowRefresher = function() {
    if ($ionicSideMenuDelegate.getOpenRatio() != 0) self.allowPullToRefresh = false;
  }

  self.hideBalance = function() {
    storageService.getHideBalanceFlag(self.walletId, function(err, shouldHideBalance) {
      if (err) self.shouldHideBalance = false;
      else self.shouldHideBalance = (shouldHideBalance == 'true') ? true : false;
    });
  }

  self.onHold = function() {
    self.shouldHideBalance = !self.shouldHideBalance;
    storageService.setHideBalanceFlag(self.walletId, self.shouldHideBalance.toString(), function() {});
  }

  self.setWalletPreferencesTitle = function() {
    return gettext("Wallet Preferences");
  }

  self.cleanInstance = function() {
    $log.debug('Cleaning Index Instance');
    lodash.each(self, function(v, k) {
      if (lodash.isFunction(v)) return;
      // This are to prevent flicker in mobile:
      if (k == 'hasProfile') return;
      if (k == 'tab') return;
      if (k == 'noFocusedWallet') return;
      if (k == 'backgroundColor') return;
      if (k == 'physicalScreenWidth') return;
      if (k == 'loadingWallet') {
        self.loadingWallet = true;
        return;
      }
      if (!lodash.isUndefined(vanillaScope[k])) {
        self[k] = vanillaScope[k];
        return;
      }

      delete self[k];
    });
  };

  self.setFocusedWallet = function() {
    var fc = profileService.focusedClient;
    if (!fc) return;

    self.cleanInstance();
    self.loadingWallet = true;
    self.setSpendUnconfirmed();

    $timeout(function() {
      $rootScope.$apply();

      self.hasProfile = true;
      self.isSingleAddress = false;
      self.noFocusedWallet = false;
      self.updating = false;

      // Credentials Shortcuts
      self.m = fc.credentials.m;
      self.n = fc.credentials.n;
      self.network = fc.credentials.network;
      self.copayerId = fc.credentials.copayerId;
      self.copayerName = fc.credentials.copayerName;
      self.requiresMultipleSignatures = fc.credentials.m > 1;
      self.isShared = fc.credentials.n > 1;
      self.walletName = fc.credentials.walletName;
      self.walletId = fc.credentials.walletId;
      self.isComplete = fc.isComplete();
      self.canSign = fc.canSign();
      self.isPrivKeyExternal = fc.isPrivKeyExternal();
      self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted();
      self.externalSource = fc.getPrivKeyExternalSourceName();
      self.account = fc.credentials.account;
      self.incorrectDerivation = fc.keyDerivationOk === false;

      if (self.externalSource == 'trezor')
        self.account++;

      self.txps = [];
      self.copayers = [];
      self.updateColor();
      self.updateAlias();
      self.setAddressbook();

      self.initGlidera();
      self.initCoinbase();

      self.hideBalance();

      self.setCustomBWSFlag();

      if (!self.isComplete) {
        $log.debug('Wallet not complete BEFORE update... redirecting');
        go.path('copayers');
      } else {
        if (go.is('copayers')) {
          $log.debug('Wallet Complete BEFORE update... redirect to home');
          go.walletHome();
        }
      }

      profileService.needsBackup(fc, function(needsBackup) {
        self.needsBackup = needsBackup;
        self.openWallet(function() {
          if (!self.isComplete) {
            $log.debug('Wallet not complete after update... redirecting');
            go.path('copayers');
          } else {
            if (go.is('copayers')) {
              $log.debug('Wallet Complete after update... redirect to home');
              go.walletHome();
            }
          }
        });
      });
    });
  };

  self.setCustomBWSFlag = function() {
    var defaults = configService.getDefaults();
    var config = configService.getSync();

    self.usingCustomBWS = config.bwsFor && config.bwsFor[self.walletId] && (config.bwsFor[self.walletId] != defaults.bws.url);
  };


  self.setTab = function(tab, reset, tries, switchState) {
    tries = tries || 0;

    // check if the whole menu item passed
    if (typeof tab == 'object') {
      if (tab.open) {
        if (tab.link) {
          self.tab = tab.link;
        }
        tab.open();
        return;
      } else {
        return self.setTab(tab.link, reset, tries, switchState);
      }
    }
    if (self.tab === tab && !reset)
      return;

    if (!document.getElementById('menu-' + tab) && ++tries < 5) {
      return $timeout(function() {
        self.setTab(tab, reset, tries, switchState);
      }, 300);
    }

    if (!self.tab || !go.is('walletHome'))
      self.tab = 'walletHome';

    var changeTab = function() {
      if (document.getElementById(self.tab)) {
        document.getElementById(self.tab).className = 'tab-out tab-view ' + self.tab;
        var old = document.getElementById('menu-' + self.tab);
        if (old) {
          old.className = '';
        }
      }

      if (document.getElementById(tab)) {
        document.getElementById(tab).className = 'tab-in  tab-view ' + tab;
        var newe = document.getElementById('menu-' + tab);
        if (newe) {
          newe.className = 'active';
        }
      }

      self.tab = tab;
      $rootScope.$emit('Local/TabChanged', tab);
    };

    if (switchState && !go.is('walletHome')) {
      go.path('walletHome', function() {
        changeTab();
      });
      return;
    }

    changeTab();
  };


  self.setSpendUnconfirmed = function(spendUnconfirmed) {
    self.spendUnconfirmed = spendUnconfirmed || configService.getSync().wallet.spendUnconfirmed;
  };

  self.updateBalance = function() {
    var fc = profileService.focusedClient;
    $timeout(function() {
      ongoingProcess.set('updatingBalance', true);
      $log.debug('Updating Balance');
      fc.getBalance(function(err, balance) {
        ongoingProcess.set('updatingBalance', false);
        if (err) {
          self.handleError(err);
          return;
        }
        $log.debug('Wallet Balance:', balance);
        self.setBalance(balance);
      });
    });
  };

  self.updatePendingTxps = function() {
    var fc = profileService.focusedClient;
    $timeout(function() {
      self.updating = true;
      $log.debug('Updating PendingTxps');
      fc.getTxProposals({}, function(err, txps) {
        self.updating = false;
        if (err) {
          self.handleError(err);
        } else {
          $log.debug('Wallet PendingTxps:', txps);
          self.setPendingTxps(txps);
        }
        $rootScope.$apply();
      });
    });
  };

  // This handles errors from BWS/index which normally
  // trigger from async events (like updates).
  // Debounce function avoids multiple popups
  var _handleError = function(err) {
    $log.warn('Client ERROR: ', err);
    if (err instanceof errors.NOT_AUTHORIZED) {
      self.notAuthorized = true;
      go.walletHome();
    } else if (err instanceof errors.NOT_FOUND) {
      self.showErrorPopup(gettext('Could not access Wallet Service: Not found'));
    } else {
      var msg = ""
      $scope.$emit('Local/ClientError', (err.error ? err.error : err));
      var msg = bwcError.msg(err, gettext('Error at Wallet Service'));
      self.showErrorPopup(msg);
    }
  };

  self.handleError = lodash.debounce(_handleError, 1000);

  self.openWallet = function(cb) {
    var fc = profileService.focusedClient;
    $timeout(function() {
      $rootScope.$apply();
      self.updating = true;
      self.updateError = false;
      fc.openWallet(function(err, walletStatus) {
        self.updating = false;
        if (err) {
          self.updateError = true;
          self.handleError(err);
          return;
        }
        $log.debug('Wallet Opened');

        self.updateAll(lodash.isObject(walletStatus) ? {
          walletStatus: walletStatus,
          cb: cb,
        } : {
          cb: cb
        });
        $rootScope.$apply();
      });
    });
  };

  self.setPendingTxps = function(txps) {
    self.pendingTxProposalsCountForUs = 0;
    var now = Math.floor(Date.now() / 1000);

    /* Uncomment to test multiple outputs */
    /*
    var txp = {
      message: 'test multi-output',
      fee: 1000,
      createdOn: new Date() / 1000,
      outputs: []
    };
    function addOutput(n) {
      txp.outputs.push({
        amount: 600,
        toAddress: '2N8bhEwbKtMvR2jqMRcTCQqzHP6zXGToXcK',
        message: 'output #' + (Number(n) + 1)
      });
    };
    lodash.times(150, addOutput);
    txps.push(txp);
    */

    lodash.each(txps, function(tx) {

      tx = txFormatService.processTx(tx);

      // no future transactions...
      if (tx.createdOn > now)
        tx.createdOn = now;

      var action = lodash.find(tx.actions, {
        copayerId: self.copayerId
      });

      if (!action && tx.status == 'pending') {
        tx.pendingForUs = true;
      }

      if (action && action.type == 'accept') {
        tx.statusForUs = 'accepted';
      } else if (action && action.type == 'reject') {
        tx.statusForUs = 'rejected';
      } else {
        tx.statusForUs = 'pending';
      }

      if (!tx.deleteLockTime)
        tx.canBeRemoved = true;

      if (tx.creatorId != self.copayerId) {
        self.pendingTxProposalsCountForUs = self.pendingTxProposalsCountForUs + 1;
      }
      addonManager.formatPendingTxp(tx);
    });
    self.txps = txps;
  };

  var SAFE_CONFIRMATIONS = 6;

  self.processNewTxs = function(txs) {
    var config = configService.getSync().wallet.settings;
    var now = Math.floor(Date.now() / 1000);
    var txHistoryUnique = {};
    var ret = [];
    self.hasUnsafeConfirmed = false;

    lodash.each(txs, function(tx) {
      tx = txFormatService.processTx(tx);

      // no future transactions...
      if (tx.time > now)
        tx.time = now;

      if (tx.confirmations >= SAFE_CONFIRMATIONS) {
        tx.safeConfirmed = SAFE_CONFIRMATIONS + '+';
      } else {
        tx.safeConfirmed = false;
        self.hasUnsafeConfirmed = true;
      }

      if (tx.note) {
        delete tx.note.encryptedEditedByName;
        delete tx.note.encryptedBody;
      }

      if (!txHistoryUnique[tx.txid]) {
        ret.push(tx);
        txHistoryUnique[tx.txid] = true;
      } else {
        $log.debug('Ignoring duplicate TX in history: ' + tx.txid)
      }
    });

    return ret;
  };

  self.updateAlias = function() {
    var config = configService.getSync();
    config.aliasFor = config.aliasFor || {};
    self.alias = config.aliasFor[self.walletId];
    var fc = profileService.focusedClient;
    fc.alias = self.alias;
  };

  self.updateColor = function() {
    var config = configService.getSync();
    config.colorFor = config.colorFor || {};
    self.backgroundColor = config.colorFor[self.walletId] || '#4A90E2';
    var fc = profileService.focusedClient;
    fc.backgroundColor = self.backgroundColor;
    if (isCordova && StatusBar.isVisible) {
      StatusBar.backgroundColorByHexString(fc.backgroundColor);
    }
  };

  self.setBalance = function(balance) {
    if (!balance) return;
    var config = configService.getSync().wallet.settings;
    var COIN = 1e8;


    // Address with Balance
    self.balanceByAddress = balance.byAddress;

    // Spend unconfirmed funds
    if (self.spendUnconfirmed) {
      self.totalBalanceSat = balance.totalAmount;
      self.lockedBalanceSat = balance.lockedAmount;
      self.availableBalanceSat = balance.availableAmount;
      self.totalBytesToSendMax = balance.totalBytesToSendMax;
      self.pendingAmount = null;
    } else {
      self.totalBalanceSat = balance.totalConfirmedAmount;
      self.lockedBalanceSat = balance.lockedConfirmedAmount;
      self.availableBalanceSat = balance.availableConfirmedAmount;
      self.totalBytesToSendMax = balance.totalBytesToSendConfirmedMax;
      self.pendingAmount = balance.totalAmount - balance.totalConfirmedAmount;
    }

    // Selected unit
    self.unitToSatoshi = config.unitToSatoshi;
    self.satToUnit = 1 / self.unitToSatoshi;
    self.unitName = config.unitName;

    //STR
    self.totalBalanceStr = profileService.formatAmount(self.totalBalanceSat) + ' ' + self.unitName;
    self.lockedBalanceStr = profileService.formatAmount(self.lockedBalanceSat) + ' ' + self.unitName;
    self.availableBalanceStr = profileService.formatAmount(self.availableBalanceSat) + ' ' + self.unitName;

    if (self.pendingAmount) {
      self.pendingAmountStr = profileService.formatAmount(self.pendingAmount) + ' ' + self.unitName;
    } else {
      self.pendingAmountStr = null;
    }

    self.alternativeName = config.alternativeName;
    self.alternativeIsoCode = config.alternativeIsoCode;

    // Check address
    addressService.isUsed(self.walletId, balance.byAddress, function(err, used) {
      if (used) {
        $log.debug('Address used. Creating new');
        $rootScope.$emit('Local/AddressIsUsed');
      }
    });

    rateService.whenAvailable(function() {

      var totalBalanceAlternative = rateService.toFiat(self.totalBalanceSat, self.alternativeIsoCode);
      var lockedBalanceAlternative = rateService.toFiat(self.lockedBalanceSat, self.alternativeIsoCode);
      var alternativeConversionRate = rateService.toFiat(100000000, self.alternativeIsoCode);

      self.totalBalanceAlternative = $filter('formatFiatAmount')(totalBalanceAlternative);
      self.lockedBalanceAlternative = $filter('formatFiatAmount')(lockedBalanceAlternative);
      self.alternativeConversionRate = $filter('formatFiatAmount')(alternativeConversionRate);

      self.alternativeBalanceAvailable = true;

      self.isRateAvailable = true;
      $rootScope.$apply();
    });

    if (!rateService.isAvailable()) {
      $rootScope.$apply();
    }
  };


  self.showMore = function() {
    $timeout(function() {
      if (self.isSearching) {
        self.txHistorySearchResults = self.result.slice(0, self.nextTxHistory);
        $log.debug('Total txs: ', self.txHistorySearchResults.length + '/' + self.result.length);
        if (self.txHistorySearchResults.length >= self.result.length)
          self.historyShowMore = false;
      } else {
        self.txHistory = self.completeHistory.slice(0, self.nextTxHistory);
        $log.debug('Total txs: ', self.txHistory.length + '/' + self.completeHistory.length);
        if (self.txHistory.length >= self.completeHistory.length)
          self.historyShowMore = false;
      }
      self.nextTxHistory += self.historyShowMoreLimit;
      $scope.$broadcast('scroll.infiniteScrollComplete');
    }, 100);
  };




  self.toggleLeftMenu = function() {
    profileService.isDisclaimerAccepted(function(val) {
      if (val) go.toggleLeftMenu();
      else
        $log.debug('Disclaimer not accepted, cannot open menu');
    });
  };

  self.initGlidera = function(accessToken) {
    self.glideraEnabled = configService.getSync().glidera.enabled;
    self.glideraTestnet = configService.getSync().glidera.testnet;
    var network = self.glideraTestnet ? 'testnet' : 'livenet';

    self.glideraToken = null;
    self.glideraError = null;
    self.glideraPermissions = null;
    self.glideraEmail = null;
    self.glideraPersonalInfo = null;
    self.glideraTxs = null;
    self.glideraStatus = null;

    if (!self.glideraEnabled) return;

    glideraService.setCredentials(network);

    var getToken = function(cb) {
      if (accessToken) {
        cb(null, accessToken);
      } else {
        storageService.getGlideraToken(network, cb);
      }
    };

    getToken(function(err, accessToken) {
      if (err || !accessToken) return;
      else {
        glideraService.getAccessTokenPermissions(accessToken, function(err, p) {
          if (err) {
            self.glideraError = err;
          } else {
            self.glideraToken = accessToken;
            self.glideraPermissions = p;
            self.updateGlidera({
              fullUpdate: true
            });
          }
        });
      }
    });
  };

  self.updateGlidera = function(opts) {
    if (!self.glideraToken || !self.glideraPermissions) return;
    var accessToken = self.glideraToken;
    var permissions = self.glideraPermissions;

    opts = opts || {};

    glideraService.getStatus(accessToken, function(err, data) {
      self.glideraStatus = data;
    });

    glideraService.getLimits(accessToken, function(err, limits) {
      self.glideraLimits = limits;
    });

    if (permissions.transaction_history) {
      glideraService.getTransactions(accessToken, function(err, data) {
        self.glideraTxs = data;
      });
    }

    if (permissions.view_email_address && opts.fullUpdate) {
      glideraService.getEmail(accessToken, function(err, data) {
        self.glideraEmail = data.email;
      });
    }
    if (permissions.personal_info && opts.fullUpdate) {
      glideraService.getPersonalInfo(accessToken, function(err, data) {
        self.glideraPersonalInfo = data;
      });
    }

  };

  self.initCoinbase = function(accessToken) {
    self.coinbaseEnabled = configService.getSync().coinbase.enabled;
    self.coinbaseTestnet = configService.getSync().coinbase.testnet;
    var network = self.coinbaseTestnet ? 'testnet' : 'livenet';

    self.coinbaseToken = null;
    self.coinbaseError = null;
    self.coinbasePermissions = null;
    self.coinbaseEmail = null;
    self.coinbasePersonalInfo = null;
    self.coinbaseTxs = null;
    self.coinbaseStatus = null;

    if (!self.coinbaseEnabled) return;

    coinbaseService.setCredentials(network);

    var getToken = function(cb) {
      if (accessToken) {
        cb(null, accessToken);
      } else {
        storageService.getCoinbaseToken(network, cb);
      }
    };

    getToken(function(err, accessToken) {
      if (err || !accessToken) return;
      else {
        coinbaseService.getAccounts(accessToken, function(err, a) {
          if (err) {
            self.coinbaseError = err;
            if (err.errors[0] && err.errors[0].id == 'expired_token') {
              self.refreshCoinbaseToken();
            }
          } else {
            self.coinbaseToken = accessToken;
            lodash.each(a.data, function(account) {
              if (account.primary && account.type == 'wallet') {
                self.coinbaseAccount = account;
                self.updateCoinbase();
              }
            });
          }
        });
      }
    });
  };

  self.updateCoinbase = lodash.debounce(function(opts) {
    if (!self.coinbaseToken || !self.coinbaseAccount) return;
    var accessToken = self.coinbaseToken;
    var accountId = self.coinbaseAccount.id;

    opts = opts || {};

    if (opts.updateAccount) {
      coinbaseService.getAccount(accessToken, accountId, function(err, a) {
        if (err) {
          self.coinbaseError = err;
          if (err.errors[0] && err.errors[0].id == 'expired_token') {
            self.refreshCoinbaseToken();
          }
          return;
        }
        self.coinbaseAccount = a.data;
      });
    }

    coinbaseService.getCurrentUser(accessToken, function(err, u) {
      if (err) {
        self.coinbaseError = err;
        if (err.errors[0] && err.errors[0].id == 'expired_token') {
          self.refreshCoinbaseToken();
        }
        return;
      }
      self.coinbaseUser = u.data;
    });

    coinbaseService.getPendingTransactions(function(err, txs) {
      self.coinbasePendingTransactions = lodash.isEmpty(txs) ? null : txs;
      lodash.forEach(txs, function(dataFromStorage, txId) {
        if ((dataFromStorage.type == 'sell' && dataFromStorage.status == 'completed') ||
          (dataFromStorage.type == 'buy' && dataFromStorage.status == 'completed') ||
          dataFromStorage.status == 'error' ||
          (dataFromStorage.type == 'send' && dataFromStorage.status == 'completed')) return;
        coinbaseService.getTransaction(accessToken, accountId, txId, function(err, tx) {
          if (err) {
            if (err.errors[0] && err.errors[0].id == 'expired_token') {
              self.refreshCoinbaseToken();
              return;
            }
            coinbaseService.savePendingTransaction(dataFromStorage, {
              status: 'error',
              error: err
            }, function(err) {
              if (err) $log.debug(err);
            });
            return;
          }
          _updateCoinbasePendingTransactions(dataFromStorage, tx.data);
          self.coinbasePendingTransactions[txId] = dataFromStorage;
          if (tx.data.type == 'send' && tx.data.status == 'completed' && tx.data.from) {
            coinbaseService.sellPrice(accessToken, dataFromStorage.sell_price_currency, function(err, s) {
              if (err) {
                if (err.errors[0] && err.errors[0].id == 'expired_token') {
                  self.refreshCoinbaseToken();
                  return;
                }
                coinbaseService.savePendingTransaction(dataFromStorage, {
                  status: 'error',
                  error: err
                }, function(err) {
                  if (err) $log.debug(err);
                });
                return;
              }
              var newSellPrice = s.data.amount;
              var variance = Math.abs((newSellPrice - dataFromStorage.sell_price_amount) / dataFromStorage.sell_price_amount * 100);
              if (variance < dataFromStorage.price_sensitivity.value) {
                self.sellPending(tx.data);
              } else {
                var error = {
                  errors: [{
                    message: 'Price falls over the selected percentage'
                  }]
                };
                coinbaseService.savePendingTransaction(dataFromStorage, {
                  status: 'error',
                  error: error
                }, function(err) {
                  if (err) $log.debug(err);
                });
              }
            });
          } else if (tx.data.type == 'buy' && tx.data.status == 'completed' && tx.data.buy) {
            self.sendToCopay(dataFromStorage);
          } else {
            coinbaseService.savePendingTransaction(dataFromStorage, {}, function(err) {
              if (err) $log.debug(err);
            });
          }
        });
      });
    });

  }, 1000);

  var _updateCoinbasePendingTransactions = function(obj /*, …*/ ) {
    for (var i = 1; i < arguments.length; i++) {
      for (var prop in arguments[i]) {
        var val = arguments[i][prop];
        if (typeof val == "object")
          _updateCoinbasePendingTransactions(obj[prop], val);
        else
          obj[prop] = val ? val : obj[prop];
      }
    }
    return obj;
  };

  self.refreshCoinbaseToken = function() {
    var network = self.coinbaseTestnet ? 'testnet' : 'livenet';
    storageService.getCoinbaseRefreshToken(network, function(err, refreshToken) {
      if (!refreshToken) return;
      coinbaseService.refreshToken(refreshToken, function(err, data) {
        if (err) {
          self.coinbaseError = err;
        } else if (data && data.access_token && data.refresh_token) {
          storageService.setCoinbaseToken(network, data.access_token, function() {
            storageService.setCoinbaseRefreshToken(network, data.refresh_token, function() {
              $timeout(function() {
                self.initCoinbase(data.access_token);
              }, 100);
            });
          });
        }
      });
    });
  };

  self.sendToCopay = function(tx) {
    if (!tx) return;
    var data = {
      to: tx.toAddr,
      amount: tx.amount.amount,
      currency: tx.amount.currency,
      description: 'To Copay Wallet'
    };
    coinbaseService.sendTo(self.coinbaseToken, self.coinbaseAccount.id, data, function(err, res) {
      if (err) {
        if (err.errors[0] && err.errors[0].id == 'expired_token') {
          self.refreshCoinbaseToken();
          return;
        }
        coinbaseService.savePendingTransaction(tx, {
          status: 'error',
          error: err
        }, function(err) {
          if (err) $log.debug(err);
        });
      } else {
        if (!res.data.id) {
          coinbaseService.savePendingTransaction(tx, {
            status: 'error',
            error: err
          }, function(err) {
            if (err) $log.debug(err);
          });
          return;
        }
        coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.id, function(err, sendTx) {
          coinbaseService.savePendingTransaction(tx, {
            remove: true
          }, function(err) {
            coinbaseService.savePendingTransaction(sendTx.data, {}, function(err) {
              $timeout(function() {
                self.updateCoinbase({
                  updateAccount: true
                });
              }, 1000);
            });
          });
        });
      }
    });
  };

  self.sellPending = function(tx) {
    if (!tx) return;
    var data = tx.amount;
    data['commit'] = true;
    coinbaseService.sellRequest(self.coinbaseToken, self.coinbaseAccount.id, data, function(err, res) {
      if (err) {
        if (err.errors[0] && err.errors[0].id == 'expired_token') {
          self.refreshCoinbaseToken();
          return;
        }
        coinbaseService.savePendingTransaction(tx, {
          status: 'error',
          error: err
        }, function(err) {
          if (err) $log.debug(err);
        });
      } else {
        if (!res.data.transaction) {
          coinbaseService.savePendingTransaction(tx, {
            status: 'error',
            error: err
          }, function(err) {
            if (err) $log.debug(err);
          });
          return;
        }
        coinbaseService.savePendingTransaction(tx, {
          remove: true
        }, function(err) {
          coinbaseService.getTransaction(self.coinbaseToken, self.coinbaseAccount.id, res.data.transaction.id, function(err, updatedTx) {
            coinbaseService.savePendingTransaction(updatedTx.data, {}, function(err) {
              if (err) $log.debug(err);
              $timeout(function() {
                self.updateCoinbase({
                  updateAccount: true
                });
              }, 1000);
            });
          });
        });
      }
    });
  };

  self.isInFocus = function(walletId) {
    var fc = profileService.focusedClient;
    return fc && fc.credentials.walletId == walletId;
  };

  self.setAddressbook = function(ab) {
    if (ab) {
      self.addressbook = ab;
      return;
    }

    addressbookService.list(function(err, ab) {
      if (err) {
        $log.error('Error getting the addressbook');
        return;
      }
      self.addressbook = ab;
    });
  };

  $rootScope.$on('$stateChangeSuccess', function(ev, to, toParams, from, fromParams) {
    self.prevState = from.name || 'walletHome';
    self.tab = 'walletHome';
  });

  $rootScope.$on('Local/ValidatingWalletEnded', function(ev, walletId, isOK) {

    if (self.isInFocus(walletId)) {
      // NOTE: If the user changed the wallet, the flag is already turn off.
      self.incorrectDerivation = isOK === false;
    }
  });

  $rootScope.$on('Local/ClearHistory', function(event) {
    $log.debug('The wallet transaction history has been deleted');
    self.txHistory = self.completeHistory = self.txHistorySearchResults = [];
    self.debounceUpdateHistory();
  });

  $rootScope.$on('Local/AddressbookUpdated', function(event, ab) {
    self.setAddressbook(ab);
  });

  // UX event handlers
  $rootScope.$on('Local/ColorUpdated', function(event) {
    self.updateColor();
    $timeout(function() {
      $rootScope.$apply();
    });
  });

  $rootScope.$on('Local/AliasUpdated', function(event) {
    self.updateAlias();
    $timeout(function() {
      $rootScope.$apply();
    });
  });

  $rootScope.$on('Local/SpendUnconfirmedUpdated', function(event, spendUnconfirmed) {
    self.setSpendUnconfirmed(spendUnconfirmed);
    self.updateAll();
  });

  $rootScope.$on('Local/GlideraUpdated', function(event, accessToken) {
    self.initGlidera(accessToken);
  });

  $rootScope.$on('Local/CoinbaseUpdated', function(event, accessToken) {
    self.initCoinbase(accessToken);
  });

  $rootScope.$on('Local/GlideraTx', function(event, accessToken, permissions) {
    self.updateGlidera();
  });

  $rootScope.$on('Local/CoinbaseTx', function(event) {
    self.updateCoinbase({
      updateAccount: true
    });
  });

  $rootScope.$on('Local/GlideraError', function(event) {
    self.debouncedUpdate();
  });

  $rootScope.$on('Local/UnitSettingUpdated', function(event) {
    self.updateAll({
      triggerTxUpdate: true,
    });
  });

  $rootScope.$on('Local/WalletCompleted', function(event, walletId) {
    if (self.isInFocus(walletId)) {
      // reset main wallet variables
      self.setFocusedWallet();
      go.walletHome();
    }
  });

  self.debouncedUpdate = function() {
    var now = Date.now();
    var oneHr = 1000 * 60 * 60;

    if (!self.lastUpdate || (now - self.lastUpdate) > oneHr) {
      self.updateAll({
        quiet: true,
        triggerTxUpdate: true
      });
    }
  };

  $rootScope.$on('Local/Resume', function(event) {
    $log.debug('### Resume event');
    profileService.isDisclaimerAccepted(function(v) {
      if (!v) {
        $log.debug('Disclaimer not accepted, resume to home');
        go.path('disclaimer');
      }
    });
    self.debouncedUpdate();
  });

  $rootScope.$on('Local/BackupDone', function(event, walletId) {
    self.needsBackup = false;
    $log.debug('Backup done');
    storageService.setBackupFlag(walletId || self.walletId, function(err) {
      $log.debug('Backup stored');
    });
  });

  $rootScope.$on('Local/DeviceError', function(event, err) {
    self.showErrorPopup(err, function() {
      if (isCordova && navigator && navigator.app) {
        navigator.app.exitApp();
      }
    });
  });

  $rootScope.$on('Local/WalletImported', function(event, walletId) {
console.log('[index.js:1063] walletImported'); //TODO
    return;
    self.needsBackup = false;
    storageService.setBackupFlag(walletId, function() {
      $log.debug('Backup done stored');
      addressService.expireAddress(walletId, function(err) {
        $timeout(function() {
          self.txHistory = self.completeHistory = self.txHistorySearchResults = [];
          storageService.removeTxHistory(walletId, function() {
            self.startScan(walletId);
          });
        }, 500);
      });
    });
  });

  $rootScope.$on('NewIncomingTx', function() {
    self.newTx = true;
    self.updateAll({
      walletStatus: null,
      untilItChanges: true,
      triggerTxUpdate: true,
    });
  });


  $rootScope.$on('NewBlock', function() {
    if (self.glideraEnabled) {
      $timeout(function() {
        self.updateGlidera();
      });
    }
    if (self.coinbaseEnabled) {
      $timeout(function() {
        self.updateCoinbase();
      });
    }
    if (self.pendingAmount) {
      self.updateAll({
        walletStatus: null,
        untilItChanges: null,
        triggerTxUpdate: true,
      });
    } else if (self.hasUnsafeConfirmed) {
      $log.debug('Wallet has transactions with few confirmations. Updating.')
      if (self.network == 'testnet') {
        self.throttledUpdateHistory();
      } else {
        self.debounceUpdateHistory();
      }
    }
  });

  $rootScope.$on('BalanceUpdated', function(e, n) {
    self.setBalance(n.data);
  });


  //untilItChange TRUE
  lodash.each(['NewOutgoingTx', 'NewOutgoingTxByThirdParty'], function(eventName) {
    $rootScope.$on(eventName, function(event) {
      self.newTx = true;
      self.updateAll({
        walletStatus: null,
        untilItChanges: true,
        triggerTxUpdate: true,
      });
    });
  });

  //untilItChange FALSE
  lodash.each(['NewTxProposal', 'TxProposalFinallyRejected', 'TxProposalRemoved', 'NewOutgoingTxByThirdParty',
    'Local/GlideraTx'
  ], function(eventName) {
    $rootScope.$on(eventName, function(event) {
      self.updateAll({
        walletStatus: null,
        untilItChanges: null,
        triggerTxUpdate: true,
      });
    });
  });


  //untilItChange Maybe
  $rootScope.$on('Local/TxProposalAction', function(event, untilItChanges) {
    self.newTx = untilItChanges;
    self.updateAll({
      walletStatus: null,
      untilItChanges: untilItChanges,
      triggerTxUpdate: true,
    });
  });

  $rootScope.$on('ScanFinished', function() {
    $log.debug('Scan Finished. Updating history');
    storageService.removeTxHistory(self.walletId, function() {
      self.updateAll({
        walletStatus: null,
        triggerTxUpdate: true,
      });
    });
  });

  lodash.each(['TxProposalRejectedBy', 'TxProposalAcceptedBy'], function(eventName) {
    $rootScope.$on(eventName, function() {
      var f = function() {
        if (self.updating) {
          return $timeout(f, 200);
        };
        self.updatePendingTxps();
      };
      f();
    });
  });

  $rootScope.$on('Local/NoWallets', function(event) {
    $timeout(function() {
      self.hasProfile = true;
      self.noFocusedWallet = true;
      self.isComplete = null;
      self.walletName = null;
      uxLanguage.update();
    });
  });

  $rootScope.$on('Local/NewFocusedWallet', function() {
    console.log('[index.js.1200:NewFocusedWallet:] TODO'); //TODO

    return;


    uxLanguage.update();
    self.setFocusedWallet();
    self.updateHistory();
    storageService.getCleanAndScanAddresses(function(err, walletId) {

      if (walletId && profileService.walletClients[walletId]) {
        $log.debug('Clear last address cache and Scan ', walletId);
        addressService.expireAddress(walletId, function(err) {
          self.startScan(walletId);
        });
        storageService.removeCleanAndScanAddresses(function() {
          $rootScope.$emit('Local/NewFocusedWalletReady');
        });
      } else {
        $rootScope.$emit('Local/NewFocusedWalletReady');
      }
    });
  });

  $rootScope.$on('Local/SetTab', function(event, tab, reset) {
    self.setTab(tab, reset);
  });

  $rootScope.$on('disclaimerAccepted', function(event) {
    $scope.isDisclaimerAccepted = true;
  });

  $rootScope.$on('Local/WindowResize', function() {
    self.physicalScreenWidth = ((window.innerWidth > 0) ? window.innerWidth : screen.width);
  });

  $rootScope.$on('Local/NeedsConfirmation', function(event, txp, cb) {

    function openConfirmationPopup(txp, cb) {

      var config = configService.getSync();

      $scope.color = config.colorFor[txp.walletId] || '#4A90E2';
      $scope.tx = txFormatService.processTx(txp);

      self.confirmationPopup = $ionicPopup.show({
        templateUrl: 'views/includes/confirm-tx.html',
        scope: $scope,
      });

      $scope.processFee = function(amount, fee) {
        var walletSettings = configService.getSync().wallet.settings;
        var feeAlternativeIsoCode = walletSettings.alternativeIsoCode;

        $scope.feeLevel = feeService.feeOpts[feeService.getCurrentFeeLevel()];
        $scope.feeAlternativeStr = parseFloat((rateService.toFiat(fee, feeAlternativeIsoCode)).toFixed(2), 10) + ' ' + feeAlternativeIsoCode;
        $scope.feeRateStr = (fee / (amount + fee) * 100).toFixed(2) + '%';
      };

      $scope.cancel = function() {
        return cb();
      };

      $scope.accept = function() {
        return cb(true);
      };
    }

    openConfirmationPopup(txp, function(accept) {
      self.confirmationPopup.close();
      return cb(accept);
    });
  });

  $rootScope.$on('Local/NeedsPassword', function(event, isSetup, cb) {

    function openPasswordPopup(isSetup, cb) {
      $scope.data = {};
      $scope.data.password = null;
      $scope.isSetup = isSetup;
      $scope.isVerification = false;
      $scope.loading = false;
      var pass = null;

      self.passwordPopup = $ionicPopup.show({
        templateUrl: 'views/includes/password.html',
        scope: $scope,
      });

      $scope.cancel = function() {
        return cb('No spending password given');
      };

      $scope.keyPress = function(event) {
        if (!$scope.data.password || $scope.loading) return;
        if (event.keyCode == 13) $scope.set();
      }

      $scope.set = function() {
        $scope.loading = true;
        $scope.error = null;

        $timeout(function() {
          if (isSetup && !$scope.isVerification) {
            $scope.loading = false;
            $scope.isVerification = true;
            pass = $scope.data.password;
            $scope.data.password = null;
            return;
          }
          if (isSetup && pass != $scope.data.password) {
            $scope.loading = false;
            $scope.error = gettext('Spending Passwords do not match');
            $scope.isVerification = false;
            $scope.data.password = null;
            pass = null;
            return;
          }
          return cb(null, $scope.data.password);
        }, 100);
      };
    };

    openPasswordPopup(isSetup, function(err, pass) {
      self.passwordPopup.close();
      return cb(err, pass);
    });

  });

  $rootScope.$on('Local/EmailUpdated', function(event, email) {
    self.preferences.email = email;
  });

  lodash.each(['NewCopayer', 'CopayerUpdated'], function(eventName) {
    $rootScope.$on(eventName, function() {
console.log('[index.js:1324] NewCopayer, CopayerUpdated'); //TODO
      return;
      // Re try to open wallet (will triggers)
      self.setFocusedWallet();
    });
  });

  $rootScope.$on('Local/NewEncryptionSetting', function() {
    var fc = profileService.focusedClient;
    self.isPrivKeyEncrypted = fc.isPrivKeyEncrypted();
    $timeout(function() {
      $rootScope.$apply();
    });
  });


  /* Start setup */
  lodash.assign(self, vanillaScope);
  openURLService.init();
});
