package com.airbnb.android.react.navigation;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.util.ArrayMap;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;

import com.airbnb.android.R;
import com.facebook.react.bridge.ReadableMap;

import java.util.Map;
import java.util.Stack;

public class ReactNativeTabActivity extends ReactAwareActivity
        implements ScreenCoordinatorComponent, BottomNavigationView.OnNavigationItemSelectedListener {
  private static final String TAG = ReactNativeTabActivity.class.getSimpleName();

  private ViewGroup.OnHierarchyChangeListener reactViewChangeListener = new ViewGroup.OnHierarchyChangeListener() {
    @Override
    public void onChildViewAdded(View parent, View child) {
      Log.d(TAG, "onChildViewAdded");
      if (child instanceof ViewGroup) {
        Log.d(TAG, "onChildViewAdded: adding child listener");
        // onChildViewAdded is a shallow listener, so we want to recursively listen
        // to all children that are ViewGroups as well. For a tab scene, the view
        // hierarchy should not be very deep, so this seems okay to me. We should be
        // careful though.
        ((ViewGroup)child).setOnHierarchyChangeListener(reactViewChangeListener);
      }
      debouncedRefreshTabs();
    }

    @Override
    public void onChildViewRemoved(View parent, View child) {
      Log.d(TAG, "onChildViewRemoved");
      // TODO(lmr): is there any reason we would need to clean up the onHierarchyChangeListener here?
      debouncedRefreshTabs();
    }
  };

  private TabCoordinator tabCoordinator;
  private ReactBottomNavigation bottomNavigationView;
  private ViewGroup tabConfigContainer;
  private boolean tabViewsIsDirty = false;
  private Map<Integer, TabView> tabViews = new ArrayMap<>();
  private ReadableMap prevTabBarConfig = ConversionUtil.EMPTY_MAP;
  private ReadableMap renderedTabBarConfig = ConversionUtil.EMPTY_MAP;

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_tab_2);
    bottomNavigationView = (ReactBottomNavigation) findViewById(R.id.bottom_navigation);
    bottomNavigationView.setOnNavigationItemSelectedListener(this);
    tabConfigContainer = (ViewGroup) findViewById(R.id.tab_config_container);
    tabConfigContainer.setOnHierarchyChangeListener(reactViewChangeListener);
    ScreenCoordinatorLayout container = (ScreenCoordinatorLayout) findViewById(R.id.content);
    tabCoordinator = new TabCoordinator(this, container, savedInstanceState);

    ReactNativeFragment tabConfigFragment = ReactNativeFragment.newInstance("TabScreen", null);
    getSupportFragmentManager().beginTransaction()
            .add(R.id.tab_config_container, tabConfigFragment)
            .commitNow();
  }

  @Override
  public ScreenCoordinator getScreenCoordinator() {
    return tabCoordinator.getCurrentScreenCoordinator();
  }

  @Override
  public void onBackPressed() {
    if (!tabCoordinator.onBackPressed()) {
      super.onBackPressed();
    }
  }

  private void debouncedRefreshTabs() {
    if (tabViewsIsDirty) {
      return;
    }
    tabViewsIsDirty = true;
    tabConfigContainer.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
      @Override
      public boolean onPreDraw() {
        tabViewsIsDirty = false;
        tabConfigContainer.getViewTreeObserver().removeOnPreDrawListener(this);
        refreshTabs();
        return true;
      }
    });
  }

  private void refreshTabs() {
    Log.d(TAG, "refreshTabs");
    traverseTabs();
    notifyTabsHaveChanged();
  }

  private void traverseTabs() {
    Stack<ViewGroup> stack = new Stack<>();
    stack.push(tabConfigContainer);

    prevTabBarConfig = renderedTabBarConfig;
    renderedTabBarConfig = ConversionUtil.EMPTY_MAP;
    tabViews = new ArrayMap<>();

    while (!stack.empty()) {
      ViewGroup view = stack.pop();
      int childCount = view.getChildCount();
      for (int i = 0; i < childCount; ++i) {
        View child = view.getChildAt(i);

        if (child instanceof TabView) {
          tabViews.put(child.getId(), (TabView) child);
        } else if (child instanceof TabBarView) {
          TabBarView tabBarView = (TabBarView) child;
          renderedTabBarConfig = ConversionUtil.combine(renderedTabBarConfig, tabBarView.getConfig());
          stack.push(tabBarView);
        } else if (child instanceof ViewGroup) {
          stack.push((ViewGroup) child);
        }
      }
    }
  }

  private void notifyTabsHaveChanged() {
    Log.d(TAG, "notifyTabsHaveChanged");
    Menu menu = bottomNavigationView.getMenu();

    getImplementation().reconcileTabBarProperties(
            bottomNavigationView,
            menu,
            prevTabBarConfig,
            renderedTabBarConfig
    );

    menu.clear();
    bottomNavigationView.clearIconHolders();

    int index = 0;
    for (TabView tab : tabViews.values()) {
      getImplementation().makeTabItem(
              bottomNavigationView,
              menu,
              index,
              tab.getId(),
              tab.getRenderedConfig()
      );
      index++;
    }

    if (tabViews.size() > 0) {
      TabView view = tabViews.values().iterator().next();
      tabCoordinator.showTab(view.getFragment(), view.getId());
    }
  }

  @Override
  public boolean onNavigationItemSelected(@NonNull MenuItem item) {
    Log.d(TAG, "onNavigationItemSelected");
    TabView tab = tabViews.get(item.getItemId());
    if (tab != null) {
      Log.d(TAG, "found tab");
      Fragment fragment = tab.getFragment();
      tabCoordinator.showTab(fragment, item.getItemId());
    }
    return true;
  }
}