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

import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.android.messaging.Factory;
import com.android.messaging.R;
import com.android.messaging.util.Assert;

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

public class SnackBar {
    public static final int LONG_DURATION_IN_MS = 5000;
    public static final int SHORT_DURATION_IN_MS = 1000;
    public static final int MAX_DURATION_IN_MS = 10000;

    public interface SnackBarListener {
        void onActionClick();
    }

    /**
     * Defines an action to be performed when the user clicks on the action button on the snack bar
     */
    public static class Action {
        private final Runnable mActionRunnable;
        private final String mActionLabel;

        public final static int SNACK_BAR_UNDO = 0;
        public final static int SNACK_BAR_RETRY = 1;

        private Action(@Nullable Runnable actionRunnable, @Nullable String actionLabel) {
            mActionRunnable = actionRunnable;
            mActionLabel = actionLabel;
        }

        Runnable getActionRunnable() {
            return mActionRunnable;
        }

        String getActionLabel() {
            return mActionLabel;
        }

        public static Action createUndoAction(final Runnable undoRunnable) {
            return createCustomAction(undoRunnable, Factory.get().getApplicationContext()
                    .getString(R.string.snack_bar_undo));
        }

        public static Action createRetryAction(final Runnable retryRunnable) {
            return createCustomAction(retryRunnable, Factory.get().getApplicationContext()
                    .getString(R.string.snack_bar_retry));
        }


        public static Action createCustomAction(final Runnable runnable, final String actionLabel) {
            return new Action(runnable, actionLabel);
        }
    }

    /**
     * Defines the placement of the snack bar (e.g. anchored view, anchor gravity).
     */
    public static class Placement {
        private final View mAnchorView;
        private final boolean mAnchorAbove;

        private Placement(@NonNull final View anchorView, final boolean anchorAbove) {
            Assert.notNull(anchorView);
            mAnchorView = anchorView;
            mAnchorAbove = anchorAbove;
        }

        public View getAnchorView() {
            return mAnchorView;
        }

        public boolean getAnchorAbove() {
            return mAnchorAbove;
        }

        /**
         * Anchor the snack bar above the given {@code anchorView}.
         */
        public static Placement above(final View anchorView) {
            return new Placement(anchorView, true);
        }

        /**
         * Anchor the snack bar below the given {@code anchorView}.
         */
        public static Placement below(final View anchorView) {
            return new Placement(anchorView, false);
        }
    }

    public static class Builder {
        private static final List<SnackBarInteraction> NO_INTERACTIONS = 
            new ArrayList<SnackBarInteraction>();

        private final Context mContext;
        private final SnackBarManager mSnackBarManager;

        private String mSnackBarMessage;
        private int mDuration = LONG_DURATION_IN_MS;
        private List<SnackBarInteraction> mInteractions = NO_INTERACTIONS;
        private Action mAction;
        private Placement mPlacement;
        // The parent view is only used to get a window token and doesn't affect the layout
        private View mParentView;

        public Builder(final SnackBarManager snackBarManager, final View parentView) {
            Assert.notNull(snackBarManager);
            Assert.notNull(parentView);
            mSnackBarManager = snackBarManager;
            mContext = parentView.getContext();
            mParentView = parentView;
        }

        public Builder setText(final String snackBarMessage) {
            Assert.isTrue(!TextUtils.isEmpty(snackBarMessage));
            mSnackBarMessage = snackBarMessage;
            return this;
        }

        public Builder setAction(final Action action) {
            mAction = action;
            return this;
        }

        /**
         * Sets the duration to show this toast for in milliseconds.
         */
        public Builder setDuration(final int duration) {
            Assert.isTrue(0 < duration && duration < MAX_DURATION_IN_MS);
            mDuration = duration;
            return this;
        }

        /**
         * Sets the components that this toast's animation will interact with. These components may
         * be animated to make room for the toast.
         */
        public Builder withInteractions(final List<SnackBarInteraction> interactions) {
            mInteractions = interactions;
            return this;
        }

        /**
         * Place the snack bar with the given placement requirement.
         */
        public Builder withPlacement(final Placement placement) {
            Assert.isNull(mPlacement);
            mPlacement = placement;
            return this;
        }

        public SnackBar build() {
            return new SnackBar(this);
        }

        public void show() {
            mSnackBarManager.show(build());
        }
    }

    private final View mRootView;
    private final Context mContext;
    private final View mSnackBarView;
    private final String mText;
    private final int mDuration;
    private final List<SnackBarInteraction> mInteractions;
    private final Action mAction;
    private final Placement mPlacement;
    private final TextView mActionTextView;
    private final TextView mMessageView;
    private final FrameLayout mMessageWrapper;
    private final View mParentView;

    private SnackBarListener mListener;

    private SnackBar(final Builder builder) {
        mContext = builder.mContext;
        mRootView = LayoutInflater.from(mContext).inflate(R.layout.snack_bar,
            null /* WindowManager will show this in show() below */);
        mSnackBarView = mRootView.findViewById(R.id.snack_bar);
        mText = builder.mSnackBarMessage;
        mDuration = builder.mDuration;
        mAction = builder.mAction;
        mPlacement = builder.mPlacement;
        mParentView = builder.mParentView;
        if (builder.mInteractions == null) {
            mInteractions = new ArrayList<SnackBarInteraction>();
        } else {
            mInteractions = builder.mInteractions;
        }

        mActionTextView = (TextView) mRootView.findViewById(R.id.snack_bar_action);
        mMessageView = (TextView) mRootView.findViewById(R.id.snack_bar_message);
        mMessageWrapper = (FrameLayout) mRootView.findViewById(R.id.snack_bar_message_wrapper);

        setUpButton();
        setUpTextLines();
    }

    public Context getContext() {
        return mContext;
    }

    public View getRootView() {
        return mRootView;
    }

    public View getParentView() {
        return mParentView;
    }

    public View getSnackBarView() {
        return mSnackBarView;
    }

    public String getMessageText() {
        return mText;
    }

    public String getActionLabel() {
        if (mAction == null) {
            return null;
        }
        return mAction.getActionLabel();
    }

    public int getDuration() {
        return mDuration;
    }

    public Placement getPlacement() {
        return mPlacement;
    }

    public List<SnackBarInteraction> getInteractions() {
        return mInteractions;
    }

    public void setEnabled(final boolean enabled) {
        mActionTextView.setClickable(enabled);
    }

    public void setListener(final SnackBarListener snackBarListener) {
        mListener = snackBarListener;
    }

    private void setUpButton() {
        if (mAction == null || mAction.getActionRunnable() == null) {
            mActionTextView.setVisibility(View.GONE);
            // In the XML layout we add left/right padding to the button to add space between
            // the message text and the button and on the right side we add padding to put space
            // between the button and the edge of the snack bar. This is so the button can use the
            // padding area as part of it's click target. Since we have no button, we need to put
            // some margin on the right side. While the left margin is already set on the wrapper,
            // we're setting it again to not have to make a special case for RTL.
            final MarginLayoutParams lp = (MarginLayoutParams) mMessageWrapper.getLayoutParams();
            final int leftRightMargin = mContext.getResources().getDimensionPixelSize(
                    R.dimen.snack_bar_left_right_margin);
            lp.leftMargin = leftRightMargin;
            lp.rightMargin = leftRightMargin;
            mMessageWrapper.setLayoutParams(lp);
        } else {
            mActionTextView.setVisibility(View.VISIBLE);
            mActionTextView.setText(mAction.getActionLabel());
            mActionTextView.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(final View v) {
                    mAction.getActionRunnable().run();
                    if (mListener != null) {
                        mListener.onActionClick();
                    }
                }
            });
        }
    }

    private void setUpTextLines() {
        if (mText == null) {
            mMessageView.setVisibility(View.GONE);
        } else {
            mMessageView.setVisibility(View.VISIBLE);
            mMessageView.setText(mText);
        }
    }
}
