package com.swmansion.rnscreens;

import android.content.Context;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment;

import com.facebook.react.bridge.JSApplicationIllegalArgumentException;
import com.facebook.react.views.text.ReactFontManager;

import java.util.ArrayList;

public class ScreenStackHeaderConfig extends ViewGroup {

  private final ArrayList<ScreenStackHeaderSubview> mConfigSubviews = new ArrayList<>(3);
  private String mTitle;
  private int mTitleColor;
  private String mTitleFontFamily;
  private float mTitleFontSize;
  private int mBackgroundColor;
  private boolean mIsHidden;
  private boolean mIsBackButtonHidden;
  private boolean mIsShadowHidden;
  private boolean mDestroyed;
  private int mTintColor;
  private final Toolbar mToolbar;

  private boolean mIsAttachedToWindow = false;

  private OnClickListener mBackClickListener = new OnClickListener() {
    @Override
    public void onClick(View view) {
      ScreenStack stack = getScreenStack();
      ScreenStackFragment fragment = getScreenFragment();
      if (stack.getRootScreen() == fragment.getScreen()) {
        Fragment parentFragment = fragment.getParentFragment();
        if (parentFragment instanceof ScreenStackFragment) {
          ((ScreenStackFragment) parentFragment).dismiss();
        }
      } else {
        fragment.dismiss();
      }
    }
  };

  public ScreenStackHeaderConfig(Context context) {
    super(context);
    setVisibility(View.GONE);

    mToolbar = new Toolbar(context);
    // reset content insets to be 0 to allow react position custom navbar views. Note that this does
    // not affect platform native back button as toolbar does not apply left inset when navigation
    // button is specified
    mToolbar.setContentInsetsAbsolute(0, 0);

    // set primary color as background by default
    TypedValue tv = new TypedValue();
    if (context.getTheme().resolveAttribute(android.R.attr.colorPrimary, tv, true)) {
      mToolbar.setBackgroundColor(tv.data);
    }
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    // no-op
  }

  public void destroy() {
    mDestroyed = true;
  }

  @Override
  protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    mIsAttachedToWindow = true;
    onUpdate();
  }

  @Override
  protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    mIsAttachedToWindow = false;
  }

  private Screen getScreen() {
    ViewParent screen = getParent();
    if (screen instanceof Screen) {
      return (Screen) screen;
    }
    return null;
  }

  private ScreenStack getScreenStack() {
    Screen screen = getScreen();
    if (screen  != null) {
      ScreenContainer container = screen.getContainer();
      if (container instanceof ScreenStack) {
        return (ScreenStack) container;
      }
    }
    return null;
  }

  private ScreenStackFragment getScreenFragment() {
    ViewParent screen = getParent();
    if (screen instanceof Screen) {
      Fragment fragment = ((Screen) screen).getFragment();
      if (fragment instanceof ScreenStackFragment) {
        return (ScreenStackFragment) fragment;
      }
    }
    return null;
  }

  public void onUpdate() {
    Screen parent = (Screen) getParent();
    final ScreenStack stack = getScreenStack();
    boolean isTop = stack == null ? true : stack.getTopScreen() == parent;

    if (!mIsAttachedToWindow || !isTop || mDestroyed) {
      return;
    }

    AppCompatActivity activity = (AppCompatActivity) getScreenFragment().getActivity();
    if (activity == null) {
      return;
    }

    if (mIsHidden) {
      if (mToolbar.getParent() != null) {
        getScreenFragment().removeToolbar();
      }
      return;
    }

    if (mToolbar.getParent() == null) {
      getScreenFragment().setToolbar(mToolbar);
    }

    activity.setSupportActionBar(mToolbar);
    ActionBar actionBar = activity.getSupportActionBar();

    // hide back button
    actionBar.setDisplayHomeAsUpEnabled(getScreenFragment().canNavigateBack() ? !mIsBackButtonHidden : false);

    // when setSupportActionBar is called a toolbar wrapper gets initialized that overwrites
    // navigation click listener. The default behavior set in the wrapper is to call into
    // menu options handlers, but we prefer the back handling logic to stay here instead.
    mToolbar.setNavigationOnClickListener(mBackClickListener);


    // shadow
    getScreenFragment().setToolbarShadowHidden(mIsShadowHidden);

    // title
    actionBar.setTitle(mTitle);
    TextView titleTextView = getTitleTextView();
    if (mTitleColor != 0) {
      mToolbar.setTitleTextColor(mTitleColor);
    }
    if (titleTextView != null) {
      if (mTitleFontFamily != null) {
        titleTextView.setTypeface(ReactFontManager.getInstance().getTypeface(
                mTitleFontFamily, 0, getContext().getAssets()));
      }
      if (mTitleFontSize > 0) {
        titleTextView.setTextSize(mTitleFontSize);
      }
    }

    // background
    if (mBackgroundColor != 0) {
      mToolbar.setBackgroundColor(mBackgroundColor);
    }

    // color
    if (mTintColor != 0) {
      Drawable navigationIcon = mToolbar.getNavigationIcon();
      if (navigationIcon != null) {
        navigationIcon.setColorFilter(mTintColor, PorterDuff.Mode.SRC_ATOP);
      }
    }

    // subviews
    for (int i = mToolbar.getChildCount() - 1; i >= 0; i--) {
      if (mToolbar.getChildAt(i) instanceof ScreenStackHeaderSubview) {
        mToolbar.removeViewAt(i);
      }
    }
    for (int i = 0, size = mConfigSubviews.size(); i < size; i++) {
      ScreenStackHeaderSubview view = mConfigSubviews.get(i);
      ScreenStackHeaderSubview.Type type = view.getType();

      if (type == ScreenStackHeaderSubview.Type.BACK) {
        // we special case BACK button header config type as we don't add it as a view into toolbar
        // but instead just copy the drawable from imageview that's added as a first child to it.
        View firstChild = view.getChildAt(0);
        if (!(firstChild instanceof ImageView)) {
          throw new JSApplicationIllegalArgumentException("Back button header config view should have Image as first child");
        }
        actionBar.setHomeAsUpIndicator(((ImageView) firstChild).getDrawable());
        continue;
      }

      Toolbar.LayoutParams params =
              new Toolbar.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);

      switch (type) {
        case LEFT:
          // when there is a left item we need to disable navigation icon
          // we also hide title as there is no other way to display left side items
          mToolbar.setNavigationIcon(null);
          mToolbar.setTitle(null);
          params.gravity = Gravity.LEFT;
          break;
        case RIGHT:
          params.gravity = Gravity.RIGHT;
          break;
        case CENTER:
          params.width = LayoutParams.MATCH_PARENT;
          params.gravity = Gravity.CENTER_HORIZONTAL;
          mToolbar.setTitle(null);
          break;
      }

      view.setLayoutParams(params);
      mToolbar.addView(view);
    }
  }

  private void maybeUpdate() {
    if (getParent() != null && !mDestroyed) {
      onUpdate();
    }
  }

  public ScreenStackHeaderSubview getConfigSubview(int index) {
    return mConfigSubviews.get(index);
  }

  public int getConfigSubviewsCount() {
    return mConfigSubviews.size();
  }

  public void removeConfigSubview(int index) {
    mConfigSubviews.remove(index);
    maybeUpdate();
  }

  public void removeAllConfigSubviews() {
    mConfigSubviews.clear();
    maybeUpdate();
  }

  public void addConfigSubview(ScreenStackHeaderSubview child, int index) {
    mConfigSubviews.add(index, child);
    maybeUpdate();
  }

  private TextView getTitleTextView() {
    for (int i = 0, size = mToolbar.getChildCount(); i < size; i++) {
      View view = mToolbar.getChildAt(i);
      if (view instanceof TextView) {
        TextView tv = (TextView) view;
        if (tv.getText().equals(mToolbar.getTitle())) {
          return tv;
        }
      }
    }
    return null;
  }

  public void setTitle(String title) {
    mTitle = title;
  }

  public void setTitleFontFamily(String titleFontFamily) {
    mTitleFontFamily = titleFontFamily;
  }

  public void setTitleFontSize(float titleFontSize) {
    mTitleFontSize = titleFontSize;
  }

  public void setTitleColor(int color) {
    mTitleColor = color;
  }

  public void setTintColor(int color) {
    mTintColor = color;
  }

  public void setBackgroundColor(int color) {
    mBackgroundColor = color;
  }

  public void setHideShadow(boolean hideShadow) {
    mIsShadowHidden = hideShadow;
  }

  public void setHideBackButton(boolean hideBackButton) {
    mIsBackButtonHidden = hideBackButton;
  }

  public void setHidden(boolean hidden) {
    mIsHidden = hidden;
  }
}
