package de.sebse.fuplanner;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.GravityCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;

import java.util.HashMap;
import java.util.Iterator;
import java.util.function.BiConsumer;

import de.sebse.fuplanner.fragments.CanteensFragment;
import de.sebse.fuplanner.fragments.LoginFragment;
import de.sebse.fuplanner.fragments.ModulesFragment;
import de.sebse.fuplanner.fragments.PrefsFragment;
import de.sebse.fuplanner.fragments.ScheduleFragment;
import de.sebse.fuplanner.fragments.StartupFragment;
import de.sebse.fuplanner.fragments.canteen.DaySwitcherFragment;
import de.sebse.fuplanner.fragments.moddetails.ModDetailFragment;
import de.sebse.fuplanner.services.Canteen.CanteenBrowser;
import de.sebse.fuplanner.services.Canteen.types.Canteen;
import de.sebse.fuplanner.services.GoogleAuth.GoogleAuth;
import de.sebse.fuplanner.services.KVV.KVV;
import de.sebse.fuplanner.services.KVV.types.LoginToken;
import de.sebse.fuplanner.services.KVV.types.Modules;
import de.sebse.fuplanner.tools.MainActivityListener;
import de.sebse.fuplanner.tools.RequestPermissionsResultListener;
import de.sebse.fuplanner.tools.logging.Logger;

public class MainActivity extends AppCompatActivity
        implements MainActivityListener,
        NavigationView.OnNavigationItemSelectedListener,
        LoginFragment.OnLoginFragmentInteractionListener,
        ModulesFragment.OnModulesFragmentInteractionListener,
        CanteensFragment.OnCanteensFragmentInteractionListener {

    private static final int FRAGMENT_NONE = -1;
    private static final int FRAGMENT_STARTUP = 0;
    private static final int FRAGMENT_MODULES = 1;
    private static final int FRAGMENT_MODULES_DETAILS = 2;
    private static final int FRAGMENT_LOGIN = 3;
    private static final int FRAGMENT_SCHEDULE = 4;
    private static final int FRAGMENT_CANTEENS = 5;
    private static final int FRAGMENT_CANTEENS_DETAILS = 6;
    private static final int FRAGMENT_PREFERENCES = 7;

    private static final String ARG_FRAGMENT_PAGE = "fragment_page";
    private static final String ARG_FRAGMENT_STATUS = "fragment_status";

    private FragmentManager mFragmentManager;
    private GoogleAuth mGoogleAuth;
    private KVV mKVV;
    private final Logger log = new Logger(this);
    private NavigationView mNavigationView;

    private int fragmentPage = FRAGMENT_NONE;
    private int currentPage = FRAGMENT_NONE;
    private String fragmentData = "";
    private CanteenBrowser mCanteenBrowser;
    private boolean mOfflineMode = false;
    private HashMap<String, RequestPermissionsResultListener> permissionListeners = new HashMap<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int newFragmentPage = FRAGMENT_NONE;
        String newFragmentData = "";
        if (savedInstanceState != null) {
            newFragmentPage = savedInstanceState.getInt(ARG_FRAGMENT_PAGE, fragmentPage);
            newFragmentData = savedInstanceState.getString(ARG_FRAGMENT_STATUS, fragmentData);
        }

        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        mNavigationView = findViewById(R.id.nav_view);
        mNavigationView.setNavigationItemSelectedListener(this);
        mFragmentManager = getSupportFragmentManager();

        LoginToken loginToken = getKVV().easyLogin();
        if (loginToken == null) {
            checkAndDoLogin();
        } else {
            if (newFragmentPage != FRAGMENT_LOGIN && newFragmentPage != FRAGMENT_STARTUP && newFragmentPage != FRAGMENT_NONE)
                toLoginState(loginToken, newFragmentPage, newFragmentData);
            else
                toLoginState(loginToken, getDefaultFragmentAfterLogin(), "");
        }
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        if (currentPage == FRAGMENT_SCHEDULE) {
            getMenuInflater().inflate(R.menu.options_schedule, menu);
            return true;
        }
        return false;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.refresh) {
            ScheduleFragment fragment = (ScheduleFragment) mFragmentManager.findFragmentByTag(String.valueOf(FRAGMENT_SCHEDULE));
            if (fragment != null && fragment.isVisible()) {
                fragment.invalidate(true);
            }
            return true;
        } else if (id == R.id.go_to_today) {
            ScheduleFragment fragment = (ScheduleFragment) mFragmentManager.findFragmentByTag(String.valueOf(FRAGMENT_SCHEDULE));
            if (fragment != null && fragment.isVisible()) {
                fragment.goToToday();
            }
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();
        switch (id) {
            case R.id.nav_modules:
                changeFragment(FRAGMENT_MODULES);
                break;
            case R.id.nav_schedule:
                changeFragment(FRAGMENT_SCHEDULE);
                break;
            case R.id.nav_dining:
                changeFragment(FRAGMENT_CANTEENS);
                break;
            case R.id.nav_settings:
                changeFragment(FRAGMENT_PREFERENCES);
                break;
            case R.id.nav_share:
                Intent sendIntent = new Intent();
                sendIntent.setAction(Intent.ACTION_SEND);
                sendIntent.putExtra(Intent.EXTRA_TEXT, getResources().getString(R.string.share_intent, getResources().getString(R.string.app_url)));
                sendIntent.setType("text/plain");
                startActivity(sendIntent);
                break;
            case R.id.nav_logout:
                this.getKVV().logout();
                this.getGoogleAuth().getLoginState(credentials -> {
                    if (credentials != null) {
                        this.getGoogleAuth().deleteLoginState(credentials.getUsername(), credentials.getPassword());
                    }
                    this.toLogoutState();
                });
                break;

        }
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);

        int size = mNavigationView.getMenu().size();
        for (int k = 0; k < size; k++) {
            mNavigationView.getMenu().getItem(k).setChecked(mNavigationView.getMenu().getItem(k) == item);
        }
        item.setChecked(true);

        return true;
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        this.getGoogleAuth().onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        Fragment fragment = mFragmentManager.findFragmentByTag(String.valueOf(fragmentPage));
        savedInstanceState.putInt(ARG_FRAGMENT_PAGE, fragmentPage);
        savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
        if (fragment instanceof ModDetailFragment) {
            savedInstanceState.putString(ARG_FRAGMENT_STATUS, ((ModDetailFragment) fragment).getData());
        } else {
            savedInstanceState.putString(ARG_FRAGMENT_STATUS, fragmentData);
        }
        super.onSaveInstanceState(savedInstanceState);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        for (RequestPermissionsResultListener listener: permissionListeners.values()) {
            listener.callback(requestCode, permissions, grantResults);
        }
    }

    /* --------------------------------------------*/
    /* --------------------------------------------*/
    /* --------------------------------------------*/

    public GoogleAuth getGoogleAuth() {
        if (this.mGoogleAuth == null) {
            this.mGoogleAuth = new GoogleAuth(this);
        }
        return this.mGoogleAuth;
    }

    public KVV getKVV() {
        if (this.mKVV == null) {
            this.mKVV = new KVV(this);
        }
        return this.mKVV;
    }

    public CanteenBrowser getCanteenBrowser() {
        if (this.mCanteenBrowser == null) {
            this.mCanteenBrowser = new CanteenBrowser(this);
        }
        return this.mCanteenBrowser;
    }

    private int getDefaultFragmentAfterLogin() {
        return getDefaultFragmentAfterLogin(new String[1]);
    }

    private int getDefaultFragmentAfterLogin(String[] id) {
        if (fragmentPage == FRAGMENT_NONE){
            id[0] = "";
            return FRAGMENT_MODULES;
        }
        else {
            id[0] = fragmentData;
            return fragmentPage;
        }
    }

    private void toLogoutState() {
        changeFragment(FRAGMENT_LOGIN);
        setOfflineBanner(true);
    }

    private void toLoginState(LoginToken loginToken, int newFragment, String newData) {
        if (loginToken == null) {
            toLogoutState();
        } else {
            toLoginState(loginToken.getFullname(), loginToken.getEmail(), newFragment, newData, true);
        }
    }

    private void toLoginState(String fullname, String email, int newFragment, String newData, boolean onlineMode) {
        log.d(currentPage, newFragment);
        changeFragment(newFragment, newData);

        View header = mNavigationView.getHeaderView(0);
        ((TextView) header.findViewById(R.id.login_name)).setText(fullname);
        ((TextView) header.findViewById(R.id.login_mail)).setText(email);
        setOfflineBanner(onlineMode);
    }

    private void checkAndDoLogin() {
        changeFragment(FRAGMENT_STARTUP);
        getGoogleAuth().getLoginState(credentials -> {
            if (credentials == null || credentials.getUsername() == null || credentials.getPassword() == null) {
                toLogoutState();
                return;
            }
            String[] id = new String[1];
            id[0] = "";

            int fragment = getDefaultFragmentAfterLogin(id);
            this.getKVV().login(credentials.getUsername(), credentials.getPassword(), success -> toLoginState(success, fragment , id[0]),
            error -> {
                log.e(error);
                toLogoutState();
            });
        });
    }

    private void changeFragment(int newFragment) {
        changeFragment(newFragment, "");
    }

    private void changeFragment(int newFragment, String newData) {
        //log.d("changeFragment: ", newFragment, newData);
        onTitleTextChange(R.string.courses);
        Fragment fragment;
        switch (newFragment) {
            case FRAGMENT_MODULES:
                fragment = ModulesFragment.newInstance();
                break;
            case FRAGMENT_MODULES_DETAILS:
                fragment = ModDetailFragment.newInstance(newData);
                break;
            case FRAGMENT_LOGIN:
                fragment = LoginFragment.newInstance();
                break;
            case FRAGMENT_SCHEDULE:
                fragment = ScheduleFragment.newInstance();
                break;
            case FRAGMENT_CANTEENS:
                fragment = CanteensFragment.newInstance();
                break;
            case FRAGMENT_CANTEENS_DETAILS:
                fragment = DaySwitcherFragment.newInstance(Integer.parseInt(newData));
                break;
            case FRAGMENT_PREFERENCES:
                fragment = PrefsFragment.newInstance();
                break;
            default:  // FRAGMENT_STARTUP
                fragment = StartupFragment.newInstance();
                break;
        }

        FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.fragcontainer, fragment, String.valueOf(newFragment));
        fragmentTransaction.commit();

        if (newFragment == FRAGMENT_STARTUP) {
            findViewById(R.id.app_bar_layout).setVisibility(View.GONE);
        } else {
            findViewById(R.id.app_bar_layout).setVisibility(View.VISIBLE);
        }
        // switch to logout
        if ((currentPage != FRAGMENT_STARTUP && currentPage != FRAGMENT_LOGIN) && (newFragment == FRAGMENT_STARTUP || newFragment == FRAGMENT_LOGIN)) {
            View header = mNavigationView.getHeaderView(0);
            //header.findViewById(R.id.imageView).setVisibility(View.GONE);
            header.findViewById(R.id.login_name).setVisibility(View.GONE);
            header.findViewById(R.id.login_mail).setVisibility(View.GONE);
            header.findViewById(R.id.btn_login_page).setVisibility(View.VISIBLE);
            header.findViewById(R.id.btn_login_page).setOnClickListener(v -> {
                DrawerLayout drawer = findViewById(R.id.drawer_layout);
                if (drawer.isDrawerOpen(GravityCompat.START)) {
                    drawer.closeDrawer(GravityCompat.START);
                }
                changeFragment(FRAGMENT_LOGIN);
            });
            mNavigationView.getMenu().clear();
            mNavigationView.inflateMenu(R.menu.activity_main_drawer);
            afterAnyMenuInflate();
        } // switch to login
        else if (
                (currentPage == FRAGMENT_STARTUP || currentPage == FRAGMENT_LOGIN || currentPage == FRAGMENT_NONE) &&
                (newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_LOGIN && getKVV().isLoggedIn())
                ) {
            View header = mNavigationView.getHeaderView(0);
            //header.findViewById(R.id.imageView).setVisibility(View.VISIBLE);
            header.findViewById(R.id.login_name).setVisibility(View.VISIBLE);
            header.findViewById(R.id.login_mail).setVisibility(View.VISIBLE);
            header.findViewById(R.id.btn_login_page).setVisibility(View.GONE);
            mNavigationView.getMenu().clear();
            mNavigationView.inflateMenu(R.menu.activity_main_drawer_login);
            mNavigationView.setCheckedItem(R.id.nav_modules);
            afterAnyMenuInflate();
            getKVV().getModuleList(success -> {
                int i = 0;
                for (Iterator<Modules.Module> it = success.latestSemesterIterator(); it.hasNext(); ) {
                    Modules.Module module = it.next();
                    MenuItem menuItem = mNavigationView.getMenu().add(Menu.NONE, Menu.NONE, 101 + i, module.title);
                    menuItem.setOnMenuItemClickListener(item -> {
                        onModulesFragmentInteraction(module.getID());
                        return false;
                    });
                    i++;
                }
            }, log::e);
        }
        if (newFragment == FRAGMENT_MODULES_DETAILS) {
            getKVV().getModule(newData, success -> {
                int size = mNavigationView.getMenu().size();
                //noinspection ConstantConditions
                String title = success == null ? null : success.title;
                for (int k = 0; k < size; k++) {
                    mNavigationView.getMenu().getItem(k).setChecked(mNavigationView.getMenu().getItem(k).getTitle().equals(title));
                }
            }, log::e);
        }
        if (newFragment == FRAGMENT_CANTEENS_DETAILS) {
            getCanteenBrowser().getCanteens(success -> {
                int size = mNavigationView.getMenu().size();
                Canteen canteen = success.getCanteen(Integer.parseInt(newData));
                //noinspection ConstantConditions
                String title = canteen == null ? null : canteen.getName();
                for (int k = 0; k < size; k++) {
                    mNavigationView.getMenu().getItem(k).setChecked(mNavigationView.getMenu().getItem(k).getTitle().equals(title));
                }
            }, log::e);
        }

        if (newFragment != FRAGMENT_STARTUP && newFragment != FRAGMENT_NONE && newFragment != FRAGMENT_LOGIN) {
            this.fragmentPage = newFragment;
            this.fragmentData = newData;
        }
        this.currentPage = newFragment;
        invalidateOptionsMenu();
    }

    private void setOfflineBanner(boolean onlineMode) {
        View offline_header = findViewById(R.id.offline_msg);
        if (onlineMode)
            offline_header.setVisibility(View.GONE);
        else
            offline_header.setVisibility(View.VISIBLE);
        mOfflineMode = !onlineMode;
    }

    private void setRefreshFailedBanner(boolean refreshFailed) {
        View viewNoConnection = findViewById(R.id.no_connection_msg);
        if (!mOfflineMode && refreshFailed)
            viewNoConnection.setVisibility(View.VISIBLE);
        else
            viewNoConnection.setVisibility(View.GONE);

    }


    private void afterAnyMenuInflate() {
        getCanteenBrowser().getCanteens(success -> {
            int i = 0;
            for (Canteen canteen: success) {
                MenuItem menuItem = mNavigationView.getMenu().add(Menu.NONE, Menu.NONE, 201 + i, canteen.getName());
                menuItem.setOnMenuItemClickListener(item -> {
                    onCanteensFragmentInteraction(canteen.getId());
                    return false;
                });
                i++;
            }
        }, log::e);
    }







    public void onLoginFragmentInteraction(LoginToken loginToken, boolean onlineMode) {
        toLoginState(loginToken.getFullname(), loginToken.getEmail(), getDefaultFragmentAfterLogin(), "", onlineMode);
    }

    public void onModulesFragmentInteraction(final String itemID) {
        changeFragment(FRAGMENT_MODULES_DETAILS, itemID);
    }

    public void onCanteensFragmentInteraction(final int itemID) {
        changeFragment(FRAGMENT_CANTEENS_DETAILS, String.valueOf(itemID));
    }

    public void onTitleTextChange(String newTitle) {
        setTitle(newTitle);
    }

    public void onTitleTextChange(@StringRes int titleId) {
        setTitle(titleId);
    }

    public void loginTokenInvalid(boolean doPrecheck) {
        getKVV().invalidate();
        checkAndDoLogin();
    }

    public void refreshFailed(boolean isFailed) {
        setRefreshFailedBanner(isFailed);
    }

    public void addRequestPermissionsResultListener(RequestPermissionsResultListener listener, String id) {
        permissionListeners.put(id, listener);
    }

    public void removeRequestPermissionsResultListener(String id) {
        permissionListeners.remove(id);
    }
}
