/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

package org.mozilla.gecko.sync.repositories.android;

import org.mozilla.gecko.db.BrowserContract.Control;
import org.mozilla.gecko.sync.Logger;
import org.mozilla.gecko.sync.repositories.NoContentProviderException;

import android.content.ContentProviderClient;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;

/**
 * This class provides an interface to Fenec's control content provider, which
 * exposes some of Fennec's internal state, particularly around migrations.
 */
public class FennecControlHelper {
  private static final String LOG_TAG = "FennecControlHelper";

  protected ContentProviderClient providerClient;
  protected final RepoUtils.QueryHelper queryHelper;

  public FennecControlHelper(Context context) throws NoContentProviderException {
    providerClient = acquireContentProvider(context);
    queryHelper = new RepoUtils.QueryHelper(context, Control.CONTENT_URI, LOG_TAG);
  }

  /**
   * Acquire the content provider client.
   * <p>
   * The caller is responsible for releasing the client.
   *
   * @param context The application context.
   * @return The <code>ContentProviderClient</code>. Never null.
   * @throws NoContentProviderException
   */
  public static ContentProviderClient acquireContentProvider(final Context context)
      throws NoContentProviderException {
    final Uri uri = Control.CONTENT_URI;
    final ContentProviderClient client = context.getContentResolver().acquireContentProviderClient(uri);
    if (client == null) {
      throw new NoContentProviderException(uri);
    }
    return client;
  }

  /**
   * After invoking this method, this instance should be discarded.
   */
  public void releaseProviders() {
    try {
      if (providerClient != null) {
        providerClient.release();
      }
    } catch (Exception e) {
    }
    providerClient = null;
  }

  @Override
  protected void finalize() throws Throwable {
    this.releaseProviders();
    super.finalize();
  }

  private static String[] HISTORY_MIGRATION_COLUMNS = new String[] { Control.ENSURE_HISTORY_MIGRATED };
  private static String[] BOOKMARKS_MIGRATION_COLUMNS = new String[] { Control.ENSURE_BOOKMARKS_MIGRATED };

  /**
   * Pass in a unit array. Returns true if the named column is
   * finished migrating; false otherwise.
   *
   * @param columns an array of a single string, which should be one of the
   *                permitted control values.
   * @return true if the named column is finished migrating; false otherwise.
   */
  protected boolean isColumnMigrated(final String[] columns) {
    try {
      final Cursor cursor = queryHelper.safeQuery(providerClient, ".isColumnMigrated(" + columns[0] + ")",
                                                  columns, null, null, null);
      try {
        if (!cursor.moveToFirst()) {
          return false;
        }

        // This is why we require a unit array.
        return cursor.getInt(0) > 0;
      } finally {
        cursor.close();
      }
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Caught exception checking if Fennec has migrated column " + columns[0] + ".", e);
      return false;
    }
  }

  /**
   * @param context the context to use when querying.
   * @param columns an array of a single string, which should be one of the
   *                permitted control values.
   * @return true if the named column is finished migrating; false otherwise.
   */
  protected static boolean isColumnMigrated(Context context, String[] columns) {
    if (context == null) {
      return false;
    }

    try {
      final FennecControlHelper control = new FennecControlHelper(context);
      try {
        return control.isColumnMigrated(columns);
      } finally {
        control.releaseProviders();
      }
    } catch (Exception e) {
      Logger.warn(LOG_TAG, "Caught exception checking if Fennec has migrated column " + columns[0] + ".", e);
      return false;
    }
  }

  public static boolean isHistoryMigrated(Context context) {
    return isColumnMigrated(context, HISTORY_MIGRATION_COLUMNS);
  }

  public static boolean areBookmarksMigrated(Context context) {
    return isColumnMigrated(context, BOOKMARKS_MIGRATION_COLUMNS);
  }
}
