/*
 * Copyright 2019 Hippo Seven
 *
 * 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.hippo.ehviewer.widget;

import android.content.Context;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.view.DisplayCutoutCompat;

import com.hippo.ehviewer.R;
import com.hippo.yorozuya.ObjectUtils;

public class GalleryHeader extends ViewGroup {
    private final Rect batteryRect = new Rect();
    private final Rect progressRect = new Rect();
    private final Rect clockRect = new Rect();
    private final int[] location = new int[2];
    private DisplayCutoutCompat displayCutout;
    private int topInsets = 0;
    private View battery;
    private View progress;
    private View clock;
    private int lastX = 0;
    private int lastY = 0;

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

    public void setDisplayCutout(@Nullable DisplayCutoutCompat displayCutout) {
        if (!ObjectUtils.equal(this.displayCutout, displayCutout)) {
            this.displayCutout = displayCutout;
            requestLayout();
        }
    }

    public void setTopInsets(int topInsets) {
        if (this.topInsets != topInsets) {
            this.topInsets = topInsets;
            requestLayout();
        }
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        battery = findViewById(R.id.battery);
        progress = findViewById(R.id.progress);
        clock = findViewById(R.id.clock);
    }

    private void measureChild(Rect rect, View view, int width, int paddingLeft, int paddingRight) {
        int left;
        MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
        if (view == battery) {
            left = paddingLeft + lp.leftMargin;
        } else if (view == progress) {
            left = paddingLeft + (width - paddingLeft - paddingRight) / 2 - view.getMeasuredWidth() / 2;
        } else {
            left = width - paddingRight - lp.rightMargin - view.getMeasuredWidth();
        }
        rect.set(left, lp.topMargin + topInsets, left + view.getMeasuredWidth(), lp.topMargin + topInsets + view.getMeasuredHeight());
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    private boolean offsetVertically(Rect rect, View view, int width) {
        int offset = 0;

        measureChild(rect, view, width, 0, 0);
        rect.offset(lastX, lastY);

        for (Rect notch : displayCutout.getBoundingRects()) {
            if (Rect.intersects(notch, rect)) {
                offset = Math.max(offset, notch.bottom - lastY);
            }
        }

        if (offset != 0) {
            rect.offset(-lastX, -lastY);
            rect.offset(0, offset);
            return true;
        } else {
            return false;
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    private int getOffsetLeft(Rect rect, View view, int width) {
        int offset = 0;

        measureChild(rect, view, width, 0, 0);
        rect.offset(lastX, lastY);

        for (Rect notch : displayCutout.getBoundingRects()) {
            if (Rect.intersects(notch, rect)) {
                offset = Math.max(offset, notch.right - lastX);
            }
        }

        return offset;
    }

    @RequiresApi(api = Build.VERSION_CODES.P)
    private int getOffsetRight(Rect rect, View view, int width) {
        int offset = 0;

        measureChild(rect, view, width, 0, 0);
        rect.offset(lastX, lastY);

        for (Rect notch : displayCutout.getBoundingRects()) {
            if (Rect.intersects(notch, rect)) {
                offset = Math.max(offset, lastX + width - notch.left);
            }
        }

        return offset;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
            throw new IllegalStateException();
        }
        int width = MeasureSpec.getSize(widthMeasureSpec);

        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            height = Math.max(height, child.getMeasuredHeight() + lp.topMargin + topInsets);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
            // Check progress covered
            if (offsetVertically(progressRect, progress, width)) {
                offsetVertically(batteryRect, battery, width);
                offsetVertically(clockRect, clock, width);
                height = Math.max(progressRect.bottom, Math.max(batteryRect.bottom, clockRect.bottom));
            } else {
                // Clamp left and right
                int paddingLeft = getOffsetLeft(batteryRect, battery, width);
                int paddingRight = getOffsetRight(clockRect, clock, width);
                measureChild(batteryRect, battery, width, paddingLeft, paddingRight);
                measureChild(progressRect, progress, width, paddingLeft, paddingRight);
                measureChild(clockRect, clock, width, paddingLeft, paddingRight);
            }
        } else {
            measureChild(batteryRect, battery, width, 0, 0);
            measureChild(progressRect, progress, width, 0, 0);
            measureChild(clockRect, clock, width, 0, 0);
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        battery.layout(batteryRect.left, batteryRect.top, batteryRect.right, batteryRect.bottom);
        progress.layout(progressRect.left, progressRect.top, progressRect.right, progressRect.bottom);
        clock.layout(clockRect.left, clockRect.top, clockRect.right, clockRect.bottom);

        getLocationOnScreen(location);
        if (lastX != location[0] || lastY != location[1]) {
            lastX = location[0];
            lastY = location[1];
            requestLayout();
        }
    }

    @Override
    public MarginLayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return p instanceof MarginLayoutParams;
    }

    @Override
    protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
        if (lp instanceof MarginLayoutParams) {
            return new MarginLayoutParams((MarginLayoutParams) lp);
        }
        return new MarginLayoutParams(lp);
    }
}
