/*
 * Copyright (C) 2016 Bilibili
 *
 * 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.adealink.weparty.commonui.text;

import android.content.Context;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.text.Spanned;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatTextView;

import com.adealink.weparty.commonui.text.span.DraweeSpan;
import com.facebook.drawee.drawable.ForwardingDrawable;

/**
 * Like {@link com.facebook.drawee.view.DraweeView} that displays drawables {@link DraweeSpan} but surrounded with text.
 *
 * @author yrom
 */
public class DraweeTextView extends AppCompatTextView {
    public DraweeTextView(Context context) {
        super(context);
    }

    public DraweeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public DraweeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private boolean mHasDraweeInText;
    // detect drawee-spans has been attached or not
    private boolean mIsSpanAttached;

    @Override
    public void setText(CharSequence text, BufferType type) {
        boolean wasSpanAttached = mIsSpanAttached;
        if (mHasDraweeInText && wasSpanAttached) {
            onDetach(); // detach all old images
            mHasDraweeInText = false;
        }
        if (text instanceof Spanned) {
            // find DraweeSpan in text
            DraweeSpan[] spans = ((Spanned) text).getSpans(0, text.length(), DraweeSpan.class);
            mHasDraweeInText = spans.length > 0;
        }
        super.setText(text, type);
        if (mHasDraweeInText && wasSpanAttached) {
            onAttach(); // reattach drawee spans
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        onAttach();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        onDetach();
    }


    @Override
    public void onStartTemporaryDetach() {
        super.onStartTemporaryDetach();
        onDetach();
    }

    @Override
    public void onFinishTemporaryDetach() {
        super.onFinishTemporaryDetach();
        onAttach();
    }

    @Override
    public void invalidateDrawable(Drawable dr) {
        if (mHasDraweeInText) {
            /* invalidate the whole view in this case because it's very
             * hard to know what the bounds of drawables actually is.
             */
            invalidate();
        } else {
            super.invalidateDrawable(dr);
        }
    }

    @Override
    protected boolean verifyDrawable(Drawable who) {
        return super.verifyDrawable(who) || mHasDraweeInText
                // only schedule animation on AnimatableDrawable
                && (who instanceof ForwardingDrawable && who.getCurrent() instanceof Animatable);
    }

    /**
     * Attach DraweeSpans in text
     */
    final void onAttach() {
        DraweeSpan[] images = getImages();
        for (DraweeSpan image : images) {
            image.onAttach(this);
        }
        mIsSpanAttached = true;
    }

    private DraweeSpan[] getImages() {
        CharSequence text = getText();
        if (mHasDraweeInText && text instanceof Spanned && text.length() > 0) {
            return ((Spanned) text).getSpans(0, text.length(), DraweeSpan.class);
        }
        return new DraweeSpan[0]; // TODO: pool empty typed array
    }

    /**
     * Detach all of the DraweeSpans in text
     */
    final void onDetach() {
        DraweeSpan[] images = getImages();
        for (DraweeSpan image : images) {
            Drawable drawable = image.getDrawable();
            // reset callback first
            if (drawable != null) {
                unscheduleDrawable(drawable);
            }
            image.onDetach();
        }
        mIsSpanAttached = false;
    }
}
