/*
 * Copyright (C) 2017 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.systemui.tuner;

import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.LauncherActivityInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.preference.Preference;
import androidx.preference.PreferenceFragment;
import androidx.preference.SwitchPreference;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;

import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.IntentButtonProvider.IntentButton;
import com.android.systemui.statusbar.ScalingDrawableWrapper;
import com.android.systemui.statusbar.phone.ExpandableIndicator;
import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory;
import com.android.systemui.tuner.ShortcutParser.Shortcut;
import com.android.systemui.tuner.TunerService.Tunable;

import java.util.ArrayList;
import java.util.Map;
import java.util.function.Consumer;

public class LockscreenFragment extends PreferenceFragment {

    private static final String KEY_LEFT = "left";
    private static final String KEY_RIGHT = "right";
    private static final String KEY_CUSTOMIZE = "customize";
    private static final String KEY_SHORTCUT = "shortcut";

    public static final String LOCKSCREEN_LEFT_BUTTON = "sysui_keyguard_left";
    public static final String LOCKSCREEN_LEFT_UNLOCK = "sysui_keyguard_left_unlock";
    public static final String LOCKSCREEN_RIGHT_BUTTON = "sysui_keyguard_right";
    public static final String LOCKSCREEN_RIGHT_UNLOCK = "sysui_keyguard_right_unlock";

    private final ArrayList<Tunable> mTunables = new ArrayList<>();
    private TunerService mTunerService;
    private Handler mHandler;

    @Override
    public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
        mTunerService = Dependency.get(TunerService.class);
        mHandler = new Handler();
        addPreferencesFromResource(R.xml.lockscreen_settings);
        setupGroup(LOCKSCREEN_LEFT_BUTTON, LOCKSCREEN_LEFT_UNLOCK);
        setupGroup(LOCKSCREEN_RIGHT_BUTTON, LOCKSCREEN_RIGHT_UNLOCK);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mTunables.forEach(t -> mTunerService.removeTunable(t));
    }

    private void setupGroup(String buttonSetting, String unlockKey) {
        Preference shortcut = findPreference(buttonSetting);
        SwitchPreference unlock = (SwitchPreference) findPreference(unlockKey);
        addTunable((k, v) -> {
            boolean visible = !TextUtils.isEmpty(v);
            unlock.setVisible(visible);
            setSummary(shortcut, v);
        }, buttonSetting);
    }

    private void showSelectDialog(String buttonSetting) {
        RecyclerView v = (RecyclerView) LayoutInflater.from(getContext())
                .inflate(R.layout.tuner_shortcut_list, null);
        v.setLayoutManager(new LinearLayoutManager(getContext()));
        AlertDialog dialog = new Builder(getContext())
                .setView(v)
                .show();
        Adapter adapter = new Adapter(getContext(), item -> {
            mTunerService.setValue(buttonSetting, item.getSettingValue());
            dialog.dismiss();
        });

        v.setAdapter(adapter);
    }

    private void setSummary(Preference shortcut, String value) {
        if (value == null) {
            shortcut.setSummary(R.string.lockscreen_none);
            return;
        }
        if (value.contains("::")) {
            Shortcut info = getShortcutInfo(getContext(), value);
            shortcut.setSummary(info != null ? info.label : null);
        } else if (value.contains("/")) {
            ActivityInfo info = getActivityinfo(getContext(), value);
            shortcut.setSummary(info != null ? info.loadLabel(getContext().getPackageManager())
                    : null);
        } else {
            shortcut.setSummary(R.string.lockscreen_none);
        }
    }

    private void addTunable(Tunable t, String... keys) {
        mTunables.add(t);
        mTunerService.addTunable(t, keys);
    }

    public static ActivityInfo getActivityinfo(Context context, String value) {
        ComponentName component = ComponentName.unflattenFromString(value);
        try {
            return context.getPackageManager().getActivityInfo(component, 0);
        } catch (NameNotFoundException e) {
            return null;
        }
    }

    public static Shortcut getShortcutInfo(Context context, String value) {
        return Shortcut.create(context, value);
    }

    public static class Holder extends ViewHolder {
        public final ImageView icon;
        public final TextView title;
        public final ExpandableIndicator expand;

        public Holder(View itemView) {
            super(itemView);
            icon = (ImageView) itemView.findViewById(android.R.id.icon);
            title = (TextView) itemView.findViewById(android.R.id.title);
            expand = (ExpandableIndicator) itemView.findViewById(R.id.expand);
        }
    }

    private static class StaticShortcut extends Item {

        private final Context mContext;
        private final Shortcut mShortcut;


        public StaticShortcut(Context context, Shortcut shortcut) {
            mContext = context;
            mShortcut = shortcut;
        }

        @Override
        public Drawable getDrawable() {
            return mShortcut.icon.loadDrawable(mContext);
        }

        @Override
        public String getLabel() {
            return mShortcut.label;
        }

        @Override
        public String getSettingValue() {
            return mShortcut.toString();
        }

        @Override
        public Boolean getExpando() {
            return null;
        }
    }

    private static class App extends Item {

        private final Context mContext;
        private final LauncherActivityInfo mInfo;
        private final ArrayList<Item> mChildren = new ArrayList<>();
        private boolean mExpanded;

        public App(Context context, LauncherActivityInfo info) {
            mContext = context;
            mInfo = info;
            mExpanded = false;
        }

        public void addChild(Item child) {
            mChildren.add(child);
        }

        @Override
        public Drawable getDrawable() {
            return mInfo.getBadgedIcon(mContext.getResources().getConfiguration().densityDpi);
        }

        @Override
        public String getLabel() {
            return mInfo.getLabel().toString();
        }

        @Override
        public String getSettingValue() {
            return mInfo.getComponentName().flattenToString();
        }

        @Override
        public Boolean getExpando() {
            return mChildren.size() != 0 ? mExpanded : null;
        }

        @Override
        public void toggleExpando(Adapter adapter) {
            mExpanded = !mExpanded;
            if (mExpanded) {
                mChildren.forEach(child -> adapter.addItem(this, child));
            } else {
                mChildren.forEach(child -> adapter.remItem(child));
            }
        }
    }

    private abstract static class Item {
        public abstract Drawable getDrawable();

        public abstract String getLabel();

        public abstract String getSettingValue();

        public abstract Boolean getExpando();

        public void toggleExpando(Adapter adapter) {
        }
    }

    public static class Adapter extends RecyclerView.Adapter<Holder> {
        private ArrayList<Item> mItems = new ArrayList<>();
        private final Context mContext;
        private final Consumer<Item> mCallback;

        public Adapter(Context context, Consumer<Item> callback) {
            mContext = context;
            mCallback = callback;
        }

        @Override
        public Holder onCreateViewHolder(ViewGroup parent, int viewType) {
            return new Holder(LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.tuner_shortcut_item, parent, false));
        }

        @Override
        public void onBindViewHolder(Holder holder, int position) {
            Item item = mItems.get(position);
            holder.icon.setImageDrawable(item.getDrawable());
            holder.title.setText(item.getLabel());
            holder.itemView.setOnClickListener(
                    v -> mCallback.accept(mItems.get(holder.getAdapterPosition())));
            Boolean expando = item.getExpando();
            if (expando != null) {
                holder.expand.setVisibility(View.VISIBLE);
                holder.expand.setExpanded(expando);
                holder.expand.setOnClickListener(
                        v -> mItems.get(holder.getAdapterPosition()).toggleExpando(Adapter.this));
            } else {
                holder.expand.setVisibility(View.GONE);
            }
        }

        @Override
        public int getItemCount() {
            return mItems.size();
        }

        public void addItem(Item item) {
            mItems.add(item);
            notifyDataSetChanged();
        }

        public void remItem(Item item) {
            int index = mItems.indexOf(item);
            mItems.remove(item);
            notifyItemRemoved(index);
        }

        public void addItem(Item parent, Item child) {
            int index = mItems.indexOf(parent);
            mItems.add(index + 1, child);
            notifyItemInserted(index + 1);
        }
    }

    public static class LockButtonFactory implements TunerFactory<IntentButton> {

        private final String mKey;
        private final Context mContext;

        public LockButtonFactory(Context context, String key) {
            mContext = context;
            mKey = key;
        }

        @Override
        public String[] keys() {
            return new String[]{mKey};
        }

        @Override
        public IntentButton create(Map<String, String> settings) {
            String buttonStr = settings.get(mKey);
            if (!TextUtils.isEmpty(buttonStr)) {
                if (buttonStr.contains("::")) {
                    Shortcut shortcut = getShortcutInfo(mContext, buttonStr);
                    if (shortcut != null) {
                        return new ShortcutButton(mContext, shortcut);
                    }
                } else if (buttonStr.contains("/")) {
                    ActivityInfo info = getActivityinfo(mContext, buttonStr);
                    if (info != null) {
                        return new ActivityButton(mContext, info);
                    }
                }
            }
            return null;
        }
    }

    private static class ShortcutButton implements IntentButton {
        private final Shortcut mShortcut;
        private final IconState mIconState;

        public ShortcutButton(Context context, Shortcut shortcut) {
            mShortcut = shortcut;
            mIconState = new IconState();
            mIconState.isVisible = true;
            mIconState.drawable = shortcut.icon.loadDrawable(context).mutate();
            mIconState.contentDescription = mShortcut.label;
            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32,
                    context.getResources().getDisplayMetrics());
            mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable,
                    size / (float) mIconState.drawable.getIntrinsicWidth());
            mIconState.tint = false;
        }

        @Override
        public IconState getIcon() {
            return mIconState;
        }

        @Override
        public Intent getIntent() {
            return mShortcut.intent;
        }
    }

    private static class ActivityButton implements IntentButton {
        private final Intent mIntent;
        private final IconState mIconState;

        public ActivityButton(Context context, ActivityInfo info) {
            mIntent = new Intent().setComponent(new ComponentName(info.packageName, info.name));
            mIconState = new IconState();
            mIconState.isVisible = true;
            mIconState.drawable = info.loadIcon(context.getPackageManager()).mutate();
            mIconState.contentDescription = info.loadLabel(context.getPackageManager());
            int size = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 32,
                    context.getResources().getDisplayMetrics());
            mIconState.drawable = new ScalingDrawableWrapper(mIconState.drawable,
                    size / (float) mIconState.drawable.getIntrinsicWidth());
            mIconState.tint = false;
        }

        @Override
        public IconState getIcon() {
            return mIconState;
        }

        @Override
        public Intent getIntent() {
            return mIntent;
        }
    }
}
