package org.torproject.artitoyvpn.ui.appListing;

import android.app.Application;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Pair;

import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;

import org.torproject.artitoyvpn.utils.PreferenceHelper;
import org.torproject.artitoyvpn.vpn.VpnStatusObservable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * View model fom App list fragment. Used AndroidViewModel as context is required.
 */
public class AppListingViewModel extends AndroidViewModel {
    private final MutableLiveData<List<DataItem>> appList = new MutableLiveData<>();
    private final MutableLiveData<Pair<Integer, Integer>> installedAppCount = new MutableLiveData<>();
    private final MutableLiveData<Integer> itemChanged = new MutableLiveData<>();
    private final MutableLiveData<Integer> appUIDSelected = new MutableLiveData<>();
    private final MutableLiveData<Boolean> isLoading = new MutableLiveData<>();
    private final MutableLiveData<Void> showVpnRestartRequiredMessage = new MutableLiveData<>();

    private PreferenceHelper preferenceHelper;

    //We need this to show the restart required snack.
    private boolean isVpnConnected = false;

    public AppListingViewModel(@NonNull Application application) {
        super(application);
        preferenceHelper = new PreferenceHelper(application);
        isLoading.setValue(true);
        //Dirty way to multi-thread!! but, until we finalise something..
        //the app loading is a heavy process if there are lots of apps(my phone has 400+ apps, including all system apps).
        new Thread(() -> {
            List<DataItem> list = new ArrayList<>();
            PackageManager pm = getApplication().getPackageManager();
            List<ApplicationInfo> appInfos = pm.getInstalledApplications(PackageManager.GET_META_DATA);
            Set<String> selectedApps = preferenceHelper.getSelectedApps();
            boolean isAllAppsSelected = true;

            for (int i = 0; i < appInfos.size(); i++) {
                String appName = (String) pm.getApplicationLabel(appInfos.get(i));
                Drawable appIcon = appInfos.get(i).loadIcon(pm);
                int appUID = appInfos.get(i).uid;
                String packageName = appInfos.get(i).packageName;
                boolean isAppSelected = selectedApps != null && selectedApps.contains(packageName);
                list.add(DataItem.createAppItem(appName, appIcon, appUID, packageName, isAppSelected));

                //if one of the app installed is not selected, we mark all app selected checkbox false.
                if (!isAppSelected) {
                    isAllAppsSelected = false;
                }
            }

            int selectedAppsSize = selectedApps != null ? selectedApps.size() : appInfos.size();

            installedAppCount.postValue(new Pair<>(appInfos.size(), selectedAppsSize));
            isLoading.postValue(false);
            Collections.sort(list);
            list.add(0, DataItem.createHeaderItem(isAllAppsSelected));
            appList.postValue(list);
        }).start();
    }

    public LiveData<List<DataItem>> getList() {
        return appList;
    }

    public LiveData<Integer> getItemChanged() {
        return itemChanged;
    }

    public LiveData<Integer> getAppUIDSelected() {
        return appUIDSelected;
    }

    public LiveData<Boolean> isLoading() {
        return isLoading;
    }

    public LiveData<Pair<Integer, Integer>> getAppCount() {
        return installedAppCount;
    }

    public LiveData<Void> getRestartVpnHeadsUp() {
        return showVpnRestartRequiredMessage;
    }

    public void checkboxClicked(int pos, boolean checked) {
        appList.getValue().get(pos).isSelected = checked;
        itemChanged.setValue(pos);
        updateUserSelection();
    }

    public void rowClicked(int pos) {
        appUIDSelected.setValue(appList.getValue().get(pos).appUID);
        appUIDSelected.setValue(-1);
    }

    public void selectAllRowClicked(boolean checked) {
        List<DataItem> allApps = appList.getValue();
        for (int i = 0; i < allApps.size(); i++) {
            allApps.get(i).isSelected = checked;
        }
        appList.setValue(allApps);
        updateUserSelection();
    }


    private void updateUserSelection() {
        List<DataItem> allAppsWithHeader = appList.getValue();
        HashSet<String> allAppsSet = new HashSet<>();
        for (int i = 0; i < allAppsWithHeader.size(); i++) {
            if (allAppsWithHeader.get(i).packageName != null && allAppsWithHeader.get(i).isSelected) {
                allAppsSet.add(allAppsWithHeader.get(i).packageName);
            }
        }

        //we have to subtract header row
        int totalAppsInstalled = allAppsWithHeader.size() - 1;
        installedAppCount.postValue(new Pair<>(totalAppsInstalled, allAppsSet.size()));
        preferenceHelper.setSelectedApps(allAppsSet);

        if (isVpnConnected) {
            showVpnRestartRequiredMessage.setValue(null);
        }
    }


    public void setVpnStatus(VpnStatusObservable.Status status) {
        isVpnConnected = status == VpnStatusObservable.Status.RUNNING;
    }
}


