# ProgressWheel.kt

    /*
     * The MIT License
     *
     * Original work sponsored and donated by The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     * Copyright (C) 2021 The Danish Health Data Authority (http://www.sundhedsdatastyrelsen.dk)
     *
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
     * of the Software, and to permit persons to whom the Software is furnished to do
     * so, subject to the following conditions:
     *
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
     * SOFTWARE.
     */

    package dk.nsp.mingraviditet.ui.widgets

    import android.animation.ValueAnimator
    import android.content.Context
    import android.graphics.Canvas
    import android.graphics.Paint
    import android.graphics.RectF
    import android.text.TextPaint
    import android.util.AttributeSet
    import android.view.View
    import android.view.animation.DecelerateInterpolator
    import dk.nsp.mingraviditet.R
    import dk.nsp.mingraviditet.util.extensions.getColorFromAttr

    class ProgressWheel : View {

        //Sizes (with defaults)
        // When setting the barWidth, the actual progress bar width will be a third
        private var mBarWidth = 32f
        private var mProgressBarWidth = mBarWidth / 3

        private var mCountTextSize = context.resources.getDimension(R.dimen.info_text_size)
        private var layoutHeight = 0
        private var layoutWidth = 0

        //Colors (with defaults)
        private var mProgressColor = context.getColorFromAttr(R.attr.colorPrimary)
        private val mRimColor = context.getColorFromAttr(R.attr.progressBarRimColor)
        private var mCountTextColor = context.getColorFromAttr(R.attr.colorPrimary)

        //Rectangles
        private var mRimBounds = RectF()
        private var mProgressBounds = RectF()

        //Paints
        private val mCirclePaint = Paint()
        private val mBarPaint = Paint()
        private var mCountTextPaint = TextPaint()

        private val mCircleBackgroundPaint = Paint()

        private val customTypeface = resources.getFont(R.font.ptsans_bold)

        private var mCountText: String? = "0%"

        private var mCountTextWidth: Float = 0f
        private var mCountTextHeight: Float = 0f

        // Set percentage
        private var mPercentage = 0

        private var hideTextOnNull = false

        constructor(context: Context) : super(context) {
            init(null, 0)
        }

        constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
            init(attrs, 0)
        }

        constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
            context,
            attrs,
            defStyle
        ) {
            init(attrs, defStyle)
        }

        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            layoutWidth = w
            layoutHeight = h
            setupBounds()
            setupPaints()

            invalidate()
        }

        private fun setupPaints() {
            mBarPaint.color = mProgressColor
            mBarPaint.isAntiAlias = true
            mBarPaint.style = Paint.Style.STROKE
            mBarPaint.strokeWidth = mProgressBarWidth//mBarWidth / 3
            mBarPaint.strokeCap = Paint.Cap.ROUND

            mCirclePaint.color = mRimColor
            mCirclePaint.isAntiAlias = true
            mCirclePaint.style = Paint.Style.STROKE
            mCirclePaint.strokeWidth = mBarWidth

            mCountTextPaint.color = mCountTextColor
            mCountTextPaint.flags = Paint.ANTI_ALIAS_FLAG
            mCountTextPaint.typeface = customTypeface

            mCircleBackgroundPaint.color = context.getColorFromAttr(R.attr.circleBackgroundColor)
            mCircleBackgroundPaint.isAntiAlias = true
            mCircleBackgroundPaint.flags = Paint.ANTI_ALIAS_FLAG
        }

        private fun setupBounds() {
            mRimBounds = RectF(
                mBarWidth,
                mBarWidth,
                width.toFloat() - mBarWidth,
                height.toFloat() - mBarWidth
            )

            mProgressBounds = RectF(
                mBarWidth,
                mBarWidth,
                width.toFloat() - mBarWidth,
                height.toFloat() - mBarWidth
            )

            // Count number text
            mCountTextPaint.textSize = mCountTextSize
            val fontMetrics = mCountTextPaint.fontMetrics
            mCountTextHeight = fontMetrics.bottom
            mCountTextWidth =
                mCountTextPaint.measureText(if (mCountText == null || mCountText!!.isEmpty()) " " else mCountText)
        }

        private fun init(attrs: AttributeSet?, defStyle: Int) {
            // Load attributes
            val a = context.obtainStyledAttributes(
                attrs, R.styleable.ProgressWheel, defStyle, 0
            )

            if (a.hasValue(R.styleable.ProgressWheel_pw_countText)) {
                mCountText = a.getString(R.styleable.ProgressWheel_pw_countText)
            }

            mBarWidth = a.getDimension(R.styleable.ProgressWheel_barWidth, mBarWidth)
            mProgressBarWidth =
                a.getDimension(R.styleable.ProgressWheel_progressBarWidth, mProgressBarWidth)
            mProgressColor = a.getColor(R.styleable.ProgressWheel_progressColor, mProgressColor)
    //        mRimColor = a.getColor(R.styleable.ProgressWheel_rimColor, mRimColor)
            mCountTextColor = a.getColor(R.styleable.ProgressWheel_countTextColor, mCountTextColor)
            mCountTextSize = a.getDimension(R.styleable.ProgressWheel_countTextSize, mCountTextSize)
            mPercentage = a.getInt(R.styleable.ProgressWheel_percentage, mPercentage)
            hideTextOnNull = a.getBoolean(R.styleable.ProgressWheel_hideTextOnNull, false)

            a.recycle()

            // Set up a default TextPaint object
            mCountTextPaint = TextPaint()
            mCountTextPaint.flags = Paint.ANTI_ALIAS_FLAG
            mCountTextPaint.textAlign = Paint.Align.LEFT

            // Update TextPaint and text measurements from attributes
            invalidate()
        }

        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            canvas.drawCircle(
                layoutWidth / 2.toFloat(),
                layoutHeight / 2.toFloat(),
                layoutWidth / 2.toFloat() - mBarWidth,
                mCircleBackgroundPaint
            )

            canvas.drawArc(mRimBounds, 0f, 360f, false, mCirclePaint)
            val percentage = mPercentage * 3.6F // Percentage -> Angle
            canvas.drawArc(mProgressBounds, -90f, percentage, false, mBarPaint)

            // Dynamically update text color
            if (mCountText == "0%") {
                if (hideTextOnNull) {
                    hideCountText()
                } else if (!hideTextOnNull) {
                    mCountTextPaint.color = context.getColorFromAttr(R.attr.disabledTextColor01)
                }
            } else {
                mCountTextPaint.color = context.getColorFromAttr(R.attr.colorPrimary)
            }

            if (mCountText != null) {
                val horizontalCountTextOffset = mCountTextPaint.measureText(mCountText) / 2
                canvas.drawText(
                    mCountText!!,
                    this.width / 2 - horizontalCountTextOffset,
                    (this.height / 2).toFloat() + mCountTextHeight * 1.4f, //1.4F is a magic float, to adjust for the font family's slight bigger margins..
                    mCountTextPaint
                )
            }
        }

        fun setStepCountText(countText: String) {
            mCountText = countText
            invalidate()
        }

        fun setProgressColor(color: Int) {
            mProgressColor = color
            invalidate()
        }

        fun setPercentage(per: Int) {
            startAnimation(per)
        }

        fun setCountTextColor(color: Int) {
            mCountTextColor = color
            invalidate()
        }

        fun hideCountText() {
            mCountText = null
            invalidate()
        }

        private fun startAnimation(per: Int) {
            val diff = per - mPercentage

            val valueAnimator = ValueAnimator
                .ofInt(mPercentage, mPercentage + diff)
                .setDuration(1000)
            valueAnimator.interpolator = DecelerateInterpolator()

            valueAnimator.addUpdateListener { animation ->
                mPercentage = animation.animatedValue as Int
                invalidate()
            }

            valueAnimator.start()
        }
    }