package com.gh.gamecenter.common.view;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.os.Build;
import android.text.Layout;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.text.style.ForegroundColorSpan;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatTextView;
import androidx.core.content.ContextCompat;

import com.gh.gamecenter.common.R;


public class ExpandTextView extends AppCompatTextView {

    private CharSequence mSnapshotText;

    private String mEndText = "...";
    private CharSequence mShrankText = "";
    private String mExpandText = mEndText + "全文";
    private CharSequence mExpandedText = "";
    private boolean mUseGradientAlphaEndText = false;
    private boolean mShowExpandTextRegardlessOfMaxLines = false; // 不论文字超过 maxLines 都显示"...展开"文字
    private boolean mKeepShrank = false; // 保持收起状态，只响应 ExpandCallback

    private int mMaxLines = 3; // 由于sdk版本限制(getMaxLines) 这里设置默认值

    private boolean mInitLayout = false;
    private boolean mIsExpanded = false; // 位于 recyclerView 时需要自行在外层管理是否已展开

    private ExpandCallback mExpandCallback;
    private SelfCalculateMaxLinesCallback mMaxLinesCalculatedCallback;

    private Rect mLastVisibleLineRect;
    private Rect mLastActualLineRect;

    private static int DEFAULT_ADDITIONAL_END_TEXT_COUNT = 2;

    public ExpandTextView(Context context) {
        super(context);
    }

    public ExpandTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            mMaxLines = getMaxLines();
        }

        mLastVisibleLineRect = new Rect();
        mLastActualLineRect = new Rect();

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ExpandTextView);
        mUseGradientAlphaEndText = ta.getBoolean(R.styleable.ExpandTextView_useGradientAlphaEndText, false);
        mEndText = ta.getString(R.styleable.ExpandTextView_endText) == null ? mEndText : ta.getString(R.styleable.ExpandTextView_endText);
        mExpandText = ta.getString(R.styleable.ExpandTextView_expandText) == null ? mExpandText : ta.getString(R.styleable.ExpandTextView_expandText);
        mKeepShrank = ta.getBoolean(R.styleable.ExpandTextView_keepShrank, false);
        ta.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (mShowExpandTextRegardlessOfMaxLines && !mIsExpanded) {
            updateMaxLines();
        }
        setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight() - getExtraBottomPadding());
    }

    private void updateMaxLines() {
        mMaxLines = getLineCount() - 1;
        setMaxLines(mMaxLines);
        mMaxLinesCalculatedCallback.onMaxLinesCalculated(mMaxLines);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (mMaxLines > 0
                && ((mShowExpandTextRegardlessOfMaxLines && !mIsExpanded) || (mInitLayout && !mIsExpanded && getLineCount() > mMaxLines))) {
            mSnapshotText = getText();
            mInitLayout = false;
            showExpandButton();
        }
    }

    public void setExpandText(String text) {
        this.mExpandText = text;
    }

    public void setExpandCallback(ExpandCallback callback) {
        this.mExpandCallback = callback;
    }

    /**
     * 适用于不使用 maxLines 而是整段收起时的文字来确定“...更多”的位置的样式
     *
     * @param shrankText   收起时的文字 （“...更多”跟在 shrankText 后）
     * @param expandedText 展开时的文字
     */
    public void setShrankTextAndExpandedText(CharSequence shrankText, CharSequence expandedText) {
        mShrankText = shrankText;
        mExpandedText = expandedText;
        mShowExpandTextRegardlessOfMaxLines = !TextUtils.isEmpty(shrankText);

        if (!mIsExpanded && mShowExpandTextRegardlessOfMaxLines) {
            setText(mShrankText);
        } else {
            setText(mExpandedText);
        }
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        mInitLayout = true;
        super.setText(text, type);
    }

    private void showExpandButton() {
        // 避免二次生成展开按钮 (有点粗暴，但是为了满足 recyclerView 复用后没问题，还是每次都用当前文字来判断比较好)
        if (getText().toString().endsWith(mExpandText)) {
            return;
        }

        String finalEndText = "";
        TextPaint paint = getPaint();

        Layout layout = getLayout();
        int start = layout.getLineStart(0);
        int lastLineEnd = layout.getLineEnd(mMaxLines - 1);
        int lastLineStart = layout.getLineStart(mMaxLines - 1);
        float lastLineRight = layout.getLineRight(mMaxLines - 1);

        int viewWidth = getWidth() - getPaddingRight() - getPaddingLeft();
        int additionalEndTextCount = 0;

        float expandTextWidth;
        if (mUseGradientAlphaEndText) {
            additionalEndTextCount = DEFAULT_ADDITIONAL_END_TEXT_COUNT;
            expandTextWidth = paint.measureText(mEndText + mExpandText);
        } else {
            expandTextWidth = paint.measureText(mExpandText);
        }

        CharSequence content = mSnapshotText.subSequence(start, lastLineEnd);

        if (viewWidth - lastLineRight > expandTextWidth) {
            if (mUseGradientAlphaEndText) {
                finalEndText = content.toString()
                        .substring(content.length() - additionalEndTextCount, content.length()) + mEndText;
                finalEndText = finalEndText.replace("\n", "");

                content = content.subSequence(0, content.length() - additionalEndTextCount) + finalEndText + mExpandText;
            } else {
                content = content.toString().trim() + mExpandText;
            }
        } else {
            CharSequence lastLineText = mSnapshotText.subSequence(lastLineStart, lastLineEnd);
            CharSequence subSequence;
            float subSequenceWidth;
            for (int i = lastLineText.length() - 1; i > 0; i--) {
                if (mUseGradientAlphaEndText) {

                    // 避免 additionalEntTextCount 大于 i 造成数组越界 (TODO 代码太多，有时间再检查)
                    if (additionalEndTextCount > i) return;

                    subSequence = lastLineText.subSequence(0, i - additionalEndTextCount);
                    subSequenceWidth = paint.measureText(subSequence.toString());

                    finalEndText = lastLineText.subSequence(i - additionalEndTextCount, i) + mEndText;
                    expandTextWidth = paint.measureText(finalEndText + mExpandText);

                    if (viewWidth - subSequenceWidth > expandTextWidth) {
                        finalEndText = finalEndText.replace("\n", "");
                        content = mSnapshotText.subSequence(start, lastLineStart + i - additionalEndTextCount) + finalEndText + mExpandText;
                        break;
                    }
                } else {
                    subSequence = lastLineText.subSequence(0, i);
                    subSequenceWidth = paint.measureText(subSequence.toString());

                    if (viewWidth - subSequenceWidth > expandTextWidth) {
                        content = mSnapshotText.subSequence(start, lastLineStart + i) + mExpandText;
                        break;
                    }
                }
            }
        }

        int length = mSnapshotText.length();
        int expandTextStartPosition;
        expandTextStartPosition = content.length() - finalEndText.length() - mExpandText.length();
        expandTextStartPosition = Math.max(expandTextStartPosition, 0);

        // 避免越界
        if (expandTextStartPosition >= length) return;

        SpannableStringBuilder msp = new SpannableStringBuilder(mSnapshotText, 0, expandTextStartPosition)
                .append(finalEndText)
                .append(mExpandText);

        if (expandTextStartPosition + mEndText.length() >= msp.length()) return;

        msp.setSpan(new ClickableSpan() {
            @Override
            public void updateDrawState(@NonNull TextPaint ds) {
                super.updateDrawState(ds);
                ds.setColor(ContextCompat.getColor(getContext(), R.color.text_theme));
                ds.setUnderlineText(false);
            }

            @Override
            public void onClick(@NonNull View widget) {
                if (!mKeepShrank) {
                    mIsExpanded = true;
                    setMaxLines(Integer.MAX_VALUE);
                    if (mShowExpandTextRegardlessOfMaxLines) {
                        setText(mExpandedText);
                    } else {
                        setText(mSnapshotText);
                    }
                }

                if (mExpandCallback != null) {
                    mExpandCallback.onExpand();
                }
            }
        }, expandTextStartPosition + mEndText.length(), msp.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        int paintColor = 0;

        // 找到第一个位置与 endTextStartPosition 贴合的 ForegroundSpan / ClickableSpan ,
        // 获取颜色赋值给 GradientAlphaTextSpan
        Object[] objects = msp.getSpans(0, expandTextStartPosition, Object.class);
        if (objects.length != 0) {
            for (Object span : objects) {
                int startPosition = msp.getSpanStart(span);
                int endPosition = msp.getSpanEnd(span);
                if (expandTextStartPosition >= startPosition && expandTextStartPosition <= endPosition) {
                    if (span instanceof ForegroundColorSpan) {
                        paintColor = ((ForegroundColorSpan) span).getForegroundColor();
                        break;
                    } else if (span instanceof ClickableSpan) {
                        paintColor = getResources().getColor(R.color.text_theme);
                        break;
                    }
                }
            }
        }
        msp.setSpan(new GradientAlphaTextSpan(paintColor), expandTextStartPosition, expandTextStartPosition + finalEndText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);

        setText(msp);
        setMovementMethod(CustomLinkMovementMethod.getInstance());
    }

    /**
     * 获取 maxLines + lineSpacingExtra + movementMethod 一起使用时产生的大小与 lineSpacingExtra 一样的底部空间
     */
    private int getExtraBottomPadding() {
        int result = 0;
        // 界面上显示的最后一行的 index
        int lastVisibleLineIndex = Math.min(getMaxLines(), getLineCount()) - 1;
        // 获取实际文字的最后一行的 index
        int lastActualLineIndex = getLineCount() - 1;

        if (lastVisibleLineIndex >= 0) {
            Layout layout = getLayout();
            int lastVisibleLineBaseline = getLineBounds(lastVisibleLineIndex, mLastVisibleLineRect);
            getLineBounds(lastActualLineIndex, mLastActualLineRect);
            int heightBetweenLastVisibleLineRectAndLastActualLineRect = (mLastActualLineRect.bottom - mLastVisibleLineRect.bottom);

            if (getMeasuredHeight() == getLayout().getHeight() - heightBetweenLastVisibleLineRectAndLastActualLineRect) {
                result = mLastVisibleLineRect.bottom - (lastVisibleLineBaseline + layout.getPaint()
                        .getFontMetricsInt().descent + getPaddingBottom());
                if (getLineSpacingExtra() > result) {
                    result = 0;
                } else {
                    result = (int) getLineSpacingExtra();
                }
            }
        }
        return result;
    }

    /**
     * 此方法仅更改标记，不做实际展开的操作
     */
    public void setIsExpanded(boolean isExpanded) {
        mIsExpanded = isExpanded;
    }

    public void setExpandMaxLines(int maxLines) {
        mMaxLines = maxLines;
        setMaxLines(maxLines);
    }

    public void setSelfCalculateMaxLinesCallback(SelfCalculateMaxLinesCallback callback) {
        mMaxLinesCalculatedCallback = callback;
    }

    public interface ExpandCallback {
        void onExpand();
    }

    public interface SelfCalculateMaxLinesCallback {
        void onMaxLinesCalculated(int maxLines);
    }

}
