/*
 * Copyright (c) 2018 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.systemui.statusbar.hvac;

import android.animation.ObjectAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.util.Property;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextSwitcher;
import android.widget.TextView;

import com.android.systemui.R;
import com.android.systemui.statusbar.car.hvac.TemperatureView;

/**
 * Simple text display of HVAC properties, It is designed to show mTemperature and is configured in
 * the XML.
 * XML properties:
 * hvacPropertyId - Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (16385)
 * hvacAreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
 * hvacTempFormat - Example: "%.1f\u00B0" (1 decimal and the degree symbol)
 * hvacOrientaion = Example: left
 */
public class AnimatedTemperatureView extends FrameLayout implements TemperatureView {

    private static final float TEMPERATURE_EQUIVALENT_DELTA = .01f;
    private static final Property<ColorDrawable, Integer> COLOR_PROPERTY =
            new Property<ColorDrawable, Integer>(Integer.class, "color") {

                @Override
                public Integer get(ColorDrawable object) {
                    return object.getColor();
                }

                @Override
                public void set(ColorDrawable object, Integer value) {
                    object.setColor(value);
                }
            };

    static boolean isHorizontal(int gravity) {
        return Gravity.isHorizontal(gravity)
                && (gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.CENTER_HORIZONTAL;
    }

    @SuppressLint("RtlHardcoded")
    static boolean isLeft(int gravity, int layoutDirection) {
        return Gravity
                .getAbsoluteGravity(gravity & Gravity.HORIZONTAL_GRAVITY_MASK, layoutDirection)
                == Gravity.LEFT;
    }

    static boolean isVertical(int gravity) {
        return Gravity.isVertical(gravity)
                && (gravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.CENTER_VERTICAL;
    }

    static boolean isTop(int gravity) {
        return (gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.TOP;
    }

    private final int mAreaId;
    private final int mPropertyId;
    private final int mPivotOffset;
    private final int mGravity;
    private final int mTextAppearanceRes;
    private final int mMinEms;
    private final Rect mPaddingRect;
    private final float mMinValue;
    private final float mMaxValue;

    private final ColorDrawable mBackgroundColor;

    private final TemperatureColorStore mColorStore = new TemperatureColorStore();
    private final TemperatureBackgroundAnimator mBackgroundAnimator;
    private final TemperatureTextAnimator mTextAnimator;
    boolean mDisplayInFahrenheit = false;

    public AnimatedTemperatureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.AnimatedTemperatureView);
        mAreaId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacAreaId, -1);
        mPropertyId = typedArray.getInt(R.styleable.AnimatedTemperatureView_hvacPropertyId, -1);
        mPivotOffset =
                typedArray.getDimensionPixelOffset(
                        R.styleable.AnimatedTemperatureView_hvacPivotOffset, 0);
        mGravity = typedArray.getInt(R.styleable.AnimatedTemperatureView_android_gravity,
                Gravity.START);
        mTextAppearanceRes =
                typedArray.getResourceId(R.styleable.AnimatedTemperatureView_android_textAppearance,
                        0);
        mMinEms = typedArray.getInteger(R.styleable.AnimatedTemperatureView_android_minEms, 0);
        mMinValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMinValue,
                Float.NaN);
        mMaxValue = typedArray.getFloat(R.styleable.AnimatedTemperatureView_hvacMaxValue,
                Float.NaN);


        mPaddingRect =
                new Rect(getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom());
        setPadding(0, 0, 0, 0);

        setClipChildren(false);
        setClipToPadding(false);

        // init Views
        TextSwitcher textSwitcher = new TextSwitcher(context);
        textSwitcher.setFactory(this::generateTextView);
        ImageView background = new ImageView(context);
        mBackgroundColor = new ColorDrawable(Color.TRANSPARENT);
        background.setImageDrawable(mBackgroundColor);
        background.setVisibility(View.GONE);

        mBackgroundAnimator = new TemperatureBackgroundAnimator(this, background);


        String format = typedArray.getString(R.styleable.AnimatedTemperatureView_hvacTempFormat);
        format = (format == null) ? "%.1f\u00B0" : format;
        CharSequence minText = typedArray.getString(
                R.styleable.AnimatedTemperatureView_hvacMinText);
        CharSequence maxText = typedArray.getString(
                R.styleable.AnimatedTemperatureView_hvacMaxText);
        mTextAnimator = new TemperatureTextAnimator(this, textSwitcher, format, mPivotOffset,
                minText, maxText);

        addView(background, ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);
        addView(textSwitcher, ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT);

        typedArray.recycle();
    }


    private TextView generateTextView() {
        TextView textView = new TextView(getContext());
        textView.setTextAppearance(mTextAppearanceRes);
        textView.setAllCaps(true);
        textView.setMinEms(mMinEms);
        textView.setGravity(mGravity);
        textView.setPadding(mPaddingRect.left, mPaddingRect.top, mPaddingRect.right,
                mPaddingRect.bottom);
        textView.getViewTreeObserver()
                .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        if (isHorizontal(mGravity)) {
                            if (isLeft(mGravity, getLayoutDirection())) {
                                textView.setPivotX(-mPivotOffset);
                            } else {
                                textView.setPivotX(textView.getWidth() + mPivotOffset);
                            }
                        }
                        textView.getViewTreeObserver().removeOnPreDrawListener(this);
                        return true;
                    }
                });
        textView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));

        return textView;
    }

    /**
     * Formats the float for display
     *
     * @param temp - The current temp or NaN
     */
    @Override
    public void setTemp(float temp) {
        if (mDisplayInFahrenheit) {
            temp = convertToFahrenheit(temp);
        }
        mTextAnimator.setTemp(temp);
        if (Float.isNaN(temp)) {
            mBackgroundAnimator.hideCircle();
            return;
        }
        int color;
        if (isMinValue(temp)) {
            color = mColorStore.getMinColor();
        } else if (isMaxValue(temp)) {
            color = mColorStore.getMaxColor();
        } else {
            color = mColorStore.getColorForTemperature(temp);
        }
        if (mBackgroundAnimator.isOpen()) {
            ObjectAnimator colorAnimator =
                    ObjectAnimator.ofInt(mBackgroundColor, COLOR_PROPERTY, color);
            colorAnimator.setEvaluator((fraction, startValue, endValue) -> mColorStore
                    .lerpColor(fraction, (int) startValue, (int) endValue));
            colorAnimator.start();
        } else {
            mBackgroundColor.setColor(color);
        }

        mBackgroundAnimator.animateOpen();
    }

    @Override
    public void setDisplayInFahrenheit(boolean displayInFahrenheit) {
        mDisplayInFahrenheit = displayInFahrenheit;
    }

    boolean isMinValue(float temp) {
        return !Float.isNaN(mMinValue) && isApproxEqual(temp, mMinValue);
    }

    boolean isMaxValue(float temp) {
        return !Float.isNaN(mMaxValue) && isApproxEqual(temp, mMaxValue);
    }

    private boolean isApproxEqual(float left, float right) {
        return Math.abs(left - right) <= TEMPERATURE_EQUIVALENT_DELTA;
    }

    int getGravity() {
        return mGravity;
    }

    int getPivotOffset() {
        return mPivotOffset;
    }

    Rect getPaddingRect() {
        return mPaddingRect;
    }

    /**
     * @return propertiyId  Example: CarHvacManager.ID_ZONED_TEMP_SETPOINT (358614275)
     */
    @Override
    public int getPropertyId() {
        return mPropertyId;
    }

    /**
     * @return hvac AreaId - Example: VehicleSeat.SEAT_ROW_1_LEFT (1)
     */
    @Override
    public int getAreaId() {
        return mAreaId;
    }

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

}

