/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.tv.settings.device.storage;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.leanback.app.GuidedStepSupportFragment;
import androidx.leanback.widget.GuidanceStylist;
import androidx.leanback.widget.GuidedAction;

import com.android.tv.settings.R;
import com.android.tv.settings.dialog.ProgressDialogFragment;
import com.android.tv.settings.widget.SettingsGuidedStepFragment;

import java.io.File;
import java.util.List;
import java.util.Objects;

public class MigrateStorageActivity extends FragmentActivity {
    private static final String TAG = "MigrateStorageActivity";

    private static final String EXTRA_MIGRATE_HERE =
            "com.android.tv.settings.device.storage.MigrateStorageActivity.MIGRATE_HERE";

    private static final String SAVE_STATE_MOVE_ID = "MigrateStorageActivity.MOVE_ID";

    private VolumeInfo mTargetVolumeInfo;
    private VolumeInfo mVolumeInfo;
    private String mTargetVolumeDesc;
    private String mVolumeDesc;
    private int mMoveId = -1;
    private final Handler mHandler = new Handler();
    private PackageManager mPackageManager;
    private final PackageManager.MoveCallback mMoveCallback = new PackageManager.MoveCallback() {
        @Override
        public void onStatusChanged(int moveId, int status, long estMillis) {
            if (moveId != mMoveId || !PackageManager.isMoveStatusFinished(status)) {
                return;
            }
            if (status == PackageManager.MOVE_SUCCEEDED) {
                showMigrationSuccessToast();
            } else {
                showMigrationFailureToast();
            }
            finish();
        }
    };

    public static Intent getLaunchIntent(Context context, String volumeId,
            boolean migrateHere) {
        final Intent i = new Intent(context, MigrateStorageActivity.class);
        i.putExtra(VolumeInfo.EXTRA_VOLUME_ID, volumeId);
        i.putExtra(EXTRA_MIGRATE_HERE, migrateHere);
        return i;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        final Intent intent = getIntent();
        final String volumeId = intent.getStringExtra(VolumeInfo.EXTRA_VOLUME_ID);
        final StorageManager storageManager = getSystemService(StorageManager.class);

        if (intent.getBooleanExtra(EXTRA_MIGRATE_HERE, true)) {
            mTargetVolumeInfo = storageManager.findVolumeById(volumeId);
            if (mTargetVolumeInfo == null) {
                finish();
                return;
            }
            mTargetVolumeDesc = storageManager.getBestVolumeDescription(mTargetVolumeInfo);
            getSupportFragmentManager().beginTransaction()
                    .add(android.R.id.content,
                            MigrateConfirmationStepFragment.newInstance(mTargetVolumeDesc))
                    .commitAllowingStateLoss();
        } else {
            mVolumeInfo = storageManager.findVolumeById(volumeId);
            if (mVolumeInfo == null) {
                finish();
                return;
            }
            mVolumeDesc = storageManager.getBestVolumeDescription(mVolumeInfo);
            getSupportFragmentManager().beginTransaction()
                    .add(android.R.id.content,
                            ChooseStorageStepFragment.newInstance(mVolumeInfo))
                    .commitAllowingStateLoss();
        }

        mPackageManager = getPackageManager();
        mPackageManager.registerMoveCallback(mMoveCallback, mHandler);
    }

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(SAVE_STATE_MOVE_ID, mMoveId);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        getPackageManager().unregisterMoveCallback(mMoveCallback);
    }

    private void onConfirmCancel() {
        finish();
    }

    private void onConfirmProceed() {
        startMigrationInternal();
    }

    private void onChoose(VolumeInfo volumeInfo) {
        mTargetVolumeInfo = volumeInfo;
        final StorageManager storageManager = getSystemService(StorageManager.class);
        mTargetVolumeDesc = storageManager.getBestVolumeDescription(mTargetVolumeInfo);
        startMigrationInternal();
    }

    private void startMigrationInternal() {
        try {
            mMoveId = mPackageManager.movePrimaryStorage(mTargetVolumeInfo);
            getSupportFragmentManager().beginTransaction()
                    .replace(android.R.id.content,
                            MigrateProgressFragment.newInstance(mTargetVolumeDesc))
                    .commitNowAllowingStateLoss();
        } catch (IllegalArgumentException e) {
            // This will generally happen if there's a move already in progress or completed
            StorageManager sm = (StorageManager) getSystemService(STORAGE_SERVICE);

            if (Objects.equals(mTargetVolumeInfo.getFsUuid(),
                    sm.getPrimaryStorageVolume().getUuid())) {
                // The data is already on the target volume
                showMigrationSuccessToast();
            } else {
                // The data is most likely in the process of being moved
                Log.e(TAG, "Storage migration failure", e);
                showMigrationFailureToast();
            }
            finish();
        } catch (IllegalStateException e) {
            showMigrationFailureToast();
            finish();
        }
    }

    private void showMigrationSuccessToast() {
        Toast.makeText(this,
                getString(R.string.storage_wizard_migrate_toast_success, mTargetVolumeDesc),
                Toast.LENGTH_SHORT).show();
    }

    private void showMigrationFailureToast() {
        Toast.makeText(this,
                getString(R.string.storage_wizard_migrate_toast_failure, mTargetVolumeDesc),
                Toast.LENGTH_SHORT).show();
    }

    public static class MigrateConfirmationStepFragment extends SettingsGuidedStepFragment {
        private static final String ARG_VOLUME_DESC = "volumeDesc";

        private static final int ACTION_CONFIRM = 1;
        private static final int ACTION_LATER = 2;

        public static MigrateConfirmationStepFragment newInstance(String volumeDescription) {
            final MigrateConfirmationStepFragment fragment = new MigrateConfirmationStepFragment();
            final Bundle b = new Bundle(1);
            b.putString(ARG_VOLUME_DESC, volumeDescription);
            fragment.setArguments(b);
            return fragment;
        }

        @NonNull
        @Override
        public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
            final String driveDesc = getArguments().getString(ARG_VOLUME_DESC);
            return new GuidanceStylist.Guidance(
                    getString(R.string.storage_wizard_migrate_confirm_title, driveDesc),
                    getString(R.string.storage_wizard_migrate_confirm_description, driveDesc),
                    null,
                    getActivity().getDrawable(R.drawable.ic_storage_132dp));
        }

        @Override
        public void onCreateActions(@NonNull List<GuidedAction> actions,
                Bundle savedInstanceState) {
            actions.add(new GuidedAction.Builder(getContext())
                    .id(ACTION_CONFIRM)
                    .title(R.string.storage_wizard_migrate_confirm_action_move_now)
                    .build());
            actions.add(new GuidedAction.Builder(getContext())
                    .id(ACTION_LATER)
                    .title(R.string.storage_wizard_migrate_confirm_action_move_later)
                    .build());
        }

        @Override
        public void onGuidedActionClicked(GuidedAction action) {
            final int id = (int) action.getId();
            switch (id) {
                case ACTION_CONFIRM:
                    ((MigrateStorageActivity) getActivity()).onConfirmProceed();
                    break;
                case ACTION_LATER:
                    ((MigrateStorageActivity) getActivity()).onConfirmCancel();
                    break;
            }
        }
    }

    public static class ChooseStorageStepFragment extends SettingsGuidedStepFragment {

        private List<VolumeInfo> mCandidateVolumes;

        public static ChooseStorageStepFragment newInstance(VolumeInfo currentVolumeInfo) {
            Bundle args = new Bundle(1);
            args.putString(VolumeInfo.EXTRA_VOLUME_ID, currentVolumeInfo.getId());

            ChooseStorageStepFragment fragment = new ChooseStorageStepFragment();
            fragment.setArguments(args);
            return fragment;
        }

        @NonNull
        @Override
        public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
            return new GuidanceStylist.Guidance(
                    getString(R.string.storage_wizard_migrate_choose_title),
                    null,
                    null,
                    getActivity().getDrawable(R.drawable.ic_storage_132dp));
        }

        @Override
        public void onCreateActions(@NonNull List<GuidedAction> actions,
                Bundle savedInstanceState) {
            final StorageManager storageManager =
                    getContext().getSystemService(StorageManager.class);
            mCandidateVolumes =
                    getContext().getPackageManager().getPrimaryStorageCandidateVolumes();
            final String volumeId = getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID);
            for (final VolumeInfo candidate : mCandidateVolumes) {
                if (TextUtils.equals(candidate.getId(), volumeId)) {
                    continue;
                }
                final File path = candidate.getPath();
                final String avail = Formatter.formatFileSize(getActivity(), path.getFreeSpace());
                actions.add(new GuidedAction.Builder(getContext())
                        .title(storageManager.getBestVolumeDescription(candidate))
                        .description(getString(
                                R.string.storage_wizard_back_up_apps_space_available, avail))
                        .id(mCandidateVolumes.indexOf(candidate))
                        .build());
            }

        }

        @Override
        public void onGuidedActionClicked(GuidedAction action) {
            final VolumeInfo volumeInfo = mCandidateVolumes.get((int) action.getId());
            ((MigrateStorageActivity)getActivity()).onChoose(volumeInfo);
        }
    }

    public static class MigrateProgressFragment extends ProgressDialogFragment {
        private static final String ARG_VOLUME_DESC = "volumeDesc";

        public static MigrateProgressFragment newInstance(String volumeDescription) {
            final MigrateProgressFragment fragment = new MigrateProgressFragment();
            final Bundle b = new Bundle(1);
            b.putString(ARG_VOLUME_DESC, volumeDescription);
            fragment.setArguments(b);
            return fragment;
        }

        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            setTitle(getActivity().getString(R.string.storage_wizard_migrate_progress_title,
                    getArguments().getString(ARG_VOLUME_DESC)));
            setSummary(getActivity()
                    .getString(R.string.storage_wizard_migrate_progress_description));
        }

    }
}
