/*
 * 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.systemui;

import android.content.Context;
import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;

import com.android.systemui.util.leak.RotationUtils;

/**
 * Layout class representing the Global Actions menu which appears when the power button is held.
 */
public abstract class MultiListLayout extends LinearLayout {
    protected boolean mHasOutsideTouch;
    protected MultiListAdapter mAdapter;
    protected int mRotation;
    protected RotationListener mRotationListener;

    public MultiListLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        mRotation = RotationUtils.getRotation(context);
    }

    protected abstract ViewGroup getSeparatedView();

    protected abstract ViewGroup getListView();

    /**
     * Sets the divided view, which may have a differently-colored background.
     */
    public abstract void setDivisionView(View v);

    /**
     * Set the view accessibility delegate for the list view container.
     */
    public void setListViewAccessibilityDelegate(View.AccessibilityDelegate delegate) {
        getListView().setAccessibilityDelegate(delegate);
    }

    protected void setSeparatedViewVisibility(boolean visible) {
        ViewGroup separatedView = getSeparatedView();
        if (separatedView != null) {
            separatedView.setVisibility(visible ? View.VISIBLE : View.GONE);
        }
    }

    /**
     * Sets the adapter used to inflate items.
     */
    public void setAdapter(MultiListAdapter adapter) {
        mAdapter = adapter;
    }

    /**
     * Sets this layout to respond to an outside touch listener.
     */
    public void setOutsideTouchListener(OnClickListener onClickListener) {
        mHasOutsideTouch = true;
        requestLayout();
        setOnClickListener(onClickListener);
        setClickable(true);
        setFocusable(true);
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        int newRotation = RotationUtils.getRotation(mContext);
        if (newRotation != mRotation) {
            rotate(mRotation, newRotation);
            mRotation = newRotation;
        }
    }

    protected void rotate(int from, int to) {
        if (mRotationListener != null) {
            mRotationListener.onRotate(from, to);
        }
    }

    /**
     * Update the list of items in both the separated and list views.
     * For this to work, mAdapter must already have been set.
     */
    public void updateList() {
        if (mAdapter == null) {
            throw new IllegalStateException("mAdapter must be set before calling updateList");
        }
        onUpdateList();
    }

    protected void removeAllSeparatedViews() {
        ViewGroup separated = getSeparatedView();
        if (separated != null) {
            separated.removeAllViews();
        }
    }

    protected void removeAllListViews() {
        ViewGroup list = getListView();
        if (list != null) {
            list.removeAllViews();
        }
    }

    protected void removeAllItems() {
        removeAllListViews();
        removeAllSeparatedViews();
    }

    protected void onUpdateList() {
        removeAllItems();
        setSeparatedViewVisibility(mAdapter.hasSeparatedItems());
    }

    public void setRotationListener(RotationListener listener) {
        mRotationListener = listener;
    }

    /**
     * Retrieve the MultiListLayout associated with the given view.
     */
    public static MultiListLayout get(View v) {
        if (v instanceof MultiListLayout) return (MultiListLayout) v;
        if (v.getParent() instanceof View) {
            return get((View) v.getParent());
        }
        return null;
    }

    /**
     * Interface to provide callbacks which trigger when this list detects a rotation.
     */
    public interface RotationListener {
        void onRotate(int from, int to);
    }

    /**
     * Get the X offset in pixels for use when animating the view onto or off of the screen.
     */
    public abstract float getAnimationOffsetX();

    /**
     * Get the Y offset in pixels for use when animating the view onto or off of the screen.
     */
    public abstract float getAnimationOffsetY();

    /**
     * Adapter class for converting items into child views for MultiListLayout and handling
     * callbacks for input events.
     */
    public abstract static class MultiListAdapter extends BaseAdapter {
        /**
         * Counts the number of items to be rendered in the separated view.
         */
        public abstract int countSeparatedItems();

        /**
         * Counts the number of items be rendered in the list view.
         */
        public abstract int countListItems();

        /**
         * Callback to run when an individual item is clicked or pressed.
         * @param position The index of the item which was clicked.
         */
        public abstract void onClickItem(int position);

        /**
         * Callback to run when an individual item is long-clicked or long-pressed.
         * @param position The index of the item which was long-clicked.
         * @return True if the long-click was handled, false otherwise.
         */
        public abstract boolean onLongClickItem(int position);

        /**
         * Determines whether the mAdapter contains any separated items, used to determine whether
         * or not to hide the separated list from view.
         */
        public boolean hasSeparatedItems() {
            return countSeparatedItems() > 0;
        }

        /**
         * Determines whether the item at the given index should be rendered in the separarted view.
         * @param position The index of the item.
         */
        public abstract boolean shouldBeSeparated(int position);
    }
}
