package protect.card_locker;

import android.app.Activity;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.CursorIndexOutOfBoundsException;
import android.database.sqlite.SQLiteDatabase;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.CheckBox;
import android.widget.Toast;

import androidx.activity.OnBackPressedCallback;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.view.ActionMode;
import androidx.appcompat.widget.SearchView;
import androidx.core.splashscreen.SplashScreen;
import androidx.recyclerview.widget.RecyclerView;

import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.tabs.TabLayout;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import protect.card_locker.databinding.ContentMainBinding;
import protect.card_locker.databinding.MainActivityBinding;
import protect.card_locker.databinding.SortingOptionBinding;
import protect.card_locker.preferences.SettingsActivity;

public class MainActivity extends CatimaAppCompatActivity implements LoyaltyCardCursorAdapter.CardAdapterListener {
    private MainActivityBinding binding;
    private ContentMainBinding contentMainBinding;
    private static final String TAG = "Catima";
    public static final String RESTART_ACTIVITY_INTENT = "restart_activity_intent";

    private static final int MEDIUM_SCALE_FACTOR_DIP = 460;

    private SQLiteDatabase mDatabase;
    private LoyaltyCardCursorAdapter mAdapter;
    private ActionMode mCurrentActionMode;
    private SearchView mSearchView;
    private int mLoyaltyCardCount = 0;
    protected String mFilter = "";
    protected Object mGroup = null;
    protected DBHelper.LoyaltyCardOrder mOrder = DBHelper.LoyaltyCardOrder.Alpha;
    protected DBHelper.LoyaltyCardOrderDirection mOrderDirection = DBHelper.LoyaltyCardOrderDirection.Ascending;
    protected int selectedTab = 0;
    private RecyclerView mCardList;
    private View mHelpSection;
    private View mNoMatchingCardsText;
    private View mNoGroupCardsText;
    private TabLayout groupsTabLayout;

    private Runnable mUpdateLoyaltyCardListRunnable;

    private ActivityResultLauncher<Intent> mBarcodeScannerLauncher;
    private ActivityResultLauncher<Intent> mSettingsLauncher;

    private ActionMode.Callback mCurrentActionModeCallback = new ActionMode.Callback() {
        @Override
        public boolean onCreateActionMode(ActionMode inputMode, Menu inputMenu) {
            inputMode.getMenuInflater().inflate(R.menu.card_longclick_menu, inputMenu);
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode inputMode, Menu inputMenu) {
            return false;
        }

        @Override
        public boolean onActionItemClicked(ActionMode inputMode, MenuItem inputItem) {
            if (inputItem.getItemId() == R.id.action_share) {
                final ImportURIHelper importURIHelper = new ImportURIHelper(MainActivity.this);
                try {
                    importURIHelper.startShareIntent(mAdapter.getSelectedItems());
                } catch (UnsupportedEncodingException e) {
                    Toast.makeText(MainActivity.this, R.string.failedGeneratingShareURL, Toast.LENGTH_LONG).show();
                    e.printStackTrace();
                }
                inputMode.finish();
                return true;
            } else if (inputItem.getItemId() == R.id.action_edit) {
                if (mAdapter.getSelectedItemCount() != 1) {
                    throw new IllegalArgumentException("Cannot edit more than 1 card at a time");
                }

                Intent intent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
                Bundle bundle = new Bundle();
                bundle.putInt(LoyaltyCardEditActivity.BUNDLE_ID, mAdapter.getSelectedItems().get(0).id);
                bundle.putBoolean(LoyaltyCardEditActivity.BUNDLE_UPDATE, true);
                intent.putExtras(bundle);
                startActivity(intent);
                inputMode.finish();
                return true;
            } else if (inputItem.getItemId() == R.id.action_delete) {
                AlertDialog.Builder builder = new MaterialAlertDialogBuilder(MainActivity.this);
                // The following may seem weird, but it is necessary to give translators enough flexibility.
                // For example, in Russian, Android's plural quantity "one" actually refers to "any number ending on 1 but not ending in 11".
                // So while in English the extra non-plural form seems unnecessary duplication, it is necessary to give translators enough flexibility.
                // In here, we use the plain string when meaning exactly 1, and otherwise use the plural forms
                if (mAdapter.getSelectedItemCount() == 1) {
                    builder.setTitle(R.string.deleteTitle);
                    builder.setMessage(R.string.deleteConfirmation);
                } else {
                    builder.setTitle(getResources().getQuantityString(R.plurals.deleteCardsTitle, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
                    builder.setMessage(getResources().getQuantityString(R.plurals.deleteCardsConfirmation, mAdapter.getSelectedItemCount(), mAdapter.getSelectedItemCount()));
                }

                builder.setPositiveButton(R.string.confirm, (dialog, which) -> {
                    for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                        Log.d(TAG, "Deleting card: " + loyaltyCard.id);

                        DBHelper.deleteLoyaltyCard(mDatabase, MainActivity.this, loyaltyCard.id);

                        ShortcutHelper.removeShortcut(MainActivity.this, loyaltyCard.id);
                    }

                    TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab);
                    mGroup = tab != null ? tab.getTag() : null;

                    updateLoyaltyCardList(true);

                    dialog.dismiss();
                });
                builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());
                AlertDialog dialog = builder.create();
                dialog.show();

                return true;
            } else if (inputItem.getItemId() == R.id.action_archive) {
                for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                    Log.d(TAG, "Archiving card: " + loyaltyCard.id);
                    DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 1);
                    updateLoyaltyCardList(false);
                    inputMode.finish();
                    invalidateOptionsMenu();
                }
                return true;
            } else if (inputItem.getItemId() == R.id.action_unarchive) {
                for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                    Log.d(TAG, "Unarchiving card: " + loyaltyCard.id);
                    DBHelper.updateLoyaltyCardArchiveStatus(mDatabase, loyaltyCard.id, 0);
                    updateLoyaltyCardList(false);
                    inputMode.finish();
                    invalidateOptionsMenu();
                }
                return true;
            } else if (inputItem.getItemId() == R.id.action_star) {
                for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                    Log.d(TAG, "Starring card: " + loyaltyCard.id);
                    DBHelper.updateLoyaltyCardStarStatus(mDatabase, loyaltyCard.id, 1);
                    updateLoyaltyCardList(false);
                    inputMode.finish();
                }
                return true;
            } else if (inputItem.getItemId() == R.id.action_unstar) {
                for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                    Log.d(TAG, "Unstarring card: " + loyaltyCard.id);
                    DBHelper.updateLoyaltyCardStarStatus(mDatabase, loyaltyCard.id, 0);
                    updateLoyaltyCardList(false);
                    inputMode.finish();
                }
                return true;
            }

            return false;
        }

        @Override
        public void onDestroyActionMode(ActionMode inputMode) {
            mAdapter.clearSelections();
            mCurrentActionMode = null;
        }
    };

    @Override
    protected void onCreate(Bundle inputSavedInstanceState) {
        extractIntentFields(getIntent());
        SplashScreen.installSplashScreen(this);
        super.onCreate(inputSavedInstanceState);

        binding = MainActivityBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        setSupportActionBar(binding.toolbar);
        groupsTabLayout = binding.groups;
        contentMainBinding = ContentMainBinding.bind(binding.include.getRoot());

        mDatabase = new DBHelper(this).getWritableDatabase();

        mUpdateLoyaltyCardListRunnable = () -> {
            updateLoyaltyCardList(false);
        };

        groupsTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                selectedTab = tab.getPosition();
                Log.d("onTabSelected", "Tab Position " + tab.getPosition());
                mGroup = tab.getTag();
                updateLoyaltyCardList(false);
                // Store active tab in Shared Preference to restore next app launch
                SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
                        getString(R.string.sharedpreference_active_tab),
                        Context.MODE_PRIVATE);
                SharedPreferences.Editor activeTabPrefEditor = activeTabPref.edit();
                activeTabPrefEditor.putInt(getString(R.string.sharedpreference_active_tab), tab.getPosition());
                activeTabPrefEditor.apply();
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });

        mHelpSection = contentMainBinding.helpSection;
        mNoMatchingCardsText = contentMainBinding.noMatchingCardsText;
        mNoGroupCardsText = contentMainBinding.noGroupCardsText;
        mCardList = contentMainBinding.list;

        mAdapter = new LoyaltyCardCursorAdapter(this, null, this, mUpdateLoyaltyCardListRunnable);
        mCardList.setAdapter(mAdapter);
        registerForContextMenu(mCardList);

        mGroup = null;
        updateLoyaltyCardList(true);

        /*
         * This was added for Huawei, but Huawei is just too much of a fucking pain.
         * Just leaving this commented out if needed for the future idk
         * https://twitter.com/SylvieLorxu/status/1379437902741012483
         *

        // Show privacy policy on first run
        SharedPreferences privacyPolicyShownPref = getApplicationContext().getSharedPreferences(
                getString(R.string.sharedpreference_privacy_policy_shown),
                Context.MODE_PRIVATE);


        if (privacyPolicyShownPref.getInt(getString(R.string.sharedpreference_privacy_policy_shown), 0) == 0) {
            SharedPreferences.Editor privacyPolicyShownPrefEditor = privacyPolicyShownPref.edit();
            privacyPolicyShownPrefEditor.putInt(getString(R.string.sharedpreference_privacy_policy_shown), 1);
            privacyPolicyShownPrefEditor.apply();

            new AlertDialog.Builder(this)
                    .setTitle(R.string.privacy_policy)
                    .setMessage(R.string.privacy_policy_popup_text)
                    .setPositiveButton(R.string.accept, null)
                    .setNegativeButton(R.string.privacy_policy, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int whichButton) {
                            openPrivacyPolicy();
                        }
                    })
                    .setIcon(android.R.drawable.ic_dialog_info)
                    .show();
        }
         */

        mBarcodeScannerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            // Exit early if the user cancelled the scan (pressed back/home)
            if (result.getResultCode() != RESULT_OK) {
                return;
            }

            Intent intent = result.getData();
            BarcodeValues barcodeValues = Utils.parseSetBarcodeActivityResult(Utils.BARCODE_SCAN, result.getResultCode(), intent, this);

            Bundle inputBundle = intent.getExtras();
            String group = inputBundle != null ? inputBundle.getString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP) : null;
            processBarcodeValues(barcodeValues, group);
        });

        mSettingsLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
            if (result.getResultCode() == Activity.RESULT_OK) {
                Intent intent = result.getData();
                if (intent != null && intent.getBooleanExtra(RESTART_ACTIVITY_INTENT, false)) {
                    recreate();
                }
            }
        });

        getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                if (mSearchView != null && !mSearchView.isIconified()) {
                    mSearchView.setIconified(true);
                } else {
                    finish();
                }
            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();

        if (mCurrentActionMode != null) {
            mAdapter.clearSelections();
            mCurrentActionMode.finish();
        }

        if (mSearchView != null && !mSearchView.isIconified()) {
            mFilter = mSearchView.getQuery().toString();
        }

        // Start of active tab logic
        updateTabGroups(groupsTabLayout);

        // Restore selected tab from Shared Preference
        SharedPreferences activeTabPref = getApplicationContext().getSharedPreferences(
                getString(R.string.sharedpreference_active_tab),
                Context.MODE_PRIVATE);
        selectedTab = activeTabPref.getInt(getString(R.string.sharedpreference_active_tab), 0);

        // Restore sort preferences from Shared Preferences
        // If one of the sorting prefererences has never been set or is set to an invalid value,
        // stick to the defaults.
        SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
                getString(R.string.sharedpreference_sort),
                Context.MODE_PRIVATE);

        String orderString = sortPref.getString(getString(R.string.sharedpreference_sort_order), null);
        String orderDirectionString = sortPref.getString(getString(R.string.sharedpreference_sort_direction), null);

        if (orderString != null && orderDirectionString != null) {
            try {
                mOrder = DBHelper.LoyaltyCardOrder.valueOf(orderString);
                mOrderDirection = DBHelper.LoyaltyCardOrderDirection.valueOf(orderDirectionString);
            } catch (IllegalArgumentException ignored) {
            }
        }

        mGroup = null;

        if (groupsTabLayout.getTabCount() != 0) {
            TabLayout.Tab tab = groupsTabLayout.getTabAt(selectedTab);
            if (tab == null) {
                tab = groupsTabLayout.getTabAt(0);
            }

            groupsTabLayout.selectTab(tab);
            assert tab != null;
            mGroup = tab.getTag();
        } else {
            scaleScreen();
        }

        updateLoyaltyCardList(true);
        // End of active tab logic

        FloatingActionButton addButton = binding.fabAdd;

        addButton.setOnClickListener(v -> {
            Intent intent = new Intent(getApplicationContext(), ScanActivity.class);
            Bundle bundle = new Bundle();
            if (selectedTab != 0) {
                bundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, groupsTabLayout.getTabAt(selectedTab).getText().toString());
            }
            intent.putExtras(bundle);
            mBarcodeScannerLauncher.launch(intent);
        });
        addButton.bringToFront();
    }

    private void displayCardSetupOptions(Menu menu, boolean shouldShow) {
        for (int id : new int[]{R.id.action_search, R.id.action_display_options, R.id.action_sort}) {
            menu.findItem(id).setVisible(shouldShow);
        }
    }

    private void updateLoyaltyCardCount() {
        mLoyaltyCardCount = DBHelper.getLoyaltyCardCount(mDatabase);
    }

    private void updateLoyaltyCardList(boolean updateCount) {
        Group group = null;
        if (mGroup != null) {
            group = (Group) mGroup;
        }

        mAdapter.swapCursor(DBHelper.getLoyaltyCardCursor(mDatabase, mFilter, group, mOrder, mOrderDirection, mAdapter.showingArchivedCards() ? DBHelper.LoyaltyCardArchiveFilter.All : DBHelper.LoyaltyCardArchiveFilter.Unarchived));

        if (updateCount) {
            updateLoyaltyCardCount();
            // Update menu icons if necessary
            invalidateOptionsMenu();
        }

        if (mLoyaltyCardCount > 0) {
            // We want the cardList to be visible regardless of the filtered match count
            // to ensure that the noMatchingCardsText doesn't end up being shown below
            // the keyboard
            mHelpSection.setVisibility(View.GONE);
            mNoGroupCardsText.setVisibility(View.GONE);

            if (mAdapter.getItemCount() > 0) {
                mCardList.setVisibility(View.VISIBLE);
                mNoMatchingCardsText.setVisibility(View.GONE);
            } else {
                mCardList.setVisibility(View.GONE);
                if (!mFilter.isEmpty()) {
                    // Actual Empty Search Result
                    mNoMatchingCardsText.setVisibility(View.VISIBLE);
                    mNoGroupCardsText.setVisibility(View.GONE);
                } else {
                    // Group Tab with no Group Cards
                    mNoMatchingCardsText.setVisibility(View.GONE);
                    mNoGroupCardsText.setVisibility(View.VISIBLE);
                }
            }
        } else {
            mCardList.setVisibility(View.GONE);
            mHelpSection.setVisibility(View.VISIBLE);

            mNoMatchingCardsText.setVisibility(View.GONE);
            mNoGroupCardsText.setVisibility(View.GONE);
        }

        if (mCurrentActionMode != null) {
            mCurrentActionMode.finish();
        }
    }

    private void processBarcodeValues(BarcodeValues barcodeValues, String group) {
        if (barcodeValues.isEmpty()) {
            throw new IllegalArgumentException("barcodesValues may not be empty");
        }

        Intent newIntent = new Intent(getApplicationContext(), LoyaltyCardEditActivity.class);
        Bundle newBundle = new Bundle();
        newBundle.putString(LoyaltyCardEditActivity.BUNDLE_BARCODETYPE, barcodeValues.format());
        newBundle.putString(LoyaltyCardEditActivity.BUNDLE_CARDID, barcodeValues.content());
        if (group != null) {
            newBundle.putString(LoyaltyCardEditActivity.BUNDLE_ADDGROUP, group);
        }
        newIntent.putExtras(newBundle);
        startActivity(newIntent);
    }

    private void onSharedIntent(Intent intent) {
        String receivedAction = intent.getAction();
        String receivedType = intent.getType();

        // Check if an image was shared to us
        if (Intent.ACTION_SEND.equals(receivedAction)) {
            if (!receivedType.startsWith("image/")) {
                Log.e(TAG, "Wrong mime-type");
                return;
            }

            BarcodeValues barcodeValues;
            Bitmap bitmap;

            Uri data = intent.getParcelableExtra(Intent.EXTRA_STREAM);
            if (data == null) {
                Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
                finish();
                return;
            }

            try {
                bitmap = Utils.retrieveImageFromUri(this, data);
            } catch (IOException e) {
                Log.e(TAG, "Error getting data from image file");
                e.printStackTrace();
                Toast.makeText(this, R.string.errorReadingImage, Toast.LENGTH_LONG).show();
                finish();
                return;
            }

            barcodeValues = Utils.getBarcodeFromBitmap(bitmap);

            if (barcodeValues.isEmpty()) {
                Log.i(TAG, "No barcode found in image file");
                Toast.makeText(this, R.string.noBarcodeFound, Toast.LENGTH_LONG).show();
                finish();
                return;
            }

            processBarcodeValues(barcodeValues, null);
        }
    }

    private void extractIntentFields(Intent intent) {
        onSharedIntent(intent);
    }

    public void updateTabGroups(TabLayout groupsTabLayout) {
        List<Group> newGroups = DBHelper.getGroups(mDatabase);

        if (newGroups.size() == 0) {
            groupsTabLayout.removeAllTabs();
            groupsTabLayout.setVisibility(View.GONE);
            return;
        }

        groupsTabLayout.removeAllTabs();

        TabLayout.Tab allTab = groupsTabLayout.newTab();
        allTab.setText(R.string.all);
        allTab.setTag(null);
        groupsTabLayout.addTab(allTab, false);

        for (Group group : newGroups) {
            TabLayout.Tab tab = groupsTabLayout.newTab();
            tab.setText(group._id);
            tab.setTag(group);
            groupsTabLayout.addTab(tab, false);
        }

        groupsTabLayout.setVisibility(View.VISIBLE);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu inputMenu) {
        getMenuInflater().inflate(R.menu.main_menu, inputMenu);

        displayCardSetupOptions(inputMenu, mLoyaltyCardCount > 0);

        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        if (searchManager != null) {
            mSearchView = (SearchView) inputMenu.findItem(R.id.action_search).getActionView();
            mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
            mSearchView.setSubmitButtonEnabled(false);

            mSearchView.setOnCloseListener(() -> {
                invalidateOptionsMenu();
                return false;
            });

            mSearchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
                @Override
                public boolean onQueryTextSubmit(String query) {
                    return false;
                }

                @Override
                public boolean onQueryTextChange(String newText) {
                    mFilter = newText;

                    TabLayout.Tab currentTab = groupsTabLayout.getTabAt(groupsTabLayout.getSelectedTabPosition());
                    mGroup = currentTab != null ? currentTab.getTag() : null;

                    updateLoyaltyCardList(false);

                    return true;
                }
            });
        }

        return super.onCreateOptionsMenu(inputMenu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem inputItem) {
        int id = inputItem.getItemId();

        if (id == android.R.id.home) {
            getOnBackPressedDispatcher().onBackPressed();
        }

        if (id == R.id.action_display_options) {
            mAdapter.showDisplayOptionsDialog();
            invalidateOptionsMenu();

            return true;
        }

        if (id == R.id.action_sort) {
            AtomicInteger currentIndex = new AtomicInteger();
            List<DBHelper.LoyaltyCardOrder> loyaltyCardOrders = Arrays.asList(DBHelper.LoyaltyCardOrder.values());
            for (int i = 0; i < loyaltyCardOrders.size(); i++) {
                if (mOrder == loyaltyCardOrders.get(i)) {
                    currentIndex.set(i);
                    break;
                }
            }

            AlertDialog.Builder builder = new MaterialAlertDialogBuilder(MainActivity.this);
            builder.setTitle(R.string.sort_by);

            SortingOptionBinding sortingOptionBinding = SortingOptionBinding
                    .inflate(LayoutInflater.from(MainActivity.this), null, false);
            final View customLayout = sortingOptionBinding.getRoot();
            builder.setView(customLayout);

            CheckBox showReversed = sortingOptionBinding.checkBoxReverse;


            showReversed.setChecked(mOrderDirection == DBHelper.LoyaltyCardOrderDirection.Descending);


            builder.setSingleChoiceItems(R.array.sort_types_array, currentIndex.get(), (dialog, which) -> currentIndex.set(which));

            builder.setPositiveButton(R.string.sort, (dialog, which) -> {

                setSort(
                        loyaltyCardOrders.get(currentIndex.get()),
                        showReversed.isChecked() ? DBHelper.LoyaltyCardOrderDirection.Descending : DBHelper.LoyaltyCardOrderDirection.Ascending
                );

                dialog.dismiss();
            });

            builder.setNegativeButton(R.string.cancel, (dialog, which) -> dialog.dismiss());

            AlertDialog dialog = builder.create();
            dialog.show();

            return true;
        }

        if (id == R.id.action_manage_groups) {
            Intent i = new Intent(getApplicationContext(), ManageGroupsActivity.class);
            startActivity(i);
            return true;
        }

        if (id == R.id.action_import_export) {
            Intent i = new Intent(getApplicationContext(), ImportExportActivity.class);
            startActivity(i);
            return true;
        }

        if (id == R.id.action_settings) {
            Intent i = new Intent(getApplicationContext(), SettingsActivity.class);
            mSettingsLauncher.launch(i);
            return true;
        }

        if (id == R.id.action_about) {
            Intent i = new Intent(getApplicationContext(), AboutActivity.class);
            startActivity(i);
            return true;
        }


        return super.onOptionsItemSelected(inputItem);
    }

    private void setSort(DBHelper.LoyaltyCardOrder order, DBHelper.LoyaltyCardOrderDirection direction) {
        // Update values
        mOrder = order;
        mOrderDirection = direction;

        // Store in Shared Preference to restore next app launch
        SharedPreferences sortPref = getApplicationContext().getSharedPreferences(
                getString(R.string.sharedpreference_sort),
                Context.MODE_PRIVATE);
        SharedPreferences.Editor sortPrefEditor = sortPref.edit();
        sortPrefEditor.putString(getString(R.string.sharedpreference_sort_order), order.name());
        sortPrefEditor.putString(getString(R.string.sharedpreference_sort_direction), direction.name());
        sortPrefEditor.apply();

        // Update card list
        updateLoyaltyCardList(false);
    }

    @Override
    public void onRowLongClicked(int inputPosition) {
        enableActionMode(inputPosition);
    }

    private void enableActionMode(int inputPosition) {
        if (mCurrentActionMode == null) {
            mCurrentActionMode = startSupportActionMode(mCurrentActionModeCallback);
        }
        toggleSelection(inputPosition);
    }

    private void scaleScreen() {
        DisplayMetrics displayMetrics = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        int screenHeight = displayMetrics.heightPixels;
        float mediumSizePx = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,MEDIUM_SCALE_FACTOR_DIP,getResources().getDisplayMetrics());
        boolean shouldScaleSmaller = screenHeight < mediumSizePx;

        binding.include.welcomeIcon.setVisibility(shouldScaleSmaller ? View.GONE : View.VISIBLE);
    }

    private void toggleSelection(int inputPosition) {
        mAdapter.toggleSelection(inputPosition);
        int count = mAdapter.getSelectedItemCount();

        if (count == 0) {
            mCurrentActionMode.finish();
        } else {
            mCurrentActionMode.setTitle(getResources().getQuantityString(R.plurals.selectedCardCount, count, count));

            MenuItem editItem = mCurrentActionMode.getMenu().findItem(R.id.action_edit);
            MenuItem archiveItem = mCurrentActionMode.getMenu().findItem(R.id.action_archive);
            MenuItem unarchiveItem = mCurrentActionMode.getMenu().findItem(R.id.action_unarchive);
            MenuItem starItem = mCurrentActionMode.getMenu().findItem(R.id.action_star);
            MenuItem unstarItem = mCurrentActionMode.getMenu().findItem(R.id.action_unstar);

            boolean hasStarred = false;
            boolean hasUnstarred = false;
            boolean hasArchived = false;
            boolean hasUnarchived = false;

            for (LoyaltyCard loyaltyCard : mAdapter.getSelectedItems()) {
                if (loyaltyCard.starStatus == 1) {
                    hasStarred = true;
                } else {
                    hasUnstarred = true;
                }

                if (loyaltyCard.archiveStatus == 1) {
                    hasArchived = true;
                } else {
                    hasUnarchived = true;
                }

                // We have all types, no need to keep checking
                if (hasStarred && hasUnstarred && hasArchived && hasUnarchived) {
                    break;
                }
            }

            unarchiveItem.setVisible(hasArchived);
            archiveItem.setVisible(hasUnarchived);

            if (count == 1) {
                starItem.setVisible(!hasStarred);
                unstarItem.setVisible(!hasUnstarred);
                editItem.setVisible(true);
                editItem.setEnabled(true);
            } else {
                starItem.setVisible(hasUnstarred);
                unstarItem.setVisible(hasStarred);

                editItem.setVisible(false);
                editItem.setEnabled(false);
            }

            mCurrentActionMode.invalidate();
        }
    }


    @Override
    public void onRowClicked(int inputPosition) {
        if (mAdapter.getSelectedItemCount() > 0) {
            enableActionMode(inputPosition);
        } else {
            // FIXME
            //
            // There is a really nasty edge case that can happen when someone taps a card but right
            // after it swipes (very small window, hard to reproduce). The cursor gets replaced and
            // may not have a card at the ID number that is returned from onRowClicked.
            //
            // The proper fix, obviously, would involve makes sure an onFling can't happen while a
            // click is being processed. Sadly, I have not yet found a way to make that possible.
            LoyaltyCard loyaltyCard;
            try {
                loyaltyCard = mAdapter.getCard(inputPosition);
            } catch (CursorIndexOutOfBoundsException e) {
                Log.w(TAG, "Prevented crash from tap + swipe on ID " + inputPosition + ": " + e);
                return;
            }

            Intent intent = new Intent(this, LoyaltyCardViewActivity.class);
            intent.setAction("");
            final Bundle b = new Bundle();
            b.putInt("id", loyaltyCard.id);

            ArrayList<Integer> cardList = new ArrayList<>();
            for (int i = 0; i < mAdapter.getItemCount(); i++) {
                cardList.add(mAdapter.getCard(i).id);
            }

            b.putIntegerArrayList("cardList", cardList);
            intent.putExtras(b);

            ShortcutHelper.updateShortcuts(MainActivity.this, loyaltyCard);

            startActivity(intent);
        }
    }
}
