/**
   * Work out which restore method to use, depending on if it is a Trezor wallet or not and what
   * backups there are
   */
  public void calculateRestoreMethod() {
    Optional<HardwareWalletService> hardwareWalletService =
        CoreServices.getOrCreateHardwareWalletService();
    // User has selected restore wallet - see if wallet is hard Trezor wallet
    // If so no need to enter a seed phrase - use the rootNode from the master public key to work
    // out the wallet id
    HardwareWalletContext context = hardwareWalletService.get().getContext();
    // Create a wallet id from the rootNode to work out the wallet root directory
    if (context.getDeterministicKey().isPresent()) {
      walletId = Optional.of(new WalletId(context.getDeterministicKey().get().getIdentifier()));
      String walletRoot = WalletManager.createWalletRoot(walletId.get());
      log.debug("Hardware wallet root : {}", walletRoot);
    }
    // Ensure Trezor is cancelled
    hardwareWalletService.get().requestCancel();

    restoreMethod = RESTORE_WALLET_HARD_TREZOR;
    if (!isLocalZipBackupPresent()) {
      // Next ask for the cloud backup location
      state = RESTORE_WALLET_SELECT_BACKUP_LOCATION;
    } else {
      // Next select one of the local backups
      state = RESTORE_WALLET_SELECT_BACKUP;
    }
  }
  /**
   * Create a Trezor hard wallet from a backup summary, decrypting it with a password created from
   * the Trezor supplied entropy
   */
  private boolean createTrezorHardWallet() {

    // Get the model that contains the selected wallet backup to use
    SelectBackupSummaryModel selectedBackupSummaryModel =
        getWizardModel().getSelectBackupSummaryModel();

    if (selectedBackupSummaryModel == null
        || selectedBackupSummaryModel.getValue() == null
        || selectedBackupSummaryModel.getValue().getFile() == null) {
      log.debug("No wallet backup to use from the model");
      return false;
    }

    log.debug(
        "Loading hard wallet backup '" + selectedBackupSummaryModel.getValue().getFile() + "'");
    try {
      // For Trezor hard wallets the backups are encrypted with the entropy derived password
      String walletPassword = null;
      Optional<HardwareWalletService> hardwareWalletService =
          CoreServices.getOrCreateHardwareWalletService();
      if (hardwareWalletService.isPresent()
          && hardwareWalletService.get().getContext().getEntropy().isPresent()) {
        walletPassword =
            Hex.toHexString(hardwareWalletService.get().getContext().getEntropy().get());
      }

      // Check there is a wallet password - if not then cannot decrypt backup
      if (walletPassword == null) {
        log.debug(
            "Cannot work out the password to decrypt the backup - there is no entropy from the Trezor");
        return false;
      }
      KeyParameter backupAESKey =
          AESUtils.createAESKey(
              walletPassword.getBytes(Charsets.UTF_8), WalletManager.scryptSalt());

      WalletId loadedWalletId =
          BackupManager.INSTANCE.loadZipBackup(
              selectedBackupSummaryModel.getValue().getFile(), backupAESKey);

      // Attempt to open the wallet
      final Optional<WalletSummary> walletSummaryOptional =
          WalletManager.INSTANCE.openWalletFromWalletId(
              InstallationManager.getOrCreateApplicationDataDirectory(),
              loadedWalletId,
              walletPassword);

      // If the wallet is present then it was opened successfully
      return walletSummaryOptional.isPresent();

    } catch (Exception e) {
      log.error("Failed to restore Trezor hard wallet.", e);
    }

    // Must have failed to be here
    return false;
  }
  /**
   * @param state The state object
   * @param mode The mode (e.g. standard, Trezor etc)
   */
  public WelcomeWizardModel(WelcomeWizardState state, WelcomeWizardMode mode) {
    super(state);

    log.debug("Welcome wizard starting in state '{}' with mode '{}'", state.name(), mode.name());

    this.seedPhraseGenerator = CoreServices.newSeedPhraseGenerator();
    this.restoringSoftWallet = WelcomeWizardState.WELCOME_SELECT_WALLET.equals(state);
    this.mode = mode;

    // If restoring a Trezor hard wallet, work out the initial screen to show
    if (WelcomeWizardState.RESTORE_WALLET_SELECT_BACKUP.equals(state)) {
      calculateRestoreMethod();
      log.debug("Starting hard wallet restore with state: {}", state);
    }
  }
  /** @return True if synchronization is occurring correctly */
  private boolean handleSynchronizationStatus() {

    log.debug("Synchronizing...");

    try {

      return CoreServices.getOrCreateBitcoinNetworkService().isStartedOk();

    } catch (Exception e) {
      log.error("Failed to start Bitcoin network.", e);
    }

    // Must have failed to be here
    return false;
  }
  @Override
  public void showPrevious() {

    Optional<HardwareWalletService> hardwareWalletService;

    switch (state) {
      case WELCOME_LICENCE:
        state = WELCOME_LICENCE;
        break;
      case WELCOME_SELECT_LANGUAGE:
        state = WELCOME_LICENCE;
        break;
      case WELCOME_ATTACH_HARDWARE_WALLET:
        state = WELCOME_SELECT_LANGUAGE;
        break;
      case WELCOME_SELECT_WALLET:
        state = WELCOME_ATTACH_HARDWARE_WALLET;
        break;
      case CREATE_WALLET_PREPARATION:
        state = WELCOME_SELECT_WALLET;
        break;
      case CREATE_WALLET_SELECT_BACKUP_LOCATION:
        state = CREATE_WALLET_PREPARATION;
        break;
      case CREATE_WALLET_SEED_PHRASE:
        state = CREATE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case CREATE_WALLET_CONFIRM_SEED_PHRASE:
        state = CREATE_WALLET_SEED_PHRASE;
        break;
      case CREATE_WALLET_REPORT:
        state = CREATE_WALLET_SEED_PHRASE;
        break;
      case TREZOR_CREATE_WALLET_PREPARATION:
        hardwareWalletService = CoreServices.getOrCreateHardwareWalletService();
        if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) {
          // A Trezor is connected
          mode = WelcomeWizardMode.TREZOR;
        } else {
          // Standard mode
          mode = WelcomeWizardMode.STANDARD;
        }
        // Back out to select wallet as the most general solution
        state = WELCOME_SELECT_WALLET;
        break;
      case TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION:
        state = TREZOR_CREATE_WALLET_PREPARATION;
        break;
      case TREZOR_CREATE_WALLET_ENTER_DETAILS:
        state = TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_CONFIRM_ENTROPY:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_ENTER_NEW_PIN:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_CONFIRM_WORD:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case TREZOR_CREATE_WALLET_REPORT:
        throw new IllegalStateException(
            "'Previous' is not permitted here - user is committed to creating wallet");
      case RESTORE_PASSWORD_SEED_PHRASE:
        state = WELCOME_SELECT_WALLET;
        break;
      case RESTORE_PASSWORD_REPORT:
        state = RESTORE_PASSWORD_SEED_PHRASE;
        break;
      case RESTORE_WALLET_SEED_PHRASE:
        state = WELCOME_SELECT_WALLET;
        break;
      case RESTORE_WALLET_SELECT_BACKUP_LOCATION:
        state = RESTORE_WALLET_SEED_PHRASE;
        break;
      case RESTORE_WALLET_SELECT_BACKUP:
        state = RESTORE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case RESTORE_WALLET_TIMESTAMP:
        state = RESTORE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case RESTORE_WALLET_REPORT:
        state = restoreMethod;
        break;
      default:
        throw new IllegalStateException("Unknown state: " + state.name());
    }
  }
  @Override
  public void showNext() {

    Optional<HardwareWalletService> hardwareWalletService;

    switch (state) {
      case WELCOME_LICENCE:
        state = WELCOME_SELECT_LANGUAGE;
        break;
      case WELCOME_SELECT_LANGUAGE:
        state = WELCOME_ATTACH_HARDWARE_WALLET;
        break;
      case WELCOME_ATTACH_HARDWARE_WALLET:
        hardwareWalletService = CoreServices.getOrCreateHardwareWalletService();
        if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) {
          // A Trezor is connected
          mode = WelcomeWizardMode.TREZOR;
          if (hardwareWalletService.get().isWalletPresent()) {
            // User may want to create or restore since they have an initialised device
            state = WELCOME_SELECT_WALLET;
          } else {
            // User can only create from an uninitialised device
            state = TREZOR_CREATE_WALLET_PREPARATION;
          }
        } else {
          // Standard mode
          mode = WelcomeWizardMode.STANDARD;
          state = WELCOME_SELECT_WALLET;
        }
        break;
      case WELCOME_SELECT_WALLET:
        hardwareWalletService = CoreServices.getOrCreateHardwareWalletService();
        if (RESTORE_WALLET_SELECT_BACKUP.equals(selectWalletChoice)) {
          if (hardwareWalletService.isPresent()
              && hardwareWalletService.get().isDeviceReady()
              && hardwareWalletService.get().isWalletPresent()) {
            // Initialised hardware wallet is attached
            calculateRestoreMethod();
            break;
          }
        } else {
          // Ensure Trezor is reset if it is attached and initialised
          if (hardwareWalletService.isPresent() && hardwareWalletService.get().isDeviceReady()) {
            hardwareWalletService.get().requestCancel();
            hardwareWalletService.get().getContext().resetToConnected();
          }
          state = selectWalletChoice;
        }
        break;
      case CREATE_WALLET_PREPARATION:
        state = CREATE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case CREATE_WALLET_SELECT_BACKUP_LOCATION:
        state = CREATE_WALLET_SEED_PHRASE;
        break;
      case CREATE_WALLET_SEED_PHRASE:
        state = CREATE_WALLET_CONFIRM_SEED_PHRASE;
        // Fail safe to ensure that the generator hasn't gone screwy
        Preconditions.checkState(
            SeedPhraseSize.isValid(getCreateWalletSeedPhrase().size()),
            "'actualSeedPhrase' is not a valid length");
        break;
      case CREATE_WALLET_CONFIRM_SEED_PHRASE:
        state = CREATE_WALLET_CREATE_PASSWORD;
        break;
      case CREATE_WALLET_CREATE_PASSWORD:
        state = CREATE_WALLET_REPORT;
        break;
      case CREATE_WALLET_REPORT:
        throw new IllegalStateException("'Next' is not permitted here");
      case TREZOR_CREATE_WALLET_PREPARATION:
        state = TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION;
        break;
      case TREZOR_CREATE_WALLET_SELECT_BACKUP_LOCATION:
        state = TREZOR_CREATE_WALLET_ENTER_DETAILS;
        break;
      case TREZOR_CREATE_WALLET_ENTER_DETAILS:
        state = TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET;
        break;
      case TREZOR_CREATE_WALLET_REQUEST_CREATE_WALLET:
        state = TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET;
        break;
      case TREZOR_CREATE_WALLET_CONFIRM_CREATE_WALLET:
        state = TREZOR_CREATE_WALLET_CONFIRM_ENTROPY;
        break;
      case TREZOR_CREATE_WALLET_CONFIRM_ENTROPY:
        state = TREZOR_CREATE_WALLET_ENTER_NEW_PIN;
        break;
      case TREZOR_CREATE_WALLET_ENTER_NEW_PIN:
        state = TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN;
        break;
      case TREZOR_CREATE_WALLET_CONFIRM_NEW_PIN:
        state = TREZOR_CREATE_WALLET_CONFIRM_WORD;
        break;
      case TREZOR_CREATE_WALLET_CONFIRM_WORD:
        state = TREZOR_CREATE_WALLET_REPORT;
        break;
      case TREZOR_CREATE_WALLET_REPORT:
        break;
      case RESTORE_PASSWORD_SEED_PHRASE:
        state = RESTORE_PASSWORD_REPORT;
        break;
      case RESTORE_PASSWORD_REPORT:
        break;
      case RESTORE_WALLET_SEED_PHRASE:
        if (!isLocalZipBackupPresent()) {
          restoreMethod = RESTORE_WALLET_SELECT_BACKUP_LOCATION;
        } else {
          restoreMethod = RESTORE_WALLET_SELECT_BACKUP;
        }
        state = restoreMethod;
        break;
      case RESTORE_WALLET_SELECT_BACKUP_LOCATION:
        if (isCloudBackupPresent()) {
          restoreMethod = RESTORE_WALLET_SELECT_BACKUP;
        } else {
          restoreMethod = RESTORE_WALLET_TIMESTAMP;
        }
        state = restoreMethod;
        break;
      case RESTORE_WALLET_SELECT_BACKUP:
        state = RESTORE_WALLET_REPORT;
        break;
      case RESTORE_WALLET_TIMESTAMP:
        state = RESTORE_WALLET_REPORT;
        break;
      case RESTORE_WALLET_REPORT:
        throw new IllegalStateException("'Next' is not permitted here");
      default:
        throw new IllegalStateException("Unknown state: " + state.name());
    }
  }