package com.lemenzo.gallery.search;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;

import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import com.lemenzo.gallery.R;
import com.lemenzo.gallery.gallery.GalleryAdapter;
import com.lemenzo.gallery.model.MediaItem;
import com.lemenzo.gallery.ui.MediaGridDecoration;
import com.lemenzo.gallery.ui.RecyclerTuning;
import com.lemenzo.gallery.ui.SystemInsets;
import com.lemenzo.gallery.util.BackgroundExecutors;
import com.lemenzo.gallery.viewer.ViewerActivity;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

public final class SearchActivity extends Activity {
    public static final String EXTRA_INITIAL_QUERY = "initial_query";
    private static final long SEARCH_DEBOUNCE_MS = 180L;

    private final ExecutorService executor = BackgroundExecutors.single("LemenzoGallerySearch");
    private final Handler handler = new Handler(Looper.getMainLooper());
    private final AtomicInteger searchGeneration = new AtomicInteger();

    private SearchViewModel viewModel;
    private SearchAdapter adapter;
    private View empty;
    private View limitNotice;
    private Runnable pendingSearch;
    private Future<?> searchTask;
    private volatile boolean loaded;

    @Override
    public void onCreate(Bundle state) {
        super.onCreate(state);
        setContentView(R.layout.activity_search);
        SystemInsets.apply(this);
        findViewById(R.id.back_button).setOnClickListener(v -> finish());

        RecyclerView results = findViewById(R.id.results);
        empty = findViewById(R.id.empty_state);
        limitNotice = findViewById(R.id.search_limit_notice);
        int columns = 3;
        GridLayoutManager layoutManager = new GridLayoutManager(this, columns);
        adapter = new SearchAdapter(this, new GalleryAdapter.Listener() {
            @Override public void onOpen(MediaItem item) {
                startActivity(ViewerActivity.intent(SearchActivity.this, item));
            }
            @Override public void onSelectionChanged(int count) {}
        });
        layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
            @Override public int getSpanSize(int position) {
                return adapter.spanSize(position, columns);
            }
        });
        results.setLayoutManager(layoutManager);
        results.addItemDecoration(new MediaGridDecoration(2, columns));
        results.setAdapter(adapter);
        RecyclerTuning.applyMediaGrid(results);

        EditText searchInput = findViewById(R.id.search_input);
        viewModel = new SearchViewModel(this);
        executor.execute(() -> {
            viewModel.load();
            loaded = true;
            runOnUiThread(() -> scheduleSearch(searchInput.getText().toString(), false));
        });

        String initial = getIntent().getStringExtra(EXTRA_INITIAL_QUERY);
        if (initial != null) searchInput.setText(initial);
        searchInput.addTextChangedListener(new TextWatcher() {
            @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
            @Override public void onTextChanged(CharSequence s, int start, int before, int count) {
                scheduleSearch(s.toString(), true);
            }
            @Override public void afterTextChanged(Editable editable) {}
        });
    }

    private void scheduleSearch(String query, boolean debounce) {
        final int generation = searchGeneration.incrementAndGet();
        if (pendingSearch != null) handler.removeCallbacks(pendingSearch);

        pendingSearch = () -> {
            if (!loaded || isFinishing() || isDestroyed()) return;
            if (searchTask != null) searchTask.cancel(true);
            searchTask = executor.submit(() -> {
                SearchViewModel.Result result = viewModel.search(query);
                List<MediaItem> items = result.items;
                if (generation != searchGeneration.get()) return;
                runOnUiThread(() -> {
                    if (generation != searchGeneration.get() || isFinishing() || isDestroyed()) return;
                    adapter.submit(items, false);
                    empty.setVisibility(items.isEmpty() ? View.VISIBLE : View.GONE);
                    limitNotice.setVisibility(result.truncated ? View.VISIBLE : View.GONE);
                });
            });
        };

        handler.postDelayed(pendingSearch, debounce ? SEARCH_DEBOUNCE_MS : 0L);
    }

    @Override
    protected void onDestroy() {
        searchGeneration.incrementAndGet();
        if (pendingSearch != null) handler.removeCallbacks(pendingSearch);
        if (searchTask != null) searchTask.cancel(true);
        searchTask = null;
        if (adapter != null) adapter.close();
        executor.shutdownNow();
        super.onDestroy();
    }
}
