/*
 * Copyright (C) 2019 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.customization.picker.theme;

import android.app.AlertDialog.Builder;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import com.android.customization.model.CustomizationManager.Callback;
import com.android.customization.model.theme.DefaultThemeProvider;
import com.android.customization.model.theme.OverlayManagerCompat;
import com.android.customization.model.theme.ThemeBundle;
import com.android.customization.model.theme.ThemeBundleProvider;
import com.android.customization.model.theme.ThemeManager;
import com.android.customization.model.theme.custom.ColorOptionsProvider;
import com.android.customization.model.theme.custom.CustomTheme;
import com.android.customization.model.theme.custom.CustomThemeManager;
import com.android.customization.model.theme.custom.FontOptionsProvider;
import com.android.customization.model.theme.custom.IconOptionsProvider;
import com.android.customization.model.theme.custom.ShapeOptionsProvider;
import com.android.customization.model.theme.custom.ThemeComponentOption;
import com.android.customization.model.theme.custom.ThemeComponentOption.ColorOption;
import com.android.customization.model.theme.custom.ThemeComponentOption.FontOption;
import com.android.customization.model.theme.custom.ThemeComponentOption.IconOption;
import com.android.customization.model.theme.custom.ThemeComponentOption.ShapeOption;
import com.android.customization.model.theme.custom.ThemeComponentOptionProvider;
import com.android.customization.module.CustomizationInjector;
import com.android.customization.module.ThemesUserEventLogger;
import com.android.customization.picker.theme.CustomThemeStepFragment.CustomThemeComponentStepHost;
import com.android.wallpaper.R;
import com.android.wallpaper.module.InjectorProvider;

import org.json.JSONException;

import java.util.ArrayList;
import java.util.List;

public class CustomThemeActivity extends FragmentActivity implements
        CustomThemeComponentStepHost {
    public static final String EXTRA_THEME_ID = "CustomThemeActivity.ThemeId";
    public static final String EXTRA_THEME_TITLE = "CustomThemeActivity.ThemeTitle";
    public static final String EXTRA_THEME_PACKAGES = "CustomThemeActivity.ThemePackages";
    public static final int REQUEST_CODE_CUSTOM_THEME = 1;
    public static final int RESULT_THEME_DELETED = 10;
    public static final int RESULT_THEME_APPLIED = 20;

    private static final String TAG = "CustomThemeActivity";
    private static final String KEY_STATE_CURRENT_STEP = "CustomThemeActivity.currentStep";

    private ThemesUserEventLogger mUserEventLogger;
    private List<ComponentStep<?>> mSteps;
    private int mCurrentStep;
    private CustomThemeManager mCustomThemeManager;
    private ThemeManager mThemeManager;
    private TextView mNextButton;
    private TextView mPreviousButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        CustomizationInjector injector = (CustomizationInjector) InjectorProvider.getInjector();
        mUserEventLogger = (ThemesUserEventLogger) injector.getUserEventLogger(this);
        ThemeBundleProvider themeProvider =
                new DefaultThemeProvider(this, injector.getCustomizationPreferences(this));
        Intent intent = getIntent();
        CustomTheme customTheme = null;
        if (intent != null && intent.hasExtra(EXTRA_THEME_PACKAGES)
                && intent.hasExtra(EXTRA_THEME_TITLE) && intent.hasExtra(EXTRA_THEME_ID)) {
            try {
                CustomTheme.Builder themeBuilder = themeProvider.parseCustomTheme(
                        intent.getStringExtra(EXTRA_THEME_PACKAGES));
                if (themeBuilder != null) {
                    themeBuilder.setId(intent.getStringExtra(EXTRA_THEME_ID));
                    themeBuilder.setTitle(intent.getStringExtra(EXTRA_THEME_TITLE));
                    customTheme = themeBuilder.build(this);
                }
            } catch (JSONException e) {
                Log.w(TAG, "Couldn't parse provided custom theme, will override it");
            }
        }

        mThemeManager = injector.getThemeManager(
                new DefaultThemeProvider(this, injector.getCustomizationPreferences(this)),
                this,
                new OverlayManagerCompat(this),
                mUserEventLogger);
        mThemeManager.fetchOptions(null, false);
        mCustomThemeManager = CustomThemeManager.create(customTheme, mThemeManager);
        if (savedInstanceState != null) {
            mCustomThemeManager.readCustomTheme(themeProvider, savedInstanceState);
        }

        int currentStep = 0;
        if (savedInstanceState != null) {
            currentStep = savedInstanceState.getInt(KEY_STATE_CURRENT_STEP);
        }
        initSteps(currentStep);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_custom_theme);
        mNextButton = findViewById(R.id.next_button);
        mNextButton.setOnClickListener(view -> onNextOrApply());
        mPreviousButton = findViewById(R.id.previous_button);
        mPreviousButton.setOnClickListener(view -> onBackPressed());

        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);
        if (fragment == null) {
            // Navigate to the first step
            navigateToStep(0);
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt(KEY_STATE_CURRENT_STEP, mCurrentStep);
        if (mCustomThemeManager != null) {
            mCustomThemeManager.saveCustomTheme(this, outState);
        }
    }

    private void navigateToStep(int i) {
        FragmentManager fragmentManager = getSupportFragmentManager();
        ComponentStep step = mSteps.get(i);
        Fragment fragment = step.getFragment(mCustomThemeManager.getOriginalTheme().getTitle());

        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.fragment_container, fragment);
        // Don't add step 0 to the back stack so that going back from it just finishes the Activity
        if (i > 0) {
            fragmentTransaction.addToBackStack("Step " + i);
        }
        fragmentTransaction.commit();
        fragmentManager.executePendingTransactions();
        updateNavigationButtonLabels();
    }

    private void initSteps(int currentStep) {
        mSteps = new ArrayList<>();
        OverlayManagerCompat manager = new OverlayManagerCompat(this);
        mSteps.add(new FontStep(new FontOptionsProvider(this, manager), 0));
        mSteps.add(new IconStep(new IconOptionsProvider(this, manager), 1));
        mSteps.add(new ColorStep(new ColorOptionsProvider(this, manager, mCustomThemeManager), 2));
        mSteps.add(new ShapeStep(new ShapeOptionsProvider(this, manager), 3));
        mSteps.add(new NameStep(4));
        mCurrentStep = currentStep;
    }

    private void onNextOrApply() {
        CustomThemeStepFragment stepFragment = getCurrentStepFragment();
        if (stepFragment instanceof CustomThemeComponentFragment) {
            CustomThemeComponentFragment fragment = (CustomThemeComponentFragment) stepFragment;
            mCustomThemeManager.apply(fragment.getSelectedOption(), new Callback() {
                @Override
                public void onSuccess() {
                    navigateToStep(mCurrentStep + 1);
                }

                @Override
                public void onError(@Nullable Throwable throwable) {
                    Log.w(TAG, "Error applying custom theme component", throwable);
                    Toast.makeText(CustomThemeActivity.this, R.string.apply_theme_error_msg,
                            Toast.LENGTH_LONG).show();
                }
            });
        } else if (stepFragment instanceof CustomThemeNameFragment) {
            CustomThemeNameFragment fragment = (CustomThemeNameFragment) stepFragment;
            CustomTheme originalTheme = mCustomThemeManager.getOriginalTheme();

            // We're on the last step, apply theme and leave
            CustomTheme themeToApply = mCustomThemeManager.buildPartialCustomTheme(this,
                    originalTheme.getId(), fragment.getThemeName());

            // If the current theme is equal to the original theme being edited, then
            // don't search for an equivalent, let the user apply the same one by keeping
            // it null.
            ThemeBundle equivalent = (originalTheme.isEquivalent(themeToApply))
                    ? null : mThemeManager.findThemeByPackages(themeToApply);

            if (equivalent != null) {
                Builder builder =
                        new Builder(CustomThemeActivity.this);
                builder.setTitle(getString(R.string.use_style_instead_title,
                        equivalent.getTitle()))
                        .setMessage(getString(R.string.use_style_instead_body,
                                equivalent.getTitle()))
                        .setPositiveButton(getString(R.string.use_style_button,
                                equivalent.getTitle()),
                                (dialogInterface, i) -> applyTheme(equivalent))
                        .setNegativeButton(R.string.no_thanks, null)
                        .create()
                        .show();
            } else {
                applyTheme(themeToApply);
            }
        } else {
            throw new IllegalStateException("Unknown CustomThemeStepFragment");
        }
    }

    private void applyTheme(ThemeBundle themeToApply) {
        mThemeManager.apply(themeToApply, new Callback() {
            @Override
            public void onSuccess() {
                overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
                Toast.makeText(getApplicationContext(), R.string.applied_theme_msg,
                        Toast.LENGTH_LONG).show();
                setResult(RESULT_THEME_APPLIED);
                finish();
            }

            @Override
            public void onError(@Nullable Throwable throwable) {
                Log.w(TAG, "Error applying custom theme", throwable);
                Toast.makeText(CustomThemeActivity.this,
                        R.string.apply_theme_error_msg,
                        Toast.LENGTH_LONG).show();
            }
        });
    }

    private CustomThemeStepFragment getCurrentStepFragment() {
        return (CustomThemeStepFragment)
                getSupportFragmentManager().findFragmentById(R.id.fragment_container);
    }

    @Override
    public void setCurrentStep(int i) {
        mCurrentStep = i;
        updateNavigationButtonLabels();
    }

    private void updateNavigationButtonLabels() {
        mPreviousButton.setVisibility(mCurrentStep == 0 ? View.INVISIBLE : View.VISIBLE);
        mNextButton.setText((mCurrentStep < mSteps.size() -1) ? R.string.custom_theme_next
                : R.string.apply_btn);
    }

    @Override
    public void delete() {
        mThemeManager.removeCustomTheme(mCustomThemeManager.getOriginalTheme());
        setResult(RESULT_THEME_DELETED);
        finish();
    }

    @Override
    public void cancel() {
        finish();
    }

    @Override
    public ThemeComponentOptionProvider<? extends ThemeComponentOption> getComponentOptionProvider(
            int position) {
        return mSteps.get(position).provider;
    }

    @Override
    public CustomThemeManager getCustomThemeManager() {
        return mCustomThemeManager;
    }

    /**
     * Represents a step in selecting a custom theme, picking a particular component (eg font,
     * color, shape, etc).
     * Each step has a Fragment instance associated that instances of this class will provide.
     */
    private static abstract class ComponentStep<T extends ThemeComponentOption> {
        @StringRes final int titleResId;
        final ThemeComponentOptionProvider<T> provider;
        final int position;
        private CustomThemeStepFragment mFragment;

        protected ComponentStep(@StringRes int titleResId, ThemeComponentOptionProvider<T> provider,
                                int position) {
            this.titleResId = titleResId;
            this.provider = provider;
            this.position = position;
        }

        CustomThemeStepFragment getFragment(String title) {
            if (mFragment == null) {
                mFragment = createFragment(title);
            }
            return mFragment;
        }

        /**
         * @return a newly created fragment that will handle this step's UI.
         */
        abstract CustomThemeStepFragment createFragment(String title);
    }

    private class FontStep extends ComponentStep<FontOption> {

        protected FontStep(ThemeComponentOptionProvider<FontOption> provider,
                int position) {
            super(R.string.font_component_title, provider, position);
        }

        @Override
        CustomThemeComponentFragment createFragment(String title) {
            return CustomThemeComponentFragment.newInstance(
                    title,
                    position,
                    titleResId);
        }
    }

    private class IconStep extends ComponentStep<IconOption> {

        protected IconStep(ThemeComponentOptionProvider<IconOption> provider,
                int position) {
            super(R.string.icon_component_title, provider, position);
        }

        @Override
        CustomThemeComponentFragment createFragment(String title) {
            return CustomThemeComponentFragment.newInstance(
                    title,
                    position,
                    titleResId);
        }
    }

    private class ColorStep extends ComponentStep<ColorOption> {

        protected ColorStep(ThemeComponentOptionProvider<ColorOption> provider,
                int position) {
            super(R.string.color_component_title, provider, position);
        }

        @Override
        CustomThemeComponentFragment createFragment(String title) {
            return CustomThemeComponentFragment.newInstance(
                    title,
                    position,
                    titleResId);
        }
    }

    private class ShapeStep extends ComponentStep<ShapeOption> {

        protected ShapeStep(ThemeComponentOptionProvider<ShapeOption> provider,
                int position) {
            super(R.string.shape_component_title, provider, position);
        }

        @Override
        CustomThemeComponentFragment createFragment(String title) {
            return CustomThemeComponentFragment.newInstance(
                    title,
                    position,
                    titleResId);
        }
    }

    private class NameStep extends ComponentStep {

        protected NameStep(int position) {
            super(R.string.name_component_title, null, position);
        }

        @Override
        CustomThemeNameFragment createFragment(String title) {
            return CustomThemeNameFragment.newInstance(
                    title,
                    position,
                    titleResId);
        }
    }
}
