package com.ratebeer.android.gui;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.SearchView;
import android.text.InputType;
import android.text.TextUtils;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.jakewharton.rxbinding.support.v7.widget.RxSearchView;
import com.ratebeer.android.R;
import com.ratebeer.android.api.Api;
import com.ratebeer.android.api.model.BeerSearchResult;
import com.ratebeer.android.api.model.BrewerySearchResult;
import com.ratebeer.android.api.model.PlaceSearchResult;
import com.ratebeer.android.db.Db;
import com.ratebeer.android.gui.lists.BeerSearchResultAdapter;
import com.ratebeer.android.gui.lists.BrewerySearchResultAdapter;
import com.ratebeer.android.gui.lists.PlaceSearchResultAdapter;
import com.ratebeer.android.gui.widget.Animations;
import com.ratebeer.android.gui.widget.ItemClickSupport;
import com.ratebeer.android.rx.RxViewPager;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import rx.Observable;

public class SearchActivity extends RateBeerActivity {

	public static final String EXTRA_BEERID = "beerId";
	public static final String EXTRA_BEERNAME = "beerName";

	private static final int TAB_BEERS = 0;
	private static final int TAB_BREWERIES = 1;
	private static final int TAB_PLACES = 2;

	private boolean modeBeerPicker;
	private SearchView searchEdit;
	private ViewPager resultsPager;
	private ProgressBar loadingProgress;
	private TextView emptyText;
	private List<Integer> tabTypes;
	private List<View> tabs;
	private List<String> tabsTitles;

	public static Intent start(Context context, String query) {
		return start(context, query, false);
	}

	public static Intent start(Context context, String query, boolean modePicker) {
		return new Intent(context, SearchActivity.class).putExtra("query", query).putExtra("modeBeerPicker", modePicker);
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_search);

		searchEdit = (SearchView) findViewById(R.id.search_text);
		TabLayout tabLayout = (TabLayout) findViewById(R.id.sliding_tabs);
		resultsPager = (ViewPager) findViewById(R.id.results_pager);
		loadingProgress = (ProgressBar) findViewById(R.id.loading_progress);
		emptyText = (TextView) findViewById(R.id.empty_text);

		setupDefaultUpButton();

		modeBeerPicker = getIntent().getBooleanExtra("modeBeerPicker", false);

		// Set up tabs
		searchEdit.setQuery(getIntent().getStringExtra("query"), false);
		searchEdit.setInputType(InputType.TYPE_CLASS_TEXT);
		tabTypes = new ArrayList<>(3);
		tabs = new ArrayList<>(3);
		tabsTitles = new ArrayList<>(3);
		addTab(TAB_BEERS, R.string.search_beers);
		if (!modeBeerPicker) {
			addTab(TAB_BREWERIES, R.string.search_breweries);
			addTab(TAB_PLACES, R.string.search_places);
		}
		RxViewPager.pageSelected(resultsPager).subscribe(this::refreshTab);
		resultsPager.setAdapter(new SearchPagerAdapter());
		tabLayout.setupWithViewPager(resultsPager);
		if (tabs.size() == 1)
			tabLayout.setVisibility(View.GONE);
		refreshTab(0);

		// When in picker mode, only search beers and set the activity result as cancelled until we actually picked a beer
		if (modeBeerPicker) {
			setResult(RESULT_CANCELED);
			if (TextUtils.isEmpty(searchEdit.getQuery())) {
				// No query given so focus on search field and do not show loading spinner until an input is given
				loadingProgress.setVisibility(View.INVISIBLE);
				searchEdit.requestFocus();
			}
		}

	}

	private void addTab(int tabType, int title) {
		RecyclerView recyclerView = (RecyclerView) getLayoutInflater().inflate(R.layout.view_feedlist, resultsPager, false);
		recyclerView.setLayoutManager(new LinearLayoutManager(this));
		tabTypes.add(tabType);
		tabs.add(recyclerView);
		tabsTitles.add(getString(title));
	}

	private void refreshTab(int position) {

		int type = tabTypes.get(position);
		RecyclerView view = ((RecyclerView) tabs.get(position));
		Animations.fadeFlipOut(loadingProgress, view, emptyText);

		Observable<CharSequence> debouncedQueries = RxSearchView.queryTextChanges(searchEdit)
				.compose(onUi())
				.debounce(666, TimeUnit.MILLISECONDS)
				.filter(query -> query.length() > 3)
				.compose(toIo());
		if (type == TAB_BEERS) {

			ItemClickSupport.addTo(view)
					.setOnItemClickListener((parent, pos, v) -> handleBeerResult(((BeerSearchResultAdapter) view.getAdapter()).get(pos)));
			debouncedQueries.switchMap(query -> Api.get().searchBeers(query.toString())
					.flatMap(result -> Db.getOfflineRatingForBeer(this, result.beerId)
							.singleOrDefault(null)
							.map(userRating -> result.withRating(userRating)))
					.toList()
					.compose(toUi())
					.doOnError(e -> Snackbar.show(SearchActivity.this, R.string.error_connectionfailure))
					.onErrorResumeNext(Observable.empty()))
					.compose(bindToLifecycle())
					.subscribe(results -> {

						view.setAdapter(new BeerSearchResultAdapter(results));
						if (results.isEmpty())
							Animations.fadeFlipOut(emptyText, view, loadingProgress);
						else
							Animations.fadeFlipOut(view, emptyText, loadingProgress);
					});

		} else if (type == TAB_BREWERIES) {

			ItemClickSupport.addTo(view)
					.setOnItemClickListener((parent, pos, v) -> handleBreweryResult(((BrewerySearchResultAdapter) view.getAdapter()).get(pos)));
			debouncedQueries.switchMap(query -> Api.get().searchBreweries(query.toString())
					.toList()
					.compose(toUi())
					.doOnError(e -> Snackbar.show(SearchActivity.this, R.string.error_connectionfailure))
					.onErrorResumeNext(Observable.empty())).compose(bindToLifecycle())
					.subscribe(results -> {

						view.setAdapter(new BrewerySearchResultAdapter(results));
						if (results.isEmpty())
							Animations.fadeFlipOut(emptyText, view, loadingProgress);
						else
							Animations.fadeFlipOut(view, emptyText, loadingProgress);
					});

		} else if (type == TAB_PLACES) {

			ItemClickSupport.addTo(view)
					.setOnItemClickListener((parent, pos, v) -> handlePlaceResult(((PlaceSearchResultAdapter) view.getAdapter()).get(pos)));
			debouncedQueries.switchMap(query -> Api.get().searchPlaces(query.toString())
					.toList()
					.compose(toUi())
					.doOnError(e -> Snackbar.show(SearchActivity.this, R.string.error_connectionfailure))
					.onErrorResumeNext(Observable.empty())).compose(bindToLifecycle())
					.subscribe(results -> {

						view.setAdapter(new PlaceSearchResultAdapter(results));
						if (results.isEmpty())
							Animations.fadeFlipOut(emptyText, view, loadingProgress);
						else
							Animations.fadeFlipOut(view, emptyText, loadingProgress);
					});

		}

	}

	private void handleBeerResult(BeerSearchResult beerSearchResult) {
		if (!modeBeerPicker) {
			startActivity(BeerActivity.start(this, beerSearchResult.beerId));
		} else {
			// Picker mode: return beer id and name as activity result extras and close this activity
			setResult(Activity.RESULT_OK, new Intent().putExtra(EXTRA_BEERID, beerSearchResult.beerId).putExtra(EXTRA_BEERNAME, beerSearchResult
					.beerName));
			finish();
		}
	}

	private void handleBreweryResult(BrewerySearchResult brewerySearchResult) {
		startActivity(BreweryActivity.start(this, brewerySearchResult.brewerId));
	}

	private void handlePlaceResult(PlaceSearchResult placeSearchResult) {
		startActivity(PlaceActivity.start(this, placeSearchResult.placeId));
	}

	private class SearchPagerAdapter extends PagerAdapter {

		@Override
		public int getCount() {
			return tabs.size();
		}

		@Override
		public CharSequence getPageTitle(int position) {
			return tabsTitles.get(position);
		}

		@Override
		public Object instantiateItem(ViewGroup container, int position) {
			container.addView(tabs.get(position));
			return tabs.get(position);
		}

		@Override
		public void destroyItem(ViewGroup container, int position, Object object) {
			container.removeView((View) object);
		}

		@Override
		public boolean isViewFromObject(View view, Object object) {
			return view == object;
		}

	}

}