/*
 * This is the source code of Telegram for Android v. 5.x.x.
 * It is licensed under GNU GPL v. 2 or later.
 * You should have received a copy of the license in this archive (see LICENSE).
 *
 * Copyright Nikolai Kudashov, 2013-2018.
 */

package org.telegram.ui.Cells;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.StaticLayout;
import android.text.TextUtils;
import android.text.style.ClickableSpan;
import android.text.style.URLSpan;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.TextView;

import androidx.core.view.GestureDetectorCompat;
import androidx.recyclerview.widget.RecyclerView;

import org.telegram.messenger.AndroidUtilities;
import org.telegram.messenger.Emoji;
import org.telegram.messenger.FileLog;
import org.telegram.messenger.LocaleController;
import org.telegram.messenger.MessageObject;
import org.telegram.messenger.R;
import org.telegram.messenger.browser.Browser;
import org.telegram.ui.ActionBar.BaseFragment;
import org.telegram.ui.ActionBar.BottomSheet;
import org.telegram.ui.ActionBar.Theme;
import org.telegram.ui.Components.AlertsCreator;
import org.telegram.ui.Components.BulletinFactory;
import org.telegram.ui.Components.LayoutHelper;
import org.telegram.ui.Components.LinkPath;
import org.telegram.ui.Components.LinkSpanDrawable;
import org.telegram.ui.Components.LoadingDrawable;
import org.telegram.ui.Components.StaticLayoutEx;
import org.telegram.ui.Components.URLSpanNoUnderline;

import java.util.concurrent.atomic.AtomicReference;

public class AboutLinkCell extends FrameLayout {

    private StaticLayout textLayout;
    private String oldText;
    private int textX;
    private int textY;
    private SpannableStringBuilder stringBuilder;
    private TextView valueTextView;
    private TextView showMoreTextView;
    private FrameLayout showMoreTextBackgroundView;
    private FrameLayout bottomShadow;
    private Drawable showMoreBackgroundDrawable;

    private LinkSpanDrawable pressedLink;
    private float pressedLinkYOffset;
    private Layout pressedLinkLayout;
    private LinkSpanDrawable.LinkCollector links;
    private Point urlPathOffset = new Point();
    private LinkPath urlPath = new LinkPath(true);
    private Browser.Progress currentProgress;
    private LoadingDrawable currentLoading;

    private BaseFragment parentFragment;
    private Theme.ResourcesProvider resourcesProvider;

    private FrameLayout container;
    private Drawable rippleBackground;

    private StaticLayout firstThreeLinesLayout;
    private StaticLayout[] nextLinesLayouts = null;
    private int lastInlineLine = -1;
    private Point[] nextLinesLayoutsPositions;
    private boolean needSpace = false;
    private boolean moreButtonDisabled;

    private GestureDetectorCompat gestureDetector;

    public AboutLinkCell(Context context, BaseFragment fragment) {
        this(context, fragment, null);
    }

    public AboutLinkCell(Context context, BaseFragment fragment, Theme.ResourcesProvider resourcesProvider) {
        super(context);

        this.resourcesProvider = resourcesProvider;
        parentFragment = fragment;

        container = new FrameLayout(context);
        container.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
        links = new LinkSpanDrawable.LinkCollector(container);
        rippleBackground = Theme.createRadSelectorDrawable(Theme.getColor(Theme.key_listSelector, resourcesProvider), 0, 0);

        valueTextView = new TextView(context);
        valueTextView.setVisibility(GONE);
        valueTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteGrayText2, resourcesProvider));
        valueTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 13);
        valueTextView.setLines(1);
        valueTextView.setMaxLines(1);
        valueTextView.setSingleLine(true);
        valueTextView.setGravity(LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT);
        valueTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
        valueTextView.setFocusable(false);
        container.addView(valueTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, (LocaleController.isRTL ? Gravity.RIGHT : Gravity.LEFT) | Gravity.BOTTOM, 23, 0, 23, 10));

        bottomShadow = new FrameLayout(context);
        Drawable shadowDrawable = context.getResources().getDrawable(R.drawable.gradient_bottom).mutate();
        shadowDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider), PorterDuff.Mode.SRC_ATOP));
        bottomShadow.setBackground(shadowDrawable);
        addView(bottomShadow, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, 12, Gravity.BOTTOM | Gravity.FILL_HORIZONTAL, 0, 0, 0, 0));

        addView(container, LayoutHelper.createFrame(LayoutHelper.MATCH_PARENT, LayoutHelper.MATCH_PARENT, Gravity.TOP | Gravity.FILL_HORIZONTAL));

        showMoreTextView = new TextView(context) {
            private boolean pressed = false;
            @Override
            public boolean onTouchEvent(MotionEvent event) {
                boolean wasPressed = pressed;
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    pressed = true;
                } else if (event.getAction() != MotionEvent.ACTION_MOVE) {
                    pressed = false;
                }
                if (wasPressed != pressed) {
                    invalidate();
                }
                return pressed || super.onTouchEvent(event);
            }

            @Override
            protected void onDraw(Canvas canvas) {
                if (pressed) {
                    AndroidUtilities.rectTmp.set(0, 0, getWidth(), getHeight());
                    canvas.drawRoundRect(AndroidUtilities.rectTmp, AndroidUtilities.dp(4), AndroidUtilities.dp(4), Theme.chat_urlPaint);
                }
                super.onDraw(canvas);
            }
        };
        showMoreTextView.setTextColor(Theme.getColor(Theme.key_windowBackgroundWhiteBlueText, resourcesProvider));
        showMoreTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
        showMoreTextView.setLines(1);
        showMoreTextView.setMaxLines(1);
        showMoreTextView.setSingleLine(true);
        showMoreTextView.setText(LocaleController.getString("DescriptionMore", R.string.DescriptionMore));
        showMoreTextView.setOnClickListener(e -> {
            updateCollapse(true, true);
        });
        showMoreTextView.setPadding(AndroidUtilities.dp(2), 0, AndroidUtilities.dp(2), 0);
        showMoreTextBackgroundView = new FrameLayout(context);
        showMoreBackgroundDrawable = context.getResources().getDrawable(R.drawable.gradient_left).mutate();
        showMoreBackgroundDrawable.setColorFilter(new PorterDuffColorFilter(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider), PorterDuff.Mode.MULTIPLY));
        showMoreTextBackgroundView.setBackground(showMoreBackgroundDrawable);
        showMoreTextBackgroundView.setPadding(
            showMoreTextBackgroundView.getPaddingLeft() + AndroidUtilities.dp(4),
            AndroidUtilities.dp(1),
            0,
            AndroidUtilities.dp(3)
        );
        showMoreTextBackgroundView.addView(showMoreTextView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT));
        addView(showMoreTextBackgroundView, LayoutHelper.createFrame(LayoutHelper.WRAP_CONTENT, LayoutHelper.WRAP_CONTENT, Gravity.RIGHT | Gravity.BOTTOM, 22 - showMoreTextBackgroundView.getPaddingLeft() / AndroidUtilities.density, 0, 22 - showMoreTextBackgroundView.getPaddingRight() / AndroidUtilities.density, 6));
        backgroundPaint.setColor(Theme.getColor(Theme.key_windowBackgroundWhite, resourcesProvider));

        setWillNotDraw(false);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();

        if (showMoreTextView.getVisibility() == View.VISIBLE &&
            x >= showMoreTextBackgroundView.getLeft() && x <= showMoreTextBackgroundView.getRight() &&
            y >= showMoreTextBackgroundView.getTop() && y <= showMoreTextBackgroundView.getBottom()
        ) {
//            event.offsetLocation(showMoreTextBackgroundView.getLeft(), showMoreTextBackgroundView.getTop());
            return false;
        }

        boolean result = false;
        if (textLayout != null || nextLinesLayouts != null) {
            if (event.getAction() == MotionEvent.ACTION_DOWN || pressedLink != null && event.getAction() == MotionEvent.ACTION_UP) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    resetPressedLink();
                    LinkSpanDrawable link = hitLink(x, y);
                    if (link != null) {
                        result = true;
                        pressedLinkLayout = textLayout;
                        links.addLink(pressedLink = link);
                        AndroidUtilities.runOnUIThread(longPressedRunnable, ViewConfiguration.getLongPressTimeout());
                    }
                } else if (pressedLink != null) {
                    try {
                        onLinkClick((ClickableSpan) pressedLink.getSpan(), textLayout, pressedLinkYOffset);
                    } catch (Exception e) {
                        FileLog.e(e);
                    }
                    resetPressedLink();
                    result = true;
                }
            } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
                resetPressedLink();
            }
        }
        return result || super.onTouchEvent(event);
    }

    private void setShowMoreMarginBottom(int marginBottom) {
        FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) showMoreTextBackgroundView.getLayoutParams();
        if (lp.bottomMargin != marginBottom) {
            lp.bottomMargin = marginBottom;
            showMoreTextBackgroundView.setLayoutParams(lp);
        }
    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
        return false;
    }

    private Paint backgroundPaint = new Paint();
    @Override
    public void draw(Canvas canvas) {
        View parent = (View) getParent();
        float alpha = parent == null ? 1f : (float) Math.pow(parent.getAlpha(), 2f);

        drawText(canvas);

        float viewAlpha = showMoreTextBackgroundView.getAlpha();
        if (viewAlpha > 0) {
            canvas.save();
            canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (viewAlpha * 255), Canvas.ALL_SAVE_FLAG);
            showMoreBackgroundDrawable.setAlpha((int) (alpha * 255));
            canvas.translate(showMoreTextBackgroundView.getLeft(), showMoreTextBackgroundView.getTop());
            showMoreTextBackgroundView.draw(canvas);
            canvas.restore();
        }
        viewAlpha = bottomShadow.getAlpha();
        if (viewAlpha > 0) {
            canvas.save();
            canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), (int) (viewAlpha * 255), Canvas.ALL_SAVE_FLAG);
            canvas.translate(bottomShadow.getLeft(), bottomShadow.getTop());
            bottomShadow.draw(canvas);
            canvas.restore();
        }

        container.draw(canvas);

        super.draw(canvas);
    }

    final float SPACE = AndroidUtilities.dp(3f);
    private void drawText(Canvas canvas) {
        canvas.save();
        canvas.clipRect(AndroidUtilities.dp(23 - 8), AndroidUtilities.dp(8), getWidth() - AndroidUtilities.dp(23), getHeight());
        canvas.translate(textX = AndroidUtilities.dp(23), 0);
        if (links != null && links.draw(canvas)) {
            invalidate();
        }
        canvas.translate(0, textY = AndroidUtilities.dp(8));

        try {
            Theme.profile_aboutTextPaint.linkColor = Theme.getColor(Theme.key_chat_messageLinkIn, resourcesProvider);
            if (firstThreeLinesLayout == null || !shouldExpand) {
                if (textLayout != null) {
                    textLayout.draw(canvas);
                }
            } else {
                firstThreeLinesLayout.draw(canvas);
                int lastLine = firstThreeLinesLayout.getLineCount() - 1;
                float top = firstThreeLinesLayout.getLineTop(lastLine) + firstThreeLinesLayout.getTopPadding();
                float x = firstThreeLinesLayout.getLineRight(lastLine) + (needSpace ? SPACE : 0),
                      y = firstThreeLinesLayout.getLineBottom(lastLine) - firstThreeLinesLayout.getLineTop(lastLine) - firstThreeLinesLayout.getBottomPadding();
                float t = easeInOutCubic(1f - (float) Math.pow(expandT, 0.25f));
                if (nextLinesLayouts != null) {
                    for (int line = 0; line < nextLinesLayouts.length; ++line) {
                        final StaticLayout layout = nextLinesLayouts[line];
                        if (layout != null) {
                            final int c = canvas.save();
                            if (nextLinesLayoutsPositions[line] != null) {
                                nextLinesLayoutsPositions[line].set((int) (textX + x * t), (int) (textY + top + y * (1f - t)));
                            }
                            if (lastInlineLine != -1 && lastInlineLine <= line) {
                                canvas.translate(0, top + y);
                                canvas.saveLayerAlpha(0, 0, layout.getWidth(), layout.getHeight(), (int) (255 * expandT), Canvas.ALL_SAVE_FLAG);
                            } else {
                                canvas.translate(x * t, top + y * (1f - t));
                            }
                            layout.draw(canvas);
                            canvas.restoreToCount(c);
                            x += layout.getLineRight(0) + SPACE;
                            y += layout.getLineBottom(0) + layout.getTopPadding();
                        }
                    }
                }
            }
        } catch (Exception e) {
            FileLog.e(e);
        }
        canvas.restore();
    }

    protected void didPressUrl(String url, Browser.Progress progress) {

    }
    protected void didResizeStart() {

    }
    protected void didResizeEnd() {

    }
    protected void didExtend() {}

    private void resetPressedLink() {
        links.clear();
        pressedLink = null;
        AndroidUtilities.cancelRunOnUIThread(longPressedRunnable);
        invalidate();
    }

    public void setText(String text, boolean parseLinks) {
        setTextAndValue(text, null, parseLinks);
    }

    public void setTextAndValue(String text, String value, boolean parseLinks) {
        if (TextUtils.isEmpty(text) || TextUtils.equals(text, oldText)) {
            return;
        }
        try {
            oldText = AndroidUtilities.getSafeString(text);
        } catch (Throwable e) {
            oldText = text;
        }
        stringBuilder = new SpannableStringBuilder(oldText);
        MessageObject.addLinks(false, stringBuilder, false, false, !parseLinks);
        Emoji.replaceEmoji(stringBuilder, Theme.profile_aboutTextPaint.getFontMetricsInt(), AndroidUtilities.dp(20), false);
        if (lastMaxWidth <= 0) {
            lastMaxWidth = AndroidUtilities.displaySize.x - AndroidUtilities.dp(23 + 23);
        }
        checkTextLayout(lastMaxWidth, true);
        updateHeight();
        int wasValueVisibility = valueTextView.getVisibility();
        if (TextUtils.isEmpty(value)) {
            valueTextView.setVisibility(GONE);
        } else {
            valueTextView.setText(value);
            valueTextView.setVisibility(VISIBLE);
        }
        if (wasValueVisibility != valueTextView.getVisibility()) {
            checkTextLayout(lastMaxWidth, true);
        }
        requestLayout();
    }

    Runnable longPressedRunnable = new Runnable() {
        @Override
        public void run() {
            if (pressedLink != null) {
                String url;
                if (pressedLink.getSpan() instanceof URLSpanNoUnderline) {
                    url = ((URLSpanNoUnderline) pressedLink.getSpan()).getURL();
                } else if (pressedLink.getSpan() instanceof URLSpan) {
                    url = ((URLSpan) pressedLink.getSpan()).getURL();
                } else {
                    url = pressedLink.getSpan().toString();
                }

                try {
                    performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
                } catch (Exception ignore) {}

                final Layout layout = pressedLinkLayout;
                final float yOffset = pressedLinkYOffset;

                ClickableSpan pressedLinkFinal = (ClickableSpan) pressedLink.getSpan();
                BottomSheet.Builder builder = new BottomSheet.Builder(parentFragment.getParentActivity());
                builder.setTitle(url);
                builder.setItems(new CharSequence[]{LocaleController.getString("Open", R.string.Open), LocaleController.getString("Copy", R.string.Copy)}, (dialog, which) -> {
                    if (which == 0) {
                        onLinkClick(pressedLinkFinal, layout, yOffset);
                    } else if (which == 1) {
                        AndroidUtilities.addToClipboard(url);
                        if (AndroidUtilities.shouldShowClipboardToast()) {
                            if (url.startsWith("@")) {
                                BulletinFactory.of(parentFragment).createSimpleBulletin(R.raw.copy, LocaleController.getString("UsernameCopied", R.string.UsernameCopied)).show();
                            } else if (url.startsWith("#") || url.startsWith("$")) {
                                BulletinFactory.of(parentFragment).createSimpleBulletin(R.raw.copy, LocaleController.getString("HashtagCopied", R.string.HashtagCopied)).show();
                            } else {
                                BulletinFactory.of(parentFragment).createSimpleBulletin(R.raw.copy, LocaleController.getString("LinkCopied", R.string.LinkCopied)).show();
                            }
                        }
                    }
                });
                builder.setOnPreDismissListener(di -> resetPressedLink());
                builder.show();

                pressedLink = null;
            }
        }
    };

    private LinkSpanDrawable hitLink(int x, int y) {
        if (x >= showMoreTextView.getLeft() && x <= showMoreTextView.getRight() &&
            y >= showMoreTextView.getTop() &&  y <= showMoreTextView.getBottom()) {
            return null;
        }
        if (getMeasuredWidth() > 0 && x > getMeasuredWidth() - AndroidUtilities.dp(23)) {
            return null;
        }
        LinkSpanDrawable link;
        if (firstThreeLinesLayout != null && expandT < 1 && shouldExpand) {
            if ((link = checkTouchTextLayout(firstThreeLinesLayout, textX, textY, x, y)) != null) {
                return link;
            }
            if (nextLinesLayouts != null) {
                for (int i = 0; i < nextLinesLayouts.length; ++i) {
                    if ((link = checkTouchTextLayout(nextLinesLayouts[i], nextLinesLayoutsPositions[i].x, nextLinesLayoutsPositions[i].y, x, y)) != null) {
                        return link;
                    }
                }
            }
        }
        if ((link = checkTouchTextLayout(textLayout, textX, textY, x, y)) != null) {
            return link;
        }
        return null;
    }

    private LinkSpanDrawable checkTouchTextLayout(StaticLayout textLayout, int textX, int textY, int ex, int ey) {
        try {
            int x = (int) (ex - textX);
            int y = (int) (ey - textY);
            final int line = textLayout.getLineForVertical(y);
            final int off = textLayout.getOffsetForHorizontal(line, x);

            final float left = textLayout.getLineLeft(line);
            if (left <= x && left + textLayout.getLineWidth(line) >= x && y >= 0 && y <= textLayout.getHeight()) {
                Spannable buffer = (Spannable) textLayout.getText();
                ClickableSpan[] link = buffer.getSpans(off, off, ClickableSpan.class);
                if (link.length != 0 && !AndroidUtilities.isAccessibilityScreenReaderEnabled()) {
                    LinkSpanDrawable linkDrawable = new LinkSpanDrawable(link[0], parentFragment.getResourceProvider(), ex, ey);
                    int start = buffer.getSpanStart(link[0]);
                    int end = buffer.getSpanEnd(link[0]);
                    LinkPath path = linkDrawable.obtainNewPath();
                    path.setCurrentLayout(textLayout, start, pressedLinkYOffset = textY);
                    textLayout.getSelectionPath(start, end, path);
                    return linkDrawable;
                }
            }
        } catch (Exception e) {
            FileLog.e(e);
        }
        return null;
    }

    private void onLinkClick(ClickableSpan pressedLink, Layout layout, float yOffset) {
        if (currentProgress != null) {
            currentProgress.cancel();
            currentProgress = null;
        }
        currentProgress = layout != null && pressedLink != null ? new Browser.Progress() {
            LoadingDrawable thisLoading;

            @Override
            public void init() {
                if (currentLoading != null) {
                    links.removeLoading(currentLoading, true);
                }
                currentLoading = thisLoading = LinkSpanDrawable.LinkCollector.makeLoading(layout, pressedLink, yOffset);
                thisLoading.setColors(
                    Theme.multAlpha(Theme.getColor(Theme.key_chat_linkSelectBackground, resourcesProvider), .8f),
                    Theme.multAlpha(Theme.getColor(Theme.key_chat_linkSelectBackground, resourcesProvider), 1.3f),
                    Theme.multAlpha(Theme.getColor(Theme.key_chat_linkSelectBackground, resourcesProvider), 1f),
                    Theme.multAlpha(Theme.getColor(Theme.key_chat_linkSelectBackground, resourcesProvider), 4f)
                );
                thisLoading.strokePaint.setStrokeWidth(AndroidUtilities.dpf2(1.25f));
                links.addLoading(thisLoading);
            }

            @Override
            public void end(boolean replacing) {
                AndroidUtilities.runOnUIThread(() -> {
                    if (thisLoading != null) {
                        links.removeLoading(thisLoading, true);
                    }
                }, replacing ? 0 : 350);
            }
        } : null;
        if (pressedLink instanceof URLSpanNoUnderline) {
            String url = ((URLSpanNoUnderline) pressedLink).getURL();
            if (url.startsWith("@") || url.startsWith("#") || url.startsWith("/")) {
                didPressUrl(url, currentProgress);
            }
        } else {
            if (pressedLink instanceof URLSpan) {
                String url = ((URLSpan) pressedLink).getURL();
                if (AndroidUtilities.shouldShowUrlInAlert(url)) {
                    AlertsCreator.showOpenUrlAlert(parentFragment, url, true, true, true, currentProgress, null);
                } else {
                    Browser.openUrl(getContext(), Uri.parse(url), true, true, currentProgress);
                }
            } else {
                pressedLink.onClick(this);
            }
        }
    }

    private static final int COLLAPSED_HEIGHT = AndroidUtilities.dp(8 + 20 * 3 + 8);
    private static final int MAX_OPEN_HEIGHT = COLLAPSED_HEIGHT;// + AndroidUtilities.dp(20);

    public class SpringInterpolator {
        public float tension;
        public float friction;
        public SpringInterpolator(float tension, float friction) {
            this.tension = tension;
            this.friction = friction;
        }

        private final float mass = 1f;
        private float position = 0, velocity = 0;
        public float getValue(float deltaTime) {
            deltaTime = Math.min(deltaTime, 250);
            final float MAX_DELTA_TIME = 18;
            while (deltaTime > 0) {
                final float step = Math.min(deltaTime, MAX_DELTA_TIME);
                step(step);
                deltaTime -= step;
            }
            return position;
        }

        private void step(float delta) {
            final float acceleration = (
                -tension * 0.000001f * (position - 1f) + // spring force
                -friction * 0.001f * velocity // damping force
            ) / mass; // pt/ms^2

            velocity = velocity + acceleration * delta; // pt/ms
            position = position + velocity * delta;
        }
    }

    private float expandT = 0f;
    private float rawCollapseT = 0f;
    private ValueAnimator collapseAnimator;
    private boolean expanded = false;
    public void updateCollapse(boolean value, boolean animated) {
        if (collapseAnimator != null) {
            collapseAnimator.cancel();
            collapseAnimator = null;
        }

        float fromValue = expandT,
              toValue   = value ? 1f : 0f;
        if (animated) {
            if (toValue > 0) {
                didExtend();
            }

            float fullHeight = textHeight();
            float collapsedHeight = Math.min(COLLAPSED_HEIGHT, fullHeight);
            float fromHeight = AndroidUtilities.lerp(collapsedHeight, fullHeight, fromValue);
            float toHeight = AndroidUtilities.lerp(collapsedHeight, fullHeight, toValue);
            float dHeight = Math.abs(toHeight - fromHeight);
//            float speedMultiplier = Math.min(Math.max(dHeight / AndroidUtilities.dp(76), 0.5f), 2f);

            collapseAnimator = ValueAnimator.ofFloat(0, 1);
            final float duration = Math.abs(fromValue - toValue) * 1250 * 2f;
            final SpringInterpolator spring = new SpringInterpolator(380f, 20.17f);
            final AtomicReference<Float> lastValue = new AtomicReference<>(fromValue);
            collapseAnimator.addUpdateListener(a -> {
                float now = (float) a.getAnimatedValue();
                float deltaTime = (now - lastValue.getAndSet(now)) * 1000 * 8f;

                rawCollapseT = AndroidUtilities.lerp(fromValue, toValue, (float) a.getAnimatedValue());
                expandT = AndroidUtilities.lerp(fromValue, toValue, spring.getValue(deltaTime));
                if (expandT > 0.8f && container.getBackground() == null) {
                    container.setBackground(rippleBackground);
                }
                showMoreTextBackgroundView.setAlpha(1f - expandT);
                bottomShadow.setAlpha((float) Math.pow(1f - expandT, 2f));

                updateHeight();
                container.invalidate();
            });
            collapseAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    didResizeEnd();
                    if (container.getBackground() == null) {
                        container.setBackground(rippleBackground);
                    }
                    expanded = true;
                }

                @Override
                public void onAnimationStart(Animator animation) {
                    didResizeStart();
                }
            });
            collapseAnimator.setDuration((long) duration);
            collapseAnimator.start();
        } else {
            expandT = toValue;
            forceLayout();
        }
    }

    private int fromHeight() {
        return Math.min(COLLAPSED_HEIGHT + (valueTextView.getVisibility() == View.VISIBLE ? AndroidUtilities.dp(20) : 0), textHeight());
    }
    private int updateHeight() {
        int textHeight = textHeight();
        float fromHeight = fromHeight();
        int height = shouldExpand ? (int) AndroidUtilities.lerp(fromHeight, textHeight, expandT) : textHeight;
        setHeight(height);
        return height;
    }
    private void setHeight(int height) {
        RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) getLayoutParams();
        int wasHeight;
        boolean newHeight;
        if (lp == null) {
            newHeight = true;
            wasHeight = (getMinimumHeight() == 0 ? getHeight() : getMinimumHeight());
            lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, height);
        } else {
            wasHeight = lp.height;
            newHeight = wasHeight != height;
            lp.height = height;
        }
        if (newHeight) {
            setLayoutParams(lp);
        }
    }

    private static final int MOST_SPEC = View.MeasureSpec.makeMeasureSpec(999999, View.MeasureSpec.AT_MOST);
    private int lastMaxWidth = 0;
    @SuppressLint("DrawAllocation")
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        checkTextLayout(MeasureSpec.getSize(widthMeasureSpec) - AndroidUtilities.dp(23 + 23), false);
        int height = updateHeight();
        super.onMeasure(
            widthMeasureSpec,
            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
        );
    }

    private StaticLayout makeTextLayout(CharSequence string, int width) {
        if (Build.VERSION.SDK_INT >= 24) {
            return StaticLayout.Builder.obtain(string, 0, string.length(), Theme.profile_aboutTextPaint, width)
                    .setBreakStrategy(StaticLayout.BREAK_STRATEGY_HIGH_QUALITY)
                    .setHyphenationFrequency(StaticLayout.HYPHENATION_FREQUENCY_NONE)
                    .setAlignment(LocaleController.isRTL ? StaticLayoutEx.ALIGN_RIGHT() : StaticLayoutEx.ALIGN_LEFT())
                    .build();
        } else {
            return new StaticLayout(string, Theme.profile_aboutTextPaint, width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false);
        }
    }

    private void checkTextLayout(int maxWidth, boolean force) {
        if (moreButtonDisabled) {
            shouldExpand = false;
        }
        if (stringBuilder != null && (maxWidth != lastMaxWidth || force)) {
            textLayout = makeTextLayout(stringBuilder, maxWidth);
            shouldExpand = textLayout.getLineCount() >= 4; // && valueTextView.getVisibility() != View.VISIBLE;

            if (textLayout.getLineCount() >= 3 && shouldExpand) {
                int end = Math.max(textLayout.getLineStart(2), textLayout.getLineEnd(2));
                if (stringBuilder.charAt(end - 1) == '\n')
                    end -= 1;
                needSpace = stringBuilder.charAt(end - 1) != ' ' && stringBuilder.charAt(end - 1) != '\n';
                firstThreeLinesLayout = makeTextLayout(stringBuilder.subSequence(0, end), maxWidth);
                nextLinesLayouts = new StaticLayout[textLayout.getLineCount() - 3];
                nextLinesLayoutsPositions = new Point[textLayout.getLineCount() - 3];
                int lastLine = firstThreeLinesLayout.getLineCount() - 1;
                float x = firstThreeLinesLayout.getLineRight(lastLine) + (needSpace ? SPACE : 0);
                lastInlineLine = -1;
                if (showMoreTextBackgroundView.getMeasuredWidth() <= 0) {
                    showMoreTextBackgroundView.measure(MOST_SPEC, MOST_SPEC);
                }
                for (int line = 3; line < textLayout.getLineCount(); ++line) {
                    int s = textLayout.getLineStart(line),
                        e = textLayout.getLineEnd(line);
                    final StaticLayout layout = makeTextLayout(stringBuilder.subSequence(Math.min(s, e), Math.max(s, e)), maxWidth);
                    nextLinesLayouts[line - 3] = layout;
                    nextLinesLayoutsPositions[line - 3] = new Point();
                    if (lastInlineLine == -1 && x > maxWidth - showMoreTextBackgroundView.getMeasuredWidth() + showMoreTextBackgroundView.getPaddingLeft()) {
                        lastInlineLine = line - 3;
                    }
                    x += layout.getLineRight(0) + SPACE;
                }
                if (x < maxWidth - showMoreTextBackgroundView.getMeasuredWidth() + showMoreTextBackgroundView.getPaddingLeft()) {
                    shouldExpand = false;
                }
            }

            if (!shouldExpand) {
                firstThreeLinesLayout = null;
                nextLinesLayouts = null;
            }
            lastMaxWidth = maxWidth;

            container.setMinimumHeight(textHeight());

            if (shouldExpand && firstThreeLinesLayout != null) {
                setShowMoreMarginBottom(
                    fromHeight()
                    -AndroidUtilities.dp(8)
                    -firstThreeLinesLayout.getLineBottom(firstThreeLinesLayout.getLineCount() - 1)
                    -showMoreTextBackgroundView.getPaddingBottom()
                    -showMoreTextView.getPaddingBottom()
                    -(showMoreTextView.getLayout() == null ? 0 : showMoreTextView.getLayout().getHeight() - showMoreTextView.getLayout().getLineBottom(showMoreTextView.getLineCount() - 1))
                );
            }
        }
        showMoreTextView.setVisibility(shouldExpand ? View.VISIBLE : View.GONE);
        if (!shouldExpand && container.getBackground() == null) {
            container.setBackground(rippleBackground);
        }
        if (shouldExpand && expandT < 1 && container.getBackground() != null) {
            container.setBackground(null);
        }
    }

    private int textHeight() {
        int height = (textLayout != null ? textLayout.getHeight() : AndroidUtilities.dp(20)) + AndroidUtilities.dp(16);
        if (valueTextView.getVisibility() == VISIBLE) {
            height += AndroidUtilities.dp(23);
        }
        return height;
    }
    private boolean shouldExpand = false;
//    private boolean shouldCollapse() {
//        return textLayout != null && textLayout.getLineCount() > 4/* && valueTextView.getVisibility() != View.VISIBLE*/;
//    }

    public boolean onClick() {
        if (shouldExpand && expandT <= 0) {
            updateCollapse(true, true);
            return true;
        }
        return false;
    }
    private float easeInOutCubic(float x) {
        return x < 0.5 ? 4 * x * x * x : 1 - (float) Math.pow(-2 * x + 2, 3) / 2;
    }

    @Override
    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
        super.onInitializeAccessibilityNodeInfo(info);
        if (textLayout != null) {
            final CharSequence text = stringBuilder;
            final CharSequence valueText = valueTextView.getText();
            info.setClassName("android.widget.TextView");
            if (TextUtils.isEmpty(valueText)) {
                info.setText(text);
            } else {
                info.setText(valueText + ": " + text);
            }
        }
    }

    public void setMoreButtonDisabled(boolean moreButtonDisabled) {
        this.moreButtonDisabled = moreButtonDisabled;
    }
}
