/**
   * This generates the SQLite commands to create the DB
   *
   * @param database
   */
  private void executeCreations(final SQLiteDatabase database) {

    TransactionManager.transact(
        database,
        new Runnable() {
          @Override
          public void run() {

            List<ModelAdapter> modelAdapters = databaseDefinition.getModelAdapters();
            for (ModelAdapter modelAdapter : modelAdapters) {
              database.execSQL(modelAdapter.getCreationQuery());
            }

            // create our model views
            List<ModelViewAdapter> modelViews = databaseDefinition.getModelViewAdapters();
            for (ModelViewAdapter modelView : modelViews) {
              QueryBuilder queryBuilder =
                  new QueryBuilder()
                      .append("CREATE VIEW")
                      .appendSpaceSeparated(modelView.getViewName())
                      .append("AS ")
                      .append(modelView.getCreationQuery());
              try {
                database.execSQL(queryBuilder.getQuery());
              } catch (SQLiteException e) {
                FlowLog.logError(e);
              }
            }
          }
        });
  }
  /**
   * Saves the database as a backup on the {@link
   * com.raizlabs.android.dbflow.runtime.DBTransactionQueue} as the highest priority ever. This will
   * create a THIRD database to use as a backup to the backup in case somehow the overwrite fails.
   */
  public void backupDB() {
    if (!databaseDefinition.backupEnabled() || !databaseDefinition.areConsistencyChecksEnabled()) {
      throw new IllegalStateException(
          "Backups are not enabled for : "
              + databaseDefinition.getDatabaseName()
              + ". Please consider adding "
              + "both backupEnabled and consistency checks enabled to the Database annotation");
    }
    // highest priority ever!
    TransactionManager.getInstance()
        .addTransaction(
            new BaseTransaction(DBTransactionInfo.create(BaseTransaction.PRIORITY_UI + 1)) {
              @Override
              public Object onExecute() {

                Context context = FlowManager.getContext();
                File backup = context.getDatabasePath(getTempDbFileName());
                File temp =
                    context.getDatabasePath(
                        TEMP_DB_NAME + "-2-" + databaseDefinition.getDatabaseFileName());

                // if exists we want to delete it before rename
                if (temp.exists()) {
                  temp.delete();
                }

                backup.renameTo(temp);
                if (backup.exists()) {
                  backup.delete();
                }
                File existing = context.getDatabasePath(databaseDefinition.getDatabaseFileName());

                try {
                  backup.getParentFile().mkdirs();
                  writeDB(backup, new FileInputStream(existing));

                  temp.delete();
                } catch (Exception e) {
                  FlowLog.logError(e);
                }
                return null;
              }
            });
  }
  private void executeMigrations(
      final SQLiteDatabase db, final int oldVersion, final int newVersion) {

    // will try migrations file or execute migrations from code
    try {
      final List<String> files =
          Arrays.asList(
              FlowManager.getContext()
                  .getAssets()
                  .list(MIGRATION_PATH + "/" + databaseDefinition.getDatabaseName()));
      Collections.sort(files, new NaturalOrderComparator());

      final Map<Integer, List<String>> migrationFileMap = new HashMap<>();
      for (String file : files) {
        try {
          final Integer version = Integer.valueOf(file.replace(".sql", ""));
          List<String> fileList = migrationFileMap.get(version);
          if (fileList == null) {
            fileList = new ArrayList<>();
            migrationFileMap.put(version, fileList);
          }
          fileList.add(file);
        } catch (NumberFormatException e) {
          FlowLog.log(FlowLog.Level.W, "Skipping invalidly named file: " + file, e);
        }
      }

      final Map<Integer, List<Migration>> migrationMap = databaseDefinition.getMigrations();

      final int curVersion = oldVersion + 1;

      TransactionManager.transact(
          db,
          new Runnable() {
            @Override
            public void run() {

              // execute migrations in order, migration file first before wrapped migration classes.
              for (int i = curVersion; i <= newVersion; i++) {
                List<String> migrationFiles = migrationFileMap.get(i);
                if (migrationFiles != null) {
                  for (String migrationFile : migrationFiles) {
                    executeSqlScript(db, migrationFile);
                    FlowLog.log(FlowLog.Level.I, migrationFile + " executed successfully.");
                  }
                }

                if (migrationMap != null) {
                  List<Migration> migrationsList = migrationMap.get(i);
                  if (migrationsList != null) {
                    for (Migration migration : migrationsList) {
                      // before migration
                      migration.onPreMigrate();

                      // migrate
                      migration.migrate(db);

                      // after migration cleanup
                      migration.onPostMigrate();
                      FlowLog.log(
                          FlowLog.Level.I, migration.getClass() + " executed successfully.");
                    }
                  }
                }
              }
            }
          });
    } catch (IOException e) {
      FlowLog.log(FlowLog.Level.E, "Failed to execute migrations.", e);
    }
  }