/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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.android.emergency.widgets.countdown;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;

import androidx.annotation.GuardedBy;

import com.android.emergency.R;

import java.time.Duration;

/**
 * The renderer which renders the text view to show how many seconds left to count down.
 */
public class CountDownRenderer {

    @GuardedBy("this")
    private final Paint mTimerTextPaint;

    @GuardedBy("this")
    private final Paint mUnitTextPaint;

    @GuardedBy("this")
    private RectF mBounds = null;

    @GuardedBy("this")
    private boolean mIsRevealed = false;

    @GuardedBy("this")
    private Duration mCountDownLeft;

    private Context mContext;
    private String mSecondUnitText;
    private float mUnitTextLeftMargin;

    CountDownRenderer(Context context) {
        mContext = context;
        mTimerTextPaint = new Paint();
        mTimerTextPaint.setTextAlign(Paint.Align.CENTER);
        mTimerTextPaint.setTypeface(Typeface.SANS_SERIF);
        mTimerTextPaint.setColor(Color.WHITE);
        mTimerTextPaint.setAntiAlias(true);
        mUnitTextPaint = new Paint();
        mUnitTextPaint.setTypeface(Typeface.SANS_SERIF);
        mUnitTextPaint.setTextAlign(Paint.Align.CENTER);
        mUnitTextPaint.setColor(Color.WHITE);
        mUnitTextPaint.setAntiAlias(true);
        mSecondUnitText = context.getString(R.string.count_down_unit);
    }

    /** Shows the count down rendering. */
    synchronized void show() {
        mIsRevealed = true;
    }

    /** Returns true if count down text should be displayed, false otherwise. */
    synchronized boolean isRevealed() {
        return mIsRevealed;
    }

    /**
     * Draws the count down text.
     *
     * <p>Called from {@link LoopingAnimationThread}.
     */
    synchronized void draw(Canvas canvas) {
        if (!mIsRevealed || mBounds == null || mCountDownLeft == null) {
            return;
        }

        String timerText = Long.toString(mCountDownLeft.getSeconds());
        RectF timerTextBounds = measureText(timerText, mTimerTextPaint);
        RectF unitTextBounds = measureText(mSecondUnitText, mUnitTextPaint);
        // Shift timer text and unit text to share baseline.
        unitTextBounds.offset(0, timerTextBounds.height() - unitTextBounds.height());
        // Calculate final text bound that all text will be drawn inside of.
        RectF finalTextBounds =
                new RectF(
                        0,
                        0,
                        timerTextBounds.width() + unitTextBounds.width() + mUnitTextLeftMargin,
                        timerTextBounds.height());
        // Offset unit text bounds to right of timer text bounds.
        unitTextBounds.offset(timerTextBounds.width() + mUnitTextLeftMargin, 0);

        // Center final text bounds in provided bounds and place timer text and unit text inside.
        finalTextBounds.offset(
                mBounds.left + (mBounds.width() - finalTextBounds.width()) * 0.5f,
                mBounds.bottom - (mBounds.height() - finalTextBounds.height()));
        timerTextBounds.offset(finalTextBounds.left, finalTextBounds.top);
        unitTextBounds.offset(finalTextBounds.left, finalTextBounds.top);

        // Draw text.
        canvas.drawText(
                timerText, timerTextBounds.centerX(), timerTextBounds.bottom, mTimerTextPaint);
        canvas.drawText(
                mSecondUnitText, unitTextBounds.centerX(), unitTextBounds.bottom, mUnitTextPaint);
    }

    /**
     * Sets the bounds text should be drawn in.
     *
     * <p>Called from ui thread.
     */
    synchronized void updateBounds(RectF loopBounds, float totalDiameter) {
        this.mBounds =
                new RectF(loopBounds.left, loopBounds.top, loopBounds.right, loopBounds.bottom);
        float textSizeToBoundsRatio =
                mContext.getResources().getFloat(
                        R.dimen.count_down_view_text_size_to_diameter_ratio);
        float unitTextToTimerTextSizeRatio =
                mContext.getResources().getFloat(
                        R.dimen.count_down_view_unit_text_to_time_text_size_ratio);
        float unitTextLeftMarginToBoundsRatio =
                mContext.getResources().getFloat(
                        R.dimen.count_down_view_unit_text_left_margin_to_diameter_ratio);
        mTimerTextPaint.setTextSize(textSizeToBoundsRatio * totalDiameter);
        mUnitTextPaint.setTextSize(
                textSizeToBoundsRatio * unitTextToTimerTextSizeRatio * totalDiameter);
        mUnitTextLeftMargin = unitTextLeftMarginToBoundsRatio * totalDiameter;
    }

    private static RectF measureText(String text, Paint textPaint) {
        Rect textBounds = new Rect();
        textPaint.getTextBounds(text, 0, text.length(), textBounds);
        RectF textBoundsF = new RectF(textBounds);
        // Readjust text to be aligned to origin as their top left.
        textBoundsF.offset(-textBounds.left, -textBounds.top);
        return textBoundsF;
    }

    synchronized void setCountDownLeft(Duration timeLeft) {
        mCountDownLeft = timeLeft;
    }
}
