/*
 * Copyright 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.google.android.setupcompat;

import android.app.Activity;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.PersistableBundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import androidx.annotation.VisibleForTesting;
import com.google.android.setupcompat.internal.FocusChangedMetricHelper;
import com.google.android.setupcompat.internal.LifecycleFragment;
import com.google.android.setupcompat.internal.PersistableBundles;
import com.google.android.setupcompat.internal.SetupCompatServiceInvoker;
import com.google.android.setupcompat.internal.TemplateLayout;
import com.google.android.setupcompat.logging.CustomEvent;
import com.google.android.setupcompat.logging.LoggingObserver;
import com.google.android.setupcompat.logging.LoggingObserver.SetupCompatUiEvent.LayoutInflatedEvent;
import com.google.android.setupcompat.logging.MetricKey;
import com.google.android.setupcompat.logging.SetupMetricsLogger;
import com.google.android.setupcompat.partnerconfig.PartnerConfigHelper;
import com.google.android.setupcompat.template.FooterBarMixin;
import com.google.android.setupcompat.template.FooterButton;
import com.google.android.setupcompat.template.StatusBarMixin;
import com.google.android.setupcompat.template.SystemNavBarMixin;
import com.google.android.setupcompat.util.BuildCompatUtils;
import com.google.android.setupcompat.util.Logger;
import com.google.android.setupcompat.util.WizardManagerHelper;
import com.google.errorprone.annotations.CanIgnoreReturnValue;

/** A templatization layout with consistent style used in Setup Wizard or app itself. */
public class PartnerCustomizationLayout extends TemplateLayout {

  private static final Logger LOG = new Logger("PartnerCustomizationLayout");

  /**
   * Attribute indicating whether usage of partner theme resources is allowed. This corresponds to
   * the {@code app:sucUsePartnerResource} XML attribute. Note that when running in setup wizard,
   * this is always overridden to true.
   */
  private boolean usePartnerResourceAttr;

  /**
   * Attribute indicating whether using full dynamic colors or not. This corresponds to the {@code
   * app:sucFullDynamicColor} XML attribute.
   */
  private boolean useFullDynamicColorAttr;

  /**
   * Attribute indicating whether usage of dynamic is allowed. This corresponds to the existence of
   * {@code app:sucFullDynamicColor} XML attribute.
   */
  private boolean useDynamicColor;

  private Activity activity;

  private PersistableBundle layoutTypeBundle;

  @CanIgnoreReturnValue
  public PartnerCustomizationLayout(Context context) {
    this(context, 0, 0);
  }

  @CanIgnoreReturnValue
  public PartnerCustomizationLayout(Context context, int template) {
    this(context, template, 0);
  }

  @CanIgnoreReturnValue
  public PartnerCustomizationLayout(Context context, int template, int containerId) {
    super(context, template, containerId);
    init(null, R.attr.sucLayoutTheme);
  }

  @CanIgnoreReturnValue
  public PartnerCustomizationLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs, R.attr.sucLayoutTheme);
  }

  @CanIgnoreReturnValue
  public PartnerCustomizationLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(attrs, defStyleAttr);
  }

  @VisibleForTesting
  final ViewTreeObserver.OnWindowFocusChangeListener windowFocusChangeListener =
      this::onFocusChanged;

  private void init(AttributeSet attrs, int defStyleAttr) {
    if (isInEditMode()) {
      return;
    }

    TypedArray a =
        getContext()
            .obtainStyledAttributes(
                attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0);

    boolean layoutFullscreen =
        a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucLayoutFullscreen, true);

    a.recycle();

    if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP && layoutFullscreen) {
      setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
    }

    registerMixin(
        StatusBarMixin.class, new StatusBarMixin(this, activity.getWindow(), attrs, defStyleAttr));
    registerMixin(SystemNavBarMixin.class, new SystemNavBarMixin(this, activity.getWindow()));
    registerMixin(FooterBarMixin.class, new FooterBarMixin(this, attrs, defStyleAttr));

    getMixin(SystemNavBarMixin.class).applyPartnerCustomizations(attrs, defStyleAttr);

    // Override the FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, FLAG_TRANSLUCENT_STATUS,
    // FLAG_TRANSLUCENT_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN attributes of window forces
    // showing status bar and navigation bar.
    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
      activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
      activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
      activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
    }
  }

  @Override
  protected View onInflateTemplate(LayoutInflater inflater, int template) {
    if (template == 0) {
      template = R.layout.partner_customization_layout;
    }
    return inflateTemplate(inflater, 0, template);
  }

  /**
   * {@inheritDoc}
   *
   * <p>This method sets all these flags before onTemplateInflated since it will be too late and get
   * incorrect flag value on PartnerCustomizationLayout if sets them after onTemplateInflated.
   */
  @Override
  protected void onBeforeTemplateInflated(AttributeSet attrs, int defStyleAttr) {

    // Sets default value to true since this timing
    // before PartnerCustomization members initialization
    usePartnerResourceAttr = true;

    activity = lookupActivityFromContext(getContext());

    boolean isSetupFlow = WizardManagerHelper.isAnySetupWizard(activity.getIntent());

    TypedArray a =
        getContext()
            .obtainStyledAttributes(
                attrs, R.styleable.SucPartnerCustomizationLayout, defStyleAttr, 0);

    if (!a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource)) {
      // TODO: Enable Log.WTF after other client already set sucUsePartnerResource.
      LOG.e("Attribute sucUsePartnerResource not found in " + activity.getComponentName());
    }

    usePartnerResourceAttr =
        isSetupFlow
            || a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucUsePartnerResource, true);

    useDynamicColor = a.hasValue(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor);
    useFullDynamicColorAttr =
        a.getBoolean(R.styleable.SucPartnerCustomizationLayout_sucFullDynamicColor, false);

    a.recycle();

    LOG.atDebug(
        "activity="
            + activity.getClass().getSimpleName()
            + " isSetupFlow="
            + isSetupFlow
            + " enablePartnerResourceLoading="
            + enablePartnerResourceLoading()
            + " usePartnerResourceAttr="
            + usePartnerResourceAttr
            + " useDynamicColor="
            + useDynamicColor
            + " useFullDynamicColorAttr="
            + useFullDynamicColorAttr);
  }

  @Override
  protected ViewGroup findContainer(int containerId) {
    if (containerId == 0) {
      containerId = R.id.suc_layout_content;
    }
    return super.findContainer(containerId);
  }

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    LifecycleFragment.attachNow(activity);
    if (WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
      getViewTreeObserver().addOnWindowFocusChangeListener(windowFocusChangeListener);
    }
    getMixin(FooterBarMixin.class).onAttachedToWindow();
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    if (VERSION.SDK_INT >= Build.VERSION_CODES.Q
        && WizardManagerHelper.isAnySetupWizard(activity.getIntent())) {
      FooterBarMixin footerBarMixin = getMixin(FooterBarMixin.class);
      footerBarMixin.onDetachedFromWindow();
      FooterButton primaryButton = footerBarMixin.getPrimaryButton();
      FooterButton secondaryButton = footerBarMixin.getSecondaryButton();
      PersistableBundle primaryButtonMetrics =
          primaryButton != null
              ? primaryButton.getMetrics("PrimaryFooterButton")
              : PersistableBundle.EMPTY;
      PersistableBundle secondaryButtonMetrics =
          secondaryButton != null
              ? secondaryButton.getMetrics("SecondaryFooterButton")
              : PersistableBundle.EMPTY;

      PersistableBundle layoutTypeMetrics =
          (layoutTypeBundle != null) ? layoutTypeBundle : PersistableBundle.EMPTY;

      PersistableBundle persistableBundle =
          PersistableBundles.mergeBundles(
              footerBarMixin.getLoggingMetrics(),
              primaryButtonMetrics,
              secondaryButtonMetrics,
              layoutTypeMetrics);

      SetupMetricsLogger.logCustomEvent(
          getContext(),
          CustomEvent.create(MetricKey.get("SetupCompatMetrics", activity), persistableBundle));
    }
    getViewTreeObserver().removeOnWindowFocusChangeListener(windowFocusChangeListener);
  }

  /**
   * PartnerCustomizationLayout is a template layout for different type of GlifLayout. This method
   * allows each type of layout to report its "GlifLayoutType".
   */
  public void setLayoutTypeMetrics(PersistableBundle bundle) {
    this.layoutTypeBundle = bundle;
  }

  /** Returns a {@link PersistableBundle} contains key "GlifLayoutType". */
  @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
  public PersistableBundle getLayoutTypeMetrics() {
    return this.layoutTypeBundle;
  }

  public static Activity lookupActivityFromContext(Context context) {
    return PartnerConfigHelper.lookupActivityFromContext(context);
  }

  /**
   * Returns true if partner resource loading is enabled. If true, and other necessary conditions
   * for loading theme attributes are met, this layout will use customized theme attributes from OEM
   * overlays. This is intended to be used with flag-based development, to allow a flag to control
   * the rollout of partner resource loading.
   */
  protected boolean enablePartnerResourceLoading() {
    return true;
  }

  /** Returns if the current layout/activity applies partner customized configurations or not. */
  public boolean shouldApplyPartnerResource() {
    if (!enablePartnerResourceLoading()) {
      return false;
    }
    if (!usePartnerResourceAttr) {
      return false;
    }
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
      return false;
    }
    if (!PartnerConfigHelper.get(getContext()).isAvailable()) {
      return false;
    }
    return true;
  }

  /**
   * Returns {@code true} if the current layout/activity applies dynamic color. Otherwise, returns
   * {@code false}.
   */
  public boolean shouldApplyDynamicColor() {
    if (!useDynamicColor) {
      return false;
    }
    if (!BuildCompatUtils.isAtLeastS()) {
      return false;
    }
    if (!PartnerConfigHelper.get(getContext()).isAvailable()) {
      return false;
    }
    return true;
  }

  /**
   * Returns {@code true} if the current layout/activity applies full dynamic color. Otherwise,
   * returns {@code false}. This method combines the result of {@link #shouldApplyDynamicColor()}
   * and the value of the {@code app:sucFullDynamicColor}.
   */
  public boolean useFullDynamicColor() {
    return shouldApplyDynamicColor() && useFullDynamicColorAttr;
  }

  /**
   * Sets a logging observer for {@link FooterBarMixin}. The logging observer is used to log
   * impressions and clicks on the layout and footer bar buttons.
   *
   * @throws UnsupportedOperationException if the primary or secondary button has been set before
   *     the logging observer is set
   */
  public void setLoggingObserver(LoggingObserver loggingObserver) {
    getMixin(FooterBarMixin.class).setLoggingObserver(loggingObserver);
    loggingObserver.log(new LayoutInflatedEvent(this));
  }

  /**
   * Invoke the method onFocusStatusChanged when onWindowFocusChangeListener receive onFocusChanged.
   */
  private void onFocusChanged(boolean hasFocus) {
    SetupCompatServiceInvoker.get(getContext())
        .onFocusStatusChanged(
            FocusChangedMetricHelper.getScreenName(activity),
            FocusChangedMetricHelper.getExtraBundle(
                activity, PartnerCustomizationLayout.this, hasFocus));
  }
}
