package com.qimai.android.widgetlib.popup

import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.view.Gravity
import androidx.annotation.IntDef
import androidx.core.content.ContextCompat
import com.qimai.android.tools.dpTopx
import com.qimai.android.widgetlib.R

/**
 *   created by wangwei   ON 2019-08-29  email:wangwei_5521@163.com
 *   @version 1.1.1
 *   @Description
 **/
@Suppress("unused")
class QmTipOverlayDrawable(builder: Builder) : Drawable() {

//    private val outlineRect = Rect()
    private val radius: Float
    private val bgColor: Int
    private var borderWidth: Int
    private var borderColor: Int
    private val bgPaint: Paint?
    private val borderPaint: Paint?
    private val path: Path
    private val arrowPath: Path
    private var padding = 0
    private var gravity = builder.gravity
    private val rectF: RectF
    private val arrowHeight = 15f
    private var showArrow = true


    class Builder {

        @IntDef(Gravity.TOP, Gravity.BOTTOM, Gravity.NO_GRAVITY)
        @Retention(AnnotationRetention.SOURCE)
        annotation class GravityRange

        var defaultOverlayStyle: Int = R.style.QmToolTipOverlayDefault

        @GravityRange
        internal var gravity = Gravity.NO_GRAVITY


        var showArrow = true

        var arrowWeight = 0.5f

        var offset = 0

        var conetxt: Context? = null


        fun withContext(ctx: Context) = apply {
            this.conetxt = ctx
        }

        fun withArrowWeight(arrowWeight: Float) = apply {
            this.arrowWeight = arrowWeight
        }


        fun withGravity(@GravityRange gravity: Int) = apply {
            this.gravity = gravity
        }

        fun withArrowShow(showArrow: Boolean) = apply {
            this.showArrow = showArrow
        }


        fun withDefaultOverlayStyle(defaultOverlayStyle: Int) = apply {

            this.defaultOverlayStyle = defaultOverlayStyle
        }

        fun withOffset(offset: Int) = apply {
            this.offset = offset
        }



    }


    /**
     * 0-1
     */
    private var arrowWeight = 0.5f

    init {
        val context = builder.conetxt
        val theme = context!!.theme.obtainStyledAttributes(
            null,
            R.styleable.QmToolTipOverlay,
            0,
            builder.defaultOverlayStyle
        )
        this.radius =
            theme.getDimensionPixelSize(
                R.styleable.QmToolTipOverlay_overlay_radius,
                context!!.dpTopx(5f)
            ).toFloat()
        this.bgColor = theme.getColor(
            R.styleable.QmToolTipOverlay_overlay_bgColor,
            ContextCompat.getColor(context, R.color.QmTopBar_color_FFFFFFF)
        )
        this.borderWidth =
            theme.getDimensionPixelOffset(
                R.styleable.QmToolTipOverlay_overlay_borderWidth,
                context.dpTopx(1f)
            )
        this.borderColor = theme.getColor(
            R.styleable.QmToolTipOverlay_overlay_borderColor,
            ContextCompat.getColor(context, R.color.QmTopBar_color_FFFFFFF)
        )

        theme.recycle()
        this.showArrow = builder.showArrow
        this.rectF = RectF()
        bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        bgPaint.color = bgColor
        bgPaint.style = Paint.Style.FILL
        if (borderColor != 0) {
            borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
            borderPaint.color = borderColor
            borderPaint.style = Paint.Style.STROKE
            borderPaint.strokeJoin = Paint.Join.ROUND
            borderPaint.strokeWidth = borderWidth.toFloat()
        } else {
            borderPaint = null
        }
        path = Path()
        arrowPath = Path()
        arrowWeight = builder.arrowWeight
    }

    override fun draw(canvas: Canvas) {
        if (showArrow) {
            path.op(arrowPath, Path.Op.UNION)

        }
        bgPaint?.let {
            canvas.drawPath(path, it)
        }
        borderPaint?.let {
            canvas.drawPath(path, it)
        }
    }

    override fun setAlpha(alpha: Int) {
        bgPaint?.alpha = alpha
        borderPaint?.alpha = alpha
    }

    override fun getOpacity(): Int {
        return PixelFormat.TRANSLUCENT
    }

    override fun setColorFilter(colorFilter: ColorFilter?) {
    }

//    override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
//        super.setBounds(left, top, right, bottom)
//        outlineRect.set(left, top, right, bottom)
////        outlineRect.inset(padding, padding)
//
//        calculatePath(outlineRect)
//    }

//    override fun getOutline(outline: Outline) {
//        super.getOutline(outline)
//        copyBounds(outlineRect)
//        outlineRect.inset(padding, padding)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//
//            outline.setRoundRect(outlineRect, radius)
//        } else {
//            // 5.0以下
//
//            // todo nothing
//        }
//
//    }

      override fun onBoundsChange(bounds: Rect?) {
        super.onBoundsChange(bounds)
        calculatePath(bounds)
    }

    private fun calculatePath(outBounds: Rect?) {
        if (outBounds != null) {
            val left = outBounds.left + padding
            val top = outBounds.top + padding
            val right = outBounds.right - padding
            val bottom = outBounds.bottom - padding

            if (showArrow) {
                calculatePathWithGravity(
                    left = left,
                    top = top,
                    right = right,
                    bottom = bottom
                )
            } else {
                rectF.set(
                    left.toFloat() + borderWidth,
                    top.toFloat() + borderWidth,
                    right.toFloat() - borderWidth,
                    bottom.toFloat() - borderWidth
                )
                path.addRoundRect(rectF, radius, radius, Path.Direction.CW)
            }
        }
    }

    private fun calculatePathWithGravity(
        left: Int,
        top: Int,
        right: Int,
        bottom: Int
    ) {

        when (gravity) {
            Gravity.BOTTOM -> {
                drawArrowBottomWithRect(left, top, right, bottom)
            }

            Gravity.TOP -> {
                drawArrowTopWithRect(left, top, right, bottom)
            }
        }
    }

    /**
     * 箭头朝下
     *
     * * --------------
     * * |            |
     * * |            |
     * * |-----\/-----|
     */

    private fun drawArrowBottomWithRect(
        left: Int,
        top: Int,
        right: Int,
        bottom: Int
    ) {

        val radioHeight = (right - left) * arrowWeight
        rectF.set(
            left.toFloat() + borderWidth,
            top.toFloat() + borderWidth,
            right.toFloat() - borderWidth,
            bottom.toFloat() - arrowHeight - borderWidth
        )
        path.addRoundRect(rectF, radius, radius, Path.Direction.CW)

        arrowPath.moveTo(
            left + radioHeight + borderWidth - arrowHeight,
            bottom - arrowHeight - borderWidth
        )
        arrowPath.lineTo(
            left.toFloat() + radioHeight + borderWidth,
            (bottom - borderWidth).toFloat()
        )
        arrowPath.lineTo(
            left + radioHeight + arrowHeight + borderWidth,
            bottom - arrowHeight - borderWidth
        )
        arrowPath.close()

    }

    /**
     * 箭头朝上
     *
     * ------/\------
     * |            |
     * |            |
     * |-------------
     *
     */
    private fun drawArrowTopWithRect(
        left: Int,
        top: Int,
        right: Int,
        bottom: Int
    ) {
        rectF.set(
            left.toFloat() + borderWidth,
            top.toFloat() + borderWidth + arrowHeight,
            right.toFloat() - borderWidth,
            bottom.toFloat() - borderWidth
        )
        path.addRoundRect(rectF, radius, radius, Path.Direction.CW)
        val radioHeight = (right - left) * arrowWeight
        arrowPath.moveTo(
            left + borderWidth + radioHeight - arrowHeight,
            top + arrowHeight + borderWidth
        )
        arrowPath.lineTo(left + radioHeight + borderWidth, top.toFloat() + borderWidth)
        arrowPath.lineTo(
            left + arrowHeight + borderWidth + radioHeight,
            top + arrowHeight + borderWidth
        )
        arrowPath.close()
    }
}