/*
 * Copyright (C) 2014 Murray Cumming
 *
 * This file is part of android-galaxyzoo.
 *
 * android-galaxyzoo is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 *
 * android-galaxyzoo is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with android-galaxyzoo.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.murrayc.galaxyzoo.app;

import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.Context;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;

import com.murrayc.galaxyzoo.app.provider.Item;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

/**
 *
 */
public final class Utils {

    /**
     * For use, for instance with String.toLowerCase()
     */
    public static final String STRING_LANGUAGE = "en";

    /**
     * For use when parsing and building raw binary data:
     */
    public static final String STRING_ENCODING = "UTF-8";
    private static final String ASSET_PATH_DECISION_TREE_DIR = "decision_tree/";
    private static final String JSON_FILE_EXTENSION = ".json";

    /**
     * Ideally you would use LoginUtils.getUseWifiOnly() instead of the copy that is in
     * the SharedPreferences, but this is useful when you don't want to call from the main thread.
     *
     * @param context
     * @return
     */
    public static boolean getUseWifiOnlyFromSharedPrefs(final Context context) {
        //We use the SharedPreferences rather than the copy of our prefs in the Account,
        //to avoid using the Account in the main thread.
        final SharedPreferences prefs = getPreferences(context);

        if (prefs == null) {
            //This should never happen, but there was at least one crash with a backtrace
            //suggesting that it can happen:
            //See https://github.com/murraycu/android-galaxyzoo/issues/16
            Log.error("getShowDiscussQuestionFromSharedPrefs(): prefs is null.");
            return true; //A suitable dfault.
        }

        final String key =  context.getString(R.string.pref_key_wifi_only);
        return prefs.getBoolean(key, false /* default */);
    }

    /**
     * Ideally you would use LoginUtils.getShowDiscussQuestion() instead of the copy that is in
     * the SharedPreferences, but this is useful when you don't want to call from the main thread.
     *
     * @param context
     * @return
     */
    public static boolean getShowDiscussQuestionFromSharedPrefs(final Context context) {
        //We use the SharedPreferences rather than the copy of our prefs in the Account,
        //to avoid using the Account in the main thread.
        final SharedPreferences prefs = getPreferences(context);

        if (prefs == null) {
            //This should never happen, but there was at least one crash with a backtrace
            //suggesting that it can happen:
            //See https://github.com/murraycu/android-galaxyzoo/issues/16
            Log.error("getShowDiscussQuestionFromSharedPrefs(): prefs is null.");
            return true; //A suitable dfault.
        }

        final String key =  context.getString(R.string.pref_key_show_discuss_question);
        return prefs.getBoolean(key, true /* default */);
    }

    static SharedPreferences getPreferences(final Context context) {
        return PreferenceManager.getDefaultSharedPreferences(context);
    }

    @Nullable
    public static File getExternalCacheDir(final Context context) {
        try {
            final File result = context.getExternalCacheDir();
            if (result == null) {
                Log.error("getExternalCacheDir(): null result without an exception from Context.getExternalCacheDir()");

                final String state = android.os.Environment.getExternalStorageState();
                Log.error("  getExternalCacheDir(): getExternalStorageState() returned: " + state);
            }
            return result;
        }  catch (final SecurityException e) {
            Log.error("getExternalCacheDir(): SecurityException from Context.getExternalCacheDir()", e);
            return null;
        } catch (final UnsupportedOperationException e) {
            //This happens while running under ProviderTestCase2.
            //so we just catch it and provide a useful value,
            //so at least the other functionality can be tested.
            //TODO: Find a way to let it succeed.
            if (context.getContentResolver() instanceof MockContentResolver) {
                return new File("testExternalCacheDirForAndroidGalaxyZooWithMockContentResolver");
            }

            Log.error("getExternalCacheDir(): Unsupported operation from Context.getExternalCacheDir()", e);
            return null;
        }
    }

    public static String getDecisionTreeFilepath(final String filename) {
        return ASSET_PATH_DECISION_TREE_DIR + filename;
    }

    public static String getTranslationFilePath(final String language, final String countryCode) {
        if (!TextUtils.isEmpty(countryCode)) {
            return ASSET_PATH_DECISION_TREE_DIR + language + "_" + countryCode + JSON_FILE_EXTENSION;
        } else {
            return ASSET_PATH_DECISION_TREE_DIR + language + JSON_FILE_EXTENSION;
        }
    }

    @NonNull
    public static String getTalkUri(final String zooniverseId) {
        if (zooniverseId == null) {
            return null;
        }

        return Config.TALK_URI + zooniverseId;
    }

    public static final class NetworkConnected {
        public final boolean connected;
        public final boolean notConnectedBecauseNotOnWifi;

        NetworkConnected(final boolean connected, final boolean notConnectedBecauseNotOnWifi) {
            super();
            this.connected = connected;
            this.notConnectedBecauseNotOnWifi = notConnectedBecauseNotOnWifi;
        }
    }

    public static NetworkConnected getNetworkIsConnected(final Context context, final boolean wifiOnly) {
        boolean connected = false;
        final ConnectivityManager connMgr = (ConnectivityManager)
                context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (connMgr == null) {
            //This happens during our test case, probably because the MockContext doesn't support
            //this, so let's ignore it.
            return new NetworkConnected(false, false);
        }

        final NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
        if (networkInfo != null && networkInfo.isConnected()) {
            connected = true;
        }

        if (!connected) {
            return new NetworkConnected(false, false);
        }

        //Consider us not connected if we should use only wi-fi but don't have wi-fi:
        if (wifiOnly && (networkInfo.getType() != ConnectivityManager.TYPE_WIFI)) {
            return new NetworkConnected(false, true);
        }

        return new NetworkConnected(true, false);
    }

    @Nullable
    public static InputStream openAsset(final Context context, final String filename) {
        try {
            return context.getAssets().open(filename);
        } catch (final IOException e) {
            //Don't log this because we expect the file to not exist sometimes,
            //and the caller will just check for a null result to know that.
            return null;
        }
    }

    public static void abandonItem(final Context context, final String itemId) {
        Log.info("abandonItem(): Abandoning item with itemId=" + itemId);

        final Uri itemUri = ContentUris.withAppendedId(
                Item.ITEMS_URI, Integer.parseInt(itemId));

        final ContentResolver resolver = context.getContentResolver();
        final int affected = resolver.delete(itemUri, null, null);
        if (affected != 1) {
            Log.error("abandonItem(): Unexpected number of rows affected: " + affected);
        }
    }

    public static Uri getItemUri(final String itemId) {
        final Uri.Builder uriBuilder = Item.ITEMS_URI.buildUpon();
        uriBuilder.appendPath(itemId);
        return uriBuilder.build();
    }

    static void initDefaultPrefs(final Context context) {
        PreferenceManager.setDefaultValues(context, R.xml.preferences, false);
    }


}