/*
 * App Mount, mount/umount apps that you don't like on Android, without root.
 * Copyright (C) 2017 Matthew Lee
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package io.github.mthli.mount.app;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.ListView;

import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import io.github.mthli.mount.R;
import io.github.mthli.mount.model.PackageRecord;
import io.github.mthli.mount.util.DisplayUtils;
import io.github.mthli.mount.util.ImageUtils;
import io.github.mthli.mount.util.IntentUtils;
import io.github.mthli.mount.util.PolicyUtils;
import io.github.mthli.mount.util.RxUtils;
import io.github.mthli.mount.util.ToastUtils;
import io.github.mthli.mount.widget.PackageSettingLayout;
import io.github.mthli.mount.widget.PolicyHintLayout;
import io.github.mthli.mount.widget.PackageItemAdapter;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;

public class MountActivity extends Activity implements AbsListView.OnScrollListener,
        AdapterView.OnItemClickListener, PackageSettingLayout.PackageSettingLayoutListener {
    private ListView mListView;
    private PolicyHintLayout mHeaderView;
    private PackageItemAdapter mPackageAdapter;
    private List<PackageRecord> mPackageList;

    private AlertDialog mSettingDialog;
    private MenuItem mLoadingItem;
    private MenuItem mSettingsItem;
    private MenuItem mDonateItem;

    private PackageRecord mCurrentRecord;
    private Disposable mFastScrollDisposable;
    private Disposable mFetchPackageRecordDisposable;
    private Disposable mHeaderViewDisposable;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        setupListView();
        setupHeaderView();
    }

    private void setupListView() {
        mListView = (ListView) findViewById(R.id.list);

        mPackageList = new ArrayList<>();
        mPackageAdapter = new PackageItemAdapter(this, mPackageList);
        mListView.setAdapter(mPackageAdapter);
        mListView.setHeaderDividersEnabled(false);

        mListView.setOnScrollListener(this); // not show fast scroll always
        mListView.setOnItemClickListener(this);
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // DO NOTHING
    }

    @Override
    public void onScrollStateChanged(final AbsListView view, int scrollState) {
        RxUtils.disposeSafety(mFastScrollDisposable);
        if (scrollState != SCROLL_STATE_IDLE) {
            view.setFastScrollAlwaysVisible(true);
            return;
        }

        mFastScrollDisposable = Observable.timer(1000L, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Exception {
                        view.setFastScrollAlwaysVisible(false);
                    }
                });
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        dismissSettingDialogSafety();
        if (!PolicyUtils.isDeviceOwnerApp(this)) {
            IntentUtils.showTutorial(this);
            return;
        }

        mCurrentRecord = mPackageList.get(position);
        PackageSettingLayout layout = (PackageSettingLayout) getLayoutInflater()
                .inflate(R.layout.layout_package_setting, null, false);
        layout.setPackageRecord(mCurrentRecord);
        layout.setPackageSettingLayoutListener(this);

        mSettingDialog = new AlertDialog.Builder(this)
                .setCancelable(true)
                .setView(layout)
                .create();
        mSettingDialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialog) {
                mSettingDialog.getWindow().setLayout(DisplayUtils.dp2px(MountActivity.this, 322.0F),
                        WindowManager.LayoutParams.WRAP_CONTENT);
            }
        });
        mSettingDialog.show();
    }

    private void dismissSettingDialogSafety() {
        if (mSettingDialog != null && mSettingDialog.isShowing()) {
            mSettingDialog.dismiss();
        }
    }

    @Override
    public void onMountSwitchChanged(boolean umount) {
        if (umount) {
            mCurrentRecord.umount = true;
            mCurrentRecord.update();
        } else {
            mCurrentRecord.umount = false;
            mCurrentRecord.delete();
        }

        mPackageAdapter.notifyDataSetChanged();
        PolicyUtils.setApplicationMount(this, mCurrentRecord.name, !umount);
        ToastUtils.showWithShortTime(this, getString(umount ? R.string.toast_umount
                : R.string.toast_mount, mCurrentRecord.label));
    }

    @Override
    public void onClickRunView() {
        dismissSettingDialogSafety();
        IntentUtils.runApplication(this, mCurrentRecord.name);
    }

    @Override
    public void onClickInfoView() {
        dismissSettingDialogSafety();
        IntentUtils.showApplicationInfo(this, mCurrentRecord.name);
    }

    @Override
    public void onClickAddToLauncher() {
        dismissSettingDialogSafety();

        Bitmap icon = ImageUtils.bytes2Bitmap(mCurrentRecord.icon);
        float width = icon.getWidth();
        float height = icon.getHeight();
        float max = width > height ? width : height;
        if (max > ImageUtils.LAUNCHER_ICON_MAX_SIZE) {
            float ratio = ImageUtils.LAUNCHER_ICON_MAX_SIZE / max;
            icon = ImageUtils.resize(icon, (int) (width * ratio), (int) (height * ratio));
        }

        IntentUtils.createShortcut(this, mCurrentRecord.name, icon, mCurrentRecord.label);
        ToastUtils.showWithShortTime(this, getString(R.string.toast_add_to_launcher, mCurrentRecord.label));
    }

    private void setupHeaderView() {
        mHeaderView = (PolicyHintLayout) getLayoutInflater().inflate(R.layout.layout_device_hint,
                mListView, false);
        if (!PolicyUtils.isDeviceOwnerApp(this)) {
            mListView.addHeaderView(mHeaderView);
            hideOptions();
        }

        RxUtils.disposeSafety(mHeaderViewDisposable);
        mHeaderViewDisposable = Observable.interval(200L, TimeUnit.MILLISECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) throws Exception {
                        if (!PolicyUtils.isDeviceOwnerApp(MountActivity.this)) {
                            hideOptions();
                            if (mListView.getHeaderViewsCount() <= 0){
                                mListView.addHeaderView(mHeaderView);
                            }
                        } else {
                            showOptions();
                            if (mListView.getHeaderViewsCount() > 0) {
                                mListView.removeHeaderView(mHeaderView);
                            }
                        }
                    }
                });
    }

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

        mLoadingItem = menu.findItem(R.id.loading);
        mSettingsItem = menu.findItem(R.id.settings);
        mDonateItem = menu.findItem(R.id.donate);

        showLoading();
        hideOptions();

        return true;
    }

    private void showLoading() {
        if (mLoadingItem != null) {
            mLoadingItem.setVisible(true);
        }
    }

    public void hideLoading() {
        if (mLoadingItem != null) {
            mLoadingItem.setVisible(false);
        }
    }

    private void showOptions() {
        if (mSettingsItem != null) {
            mSettingsItem.setVisible(true);
        }

        if (mDonateItem != null) {
            mDonateItem.setVisible(true);
        }
    }

    private void hideOptions() {
        if (mSettingsItem != null) {
            mSettingsItem.setVisible(false);
        }

        if (mDonateItem != null) {
            mDonateItem.setVisible(false);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.settings:
                IntentUtils.startSettingsActivity(this);
                break;
            case R.id.donate:
                showDonateDialog();
                break;
            default:
                break;
        }

        return true;
    }

    private void showDonateDialog() {
        final AlertDialog dialog = new AlertDialog.Builder(this)
                .setCancelable(true)
                .setTitle(R.string.donate_dialog_title)
                .setMessage(R.string.alipay_account)
                .setPositiveButton(R.string.donate_dialog_positive, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ClipboardManager manager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
                        ClipData data = ClipData.newPlainText("", getString(R.string.alipay_account));
                        manager.setPrimaryClip(data);
                        ToastUtils.showWithShortTime(MountActivity.this, R.string.toast_copied);
                    }
                })
                .create();
        dialog.setOnShowListener(new DialogInterface.OnShowListener() {
            @Override
            public void onShow(DialogInterface dialogInterface) {
                dialog.getWindow().setLayout(DisplayUtils.dp2px(MountActivity.this, 322.0F),
                        WindowManager.LayoutParams.WRAP_CONTENT);
            }
        });
        dialog.show();
    }

    @Override
    public void onResume() {
        super.onResume();
        fetchPackageRecord();
    }

    private void fetchPackageRecord() {
        RxUtils.disposeSafety(mFetchPackageRecordDisposable);
        showLoading();

        mFetchPackageRecordDisposable = Observable.create(
                new ObservableOnSubscribe<List<PackageRecord>>() {
                    @Override
                    public void subscribe(ObservableEmitter<List<PackageRecord>> e) throws Exception {
                        // list all umount apps first
                        List<PackageRecord> recordList = PackageRecord.listAll(PackageRecord.class);
                        Set<String> nameSet = new HashSet<>();
                        for (PackageRecord record : recordList) {
                            nameSet.add(record.name);
                        }

                        // query all apps can launched from launcher
                        Intent intent = new Intent(Intent.ACTION_MAIN);
                        intent.addCategory(Intent.CATEGORY_LAUNCHER);
                        List<ResolveInfo> resolveInfoList = getPackageManager().queryIntentActivities(intent, 0);

                        // get all apps' application info
                        List<ApplicationInfo> applicationInfoList = new ArrayList<>();
                        for (ResolveInfo info : resolveInfoList) {
                            applicationInfoList.add(info.activityInfo.applicationInfo);
                        }

                        // filter we don't want
                        for (ApplicationInfo info : applicationInfoList) {
                            if (TextUtils.equals(info.packageName, getPackageName())
                                    || nameSet.contains(info.packageName)) {
                                continue;
                            }

                            nameSet.add(info.packageName);
                            recordList.add(new PackageRecord(info.packageName,
                                    ImageUtils.drawable2Bytes(info.loadIcon(getPackageManager())),
                                    info.loadLabel(getPackageManager()).toString(),
                                    getPackageManager().getPackageInfo(info.packageName, 0).versionName,
                                    false));
                        }

                        // sort by language
                        Collections.sort(recordList, new Comparator<PackageRecord>() {
                            @Override
                            public int compare(PackageRecord record1, PackageRecord record2) {
                                return Collator.getInstance().compare(record1.label, record2.label);
                            }
                        });

                        e.onNext(recordList);
                        e.onComplete();
                    }
                })
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<List<PackageRecord>>() {
                    @Override
                    public void accept(List<PackageRecord> list) throws Exception {
                        mPackageList.clear();
                        mPackageList.addAll(list);
                        mPackageAdapter.notifyDataSetChanged();
                        hideLoading();
                    }
                });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        RxUtils.disposeSafety(mFastScrollDisposable);
        RxUtils.disposeSafety(mFetchPackageRecordDisposable);
        RxUtils.disposeSafety(mHeaderViewDisposable);
    }
}