package edu.whs.spin2018.baoapp.views.menues;

import android.content.ComponentName;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.IBinder;
import android.support.constraint.Group;
import android.support.v4.app.DialogFragment;
import android.support.v4.content.ContextCompat;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;

import edu.whs.spin2018.baoapp.R;
import edu.whs.spin2018.baoapp.presenter.SpieleinstellungenPresenter;
import edu.whs.spin2018.baoapp.service.BaoService;
import edu.whs.spin2018.baoapp.service.Konstanten;
import edu.whs.spin2018.baoapp.model.remote.spieleinstellungen.SpieleinstellungenRemoteDataSource;
import edu.whs.spin2018.baoapp.model.remote.spieleinstellungen.SpieleinstellungenServiceInteractor;
import edu.whs.spin2018.baoapp.utilities.SharedPrefUtils;
import edu.whs.spin2018.baoapp.utilities.ViewUtils;
import edu.whs.spin2018.baoapp.views.OnlineActivity;
import edu.whs.spin2018.baoapp.views.spiel.BaoSpielActivity;

/**
 * Diese Klasse ermöglicht die Einigung der Spieleinstellungen zweier Spieler, wenn sich diese mit
 * einenander verunden haben. Wählt ein Spieler eine Spieleinstellung aus, wird diese über den
 * Server an den verbundenen Gegenspieler gesendet und dort angezeigt.
 */
public class SpieleinstellungenActivity extends OnlineActivity implements SpieleinstellungenContract.View,
        AdapterView.OnItemSelectedListener, SpieleinstellungenVerlassenDialog.OnAuswahlGetroffenListener,
        SpieleinstellungenWurdenVerlassenDialog.OnDialogBestaetigtListener {

    private static final String TAG = "SpieleinstellungenTAG";

    private SpieleinstellungenContract.Presenter presenter;

    private String ausgewaehlterZeittyp = Konstanten.SPIELZEIT_OHNE_ZEIT;

    private ArrayAdapter<CharSequence> adapterMinuten;
    private ArrayAdapter<CharSequence> adapterSekunden;

    private DialogFragment mAktuellerDialog;

    /**
     * View-Variablen
     */

    private Group mSpinner;

    private Button btnOhneZeit;
    private Button btnRundenzeit;
    private Button btnSpieldauer;

    private ImageView mSteinSpielerOhneZeit;
    private ImageView mSteinSpielerRundenzeit;
    private ImageView mSteinSpielerSpieldauer;

    private ImageView mSteinGegnerOhneZeit;
    private ImageView mSteinGegnerRundenzeit;
    private ImageView mSteinGegnerSpieldauer;

    private Spinner mSpinnerMinutenSpieler;
    private Spinner mSpinnerSekundenSpieler;
    private Spinner mSpinnerMinutenGegner;
    private Spinner mSpinnerSekundenGegner;

    private TextView mTvSpielernameLabel;
    private TextView mTvGegnernameLabel;
    private TextView mTvZeitLabel;

    private Button mButtonSpielerBereit;
    private Button mButtonGegnerBereit;
    private String gegnername;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_spieleinstellungen);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        // View-Elemente werden initialisiert.
        initView();
        // Listener für die Buttons werden festgelegt.
        setzeButtonKlickListener();
        // Listener für die Spinner werden festgelegt.
        setzeSpinnerListener();
        // Listener für die Bereit-Buttons werden festgelegt.
        setzeBereitButtonListener();
    }

    /**
     * Fuellt den Spinner mit Werten fuer die Rundenzeit.
     */
    private void ladeRundenzeitSpinner() {
        runOnUiThread(() -> {
            adapterMinuten = ArrayAdapter.createFromResource(this,
                    R.array.minutesRoundTime, android.R.layout.simple_spinner_item);
            adapterMinuten.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerMinutenGegner.setAdapter(adapterMinuten);
            mSpinnerMinutenSpieler.setAdapter(adapterMinuten);

            adapterSekunden = ArrayAdapter.createFromResource(this,
                    R.array.secondsRoundTime, android.R.layout.simple_spinner_item);
            adapterSekunden.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerSekundenGegner.setAdapter(adapterSekunden);
            mSpinnerSekundenSpieler.setAdapter(adapterSekunden);
        });
    }

    /**
     * Fuellt den Spinner mit Werten fuer die Spieldauer.
     */
    public void ladeSpieldauerSpinner() {
        runOnUiThread(() -> {
            ArrayAdapter<CharSequence> adapterMinuten = ArrayAdapter.createFromResource(this,
                    R.array.minutesPlayTime, android.R.layout.simple_spinner_item);
            adapterMinuten.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerMinutenGegner.setAdapter(adapterMinuten);
            mSpinnerMinutenSpieler.setAdapter(adapterMinuten);

            ArrayAdapter<CharSequence> adapterSekunden = ArrayAdapter.createFromResource(this,
                    R.array.secondsRoundTimeWithNull, android.R.layout.simple_spinner_item);
            adapterSekunden.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            mSpinnerSekundenGegner.setAdapter(adapterSekunden);
            mSpinnerSekundenSpieler.setAdapter(adapterSekunden);
        });
    }

    @Override
    public void zeigeSpinner(String zeittyp) {
        switch (zeittyp) {
            case Konstanten.SPIELZEIT_RUNDENDAUER:
                ladeRundenzeitSpinner();
                mSpinnerSekundenSpieler.setEnabled(true);
                break;
            case Konstanten.SPIELZEIT_SPIELDAUER:
                ladeSpieldauerSpinner();
                mSpinnerSekundenSpieler.setEnabled(false);
                break;
        }

        runOnUiThread(() -> {
            mSpinner.setVisibility(View.VISIBLE);
            mSpinner.updatePreLayout(findViewById(R.id.spieleinstellungen));
        });
    }

    @Override
    public void versteckeSpinner() {
        runOnUiThread(() -> {
            mSpinner.setVisibility(View.INVISIBLE);
            mSpinner.updatePreLayout(findViewById(R.id.spieleinstellungen));
        });
    }

    @Override
    public void setzeBereitButtonsAktiv(boolean aktiv) {
        runOnUiThread(() -> {
            mButtonSpielerBereit.setText(getString(R.string.nicht_bereit_label));
            mButtonGegnerBereit.setText(getString(R.string.nicht_bereit_label));
            mButtonSpielerBereit.setClickable(aktiv);
            if (aktiv) {
                mButtonSpielerBereit.setBackground(gibButtonRot());
                mButtonGegnerBereit.setBackground(gibButtonRot());
            } else {
                mButtonSpielerBereit.setBackground(gibButtonGrau());
                mButtonGegnerBereit.setBackground(gibButtonGrau());
            }
        });
    }

    /**
     * Initialisiert die View-Elemente
     */
    @Override
    public void initView() {

        mSteinSpielerOhneZeit = findViewById(R.id.stein_spieler_oz);
        mSteinSpielerRundenzeit = findViewById(R.id.stein_spieler_rd);
        mSteinSpielerSpieldauer = findViewById(R.id.stein_spieler_sd);
        mSteinSpielerOhneZeit.setVisibility(View.VISIBLE);
        mSteinSpielerRundenzeit.setVisibility(View.INVISIBLE);
        mSteinSpielerSpieldauer.setVisibility(View.INVISIBLE);

        btnOhneZeit = findViewById(R.id.btn_ohne_zeit);
        btnRundenzeit = findViewById(R.id.btn_rundenzeit);
        btnSpieldauer = findViewById(R.id.btn_spieldauer);

        mSteinGegnerOhneZeit = findViewById(R.id.stein_gegner_oz);
        mSteinGegnerRundenzeit = findViewById(R.id.stein_gegner_rd);
        mSteinGegnerSpieldauer = findViewById(R.id.stein_gegner_sd);

        mSpinner = findViewById(R.id.spieleinstellungen_zeiteinstellungen);
        mSpinner.setVisibility(View.INVISIBLE);

        //Spinner fuer den Spieler
        mSpinnerMinutenSpieler = findViewById(R.id.spinner_spieler_minuten);
        mSpinnerSekundenSpieler = findViewById(R.id.spinner_spieler_sekunden);

        //Spinner fuer den Gegner
        mSpinnerMinutenGegner = findViewById(R.id.spinner_gegner_minuten);
        mSpinnerSekundenGegner = findViewById(R.id.spinner_gegner_sekunden);
        mSpinnerMinutenGegner.setEnabled(false);
        mSpinnerSekundenGegner.setEnabled(false);

        mTvSpielernameLabel = findViewById(R.id.tv_spielername);
        mTvGegnernameLabel = findViewById(R.id.tv_gegnername);
        mTvZeitLabel = findViewById(R.id.tv_zeit);

        mButtonSpielerBereit = findViewById(R.id.btn_spieler_bereit);
        mButtonGegnerBereit = findViewById(R.id.btn_gegner_bereit);

        zeigeGegnerZeittyp(Konstanten.SPIELZEIT_OHNE_ZEIT);

        setzeSchatten();

        setzeButtonKlickListener();
        // Größe der PopUp-Fenster für die Spinner wird festgelegt.

        // Listener für die Spinner werden festgelegt.
        setzeSpinnerListener();
        // Listener für die Bereit-Buttons werden festgelegt.
        setzeBereitButtonListener();

        // Spielername wird aus den SharedPreferences ausgelsesen und angezeigt.
        mTvSpielernameLabel.setText(SharedPrefUtils.gibBenutzername(this));

        // Übergebener Intent von Lobby wird ausgelsesen und die Daten werden angezeigt/gespeichert.
        Intent intent = getIntent();
        gegnername = intent.getStringExtra(Konstanten.EXTRA_GEGNER_NAME);
        mTvGegnernameLabel.setText(gegnername);
    }

    @Override
    public void setzePresenter(SpieleinstellungenContract.Presenter presenter) {
        this.presenter = presenter;
    }


    //TODO ggf. in Seperate Methoden fuer Spieler und Gegner aufteilen?
    public void zeigeZeittypOhneZeit(boolean istSpieler) {
        runOnUiThread(() -> {
            if (istSpieler) {
                mSteinSpielerOhneZeit.setVisibility(View.VISIBLE);
                mSteinSpielerRundenzeit.setVisibility(View.INVISIBLE);
                mSteinSpielerSpieldauer.setVisibility(View.INVISIBLE);
            } else {
                mSteinGegnerOhneZeit.setVisibility(View.VISIBLE);
                mSteinGegnerRundenzeit.setVisibility(View.INVISIBLE);
                mSteinGegnerSpieldauer.setVisibility(View.INVISIBLE);
            }
        });

    }

    /**
     * Zeigt den Zeittyp Rundenzeit fuer den Spieler oder den Gegner an.
     *
     * @param istSpieler - true, genau dann, wenn der Zeittyp fuer den Spieler angezeigt werden
     *                   soll
     */
    public void zeigeZeittypRundenzeit(boolean istSpieler) {
        runOnUiThread(() -> {
            if (istSpieler) {
                mSteinSpielerOhneZeit.setVisibility(View.INVISIBLE);
                mSteinSpielerRundenzeit.setVisibility(View.VISIBLE);
                mSteinSpielerSpieldauer.setVisibility(View.INVISIBLE);
            } else {
                mSteinGegnerOhneZeit.setVisibility(View.INVISIBLE);
                mSteinGegnerRundenzeit.setVisibility(View.VISIBLE);
                mSteinGegnerSpieldauer.setVisibility(View.INVISIBLE);
            }
        });
    }

    /**
     * @param istSpieler
     */
    private void zeigeZeittypSpieldauer(boolean istSpieler) {
        runOnUiThread(() -> {
            if (istSpieler) {
                mSteinSpielerOhneZeit.setVisibility(View.INVISIBLE);
                mSteinSpielerRundenzeit.setVisibility(View.INVISIBLE);
                mSteinSpielerSpieldauer.setVisibility(View.VISIBLE);
            } else {
                mSteinGegnerOhneZeit.setVisibility(View.INVISIBLE);
                mSteinGegnerRundenzeit.setVisibility(View.INVISIBLE);
                mSteinGegnerSpieldauer.setVisibility(View.VISIBLE);
            }
        });

    }

    /**
     * Zeigt den vom Gegner ausgewaehlten Zeittypen an.
     *
     * @param zeittyp - vom Gegner gewaehlter Zeittyp
     */
    public void zeigeGegnerZeittyp(String zeittyp) {

        switch (zeittyp) {
            case Konstanten.SPIELZEIT_OHNE_ZEIT:
                zeigeZeittypOhneZeit(false);
                break;
            case Konstanten.SPIELZEIT_RUNDENDAUER:
                zeigeZeittypRundenzeit(false);
                break;
            case Konstanten.SPIELZEIT_SPIELDAUER:
                zeigeZeittypSpieldauer(false);
                break;
        }
    }

    /**
     * Setzt Schatten fuer ausgewaehlte Elemente der View.
     */
    private void setzeSchatten() {
        TextView banner = findViewById(R.id.tv_spieleinstellungen_banner);
        ViewUtils.setzeSchatten(banner);
        ViewUtils.setzeSchatten(btnOhneZeit);
        ViewUtils.setzeSchatten(btnRundenzeit);
        ViewUtils.setzeSchatten(btnSpieldauer);
        ViewUtils.setzeSchatten(mTvZeitLabel);
        ViewUtils.setzeSchatten(mTvSpielernameLabel);
        ViewUtils.setzeSchatten(mButtonSpielerBereit);
        ViewUtils.setzeSchatten(mTvGegnernameLabel);
        ViewUtils.setzeSchatten(mButtonGegnerBereit);
    }

    /**
     * Setzt die OnClick Listener für die Buttons. Je nach Button der gedrückt wurde, wird der Stein
     * bei den anderen Beiden unsichtbar gemacht und beim gedrückten Button sichbar gemacht.
     */
    private void setzeButtonKlickListener() {
        btnOhneZeit.setOnClickListener(v -> {
            ausgewaehlterZeittyp = Konstanten.SPIELZEIT_OHNE_ZEIT;

            presenter.sendeZeittyp(ausgewaehlterZeittyp);
        });

        btnRundenzeit.setOnClickListener(v -> {
            ausgewaehlterZeittyp = Konstanten.SPIELZEIT_RUNDENDAUER;

            presenter.sendeZeittyp(ausgewaehlterZeittyp);
        });

        btnSpieldauer.setOnClickListener(v -> {
            ausgewaehlterZeittyp = Konstanten.SPIELZEIT_SPIELDAUER;

            presenter.sendeZeittyp(ausgewaehlterZeittyp);
        });
    }

    /**
     * Bindet Listener an die Spinner-Elemente um zu überprüfen, welches Item aus dem Spinner
     * gewählt wurde. Diese Wahl wird dann an den Gegener übertragen und angezeigt.
     */
    public void setzeSpinnerListener() {
        mSpinnerMinutenSpieler.setOnItemSelectedListener(this);
        mSpinnerSekundenSpieler.setOnItemSelectedListener(this);
    }

    /**
     * Bindet Listener an die Bereit-Buttons der Spieler, um zu überprüfen, wann ein Spieler bereit
     * zum Spielen ist. Ist eine Spieler bereit wird dies über den Server an den Gegner übertragen
     * und dort angeziegt.
     */
    private void setzeBereitButtonListener() {
        mButtonSpielerBereit.setOnClickListener(view -> presenter.sendeIstBereit());
    }

    /**
     * Zeigt an, ob der Spieler bereit ist.
     *
     * @param istBereit - boolean Wert, der angibt ob der Gegner bereit ist
     */
    public void zeigeSpielerIstBereit(boolean istBereit) {
        runOnUiThread(() -> {
            if (istBereit) {
                mButtonSpielerBereit.setText(getString(R.string.bereit_label));
                mButtonSpielerBereit.setBackground(gibButtonGruen());
            } else {
                mButtonSpielerBereit.setText(getString(R.string.nicht_bereit_label));
                mButtonSpielerBereit.setBackground(gibButtonRot());
            }
        });
    }

    /**
     * Zeigt an, ob der Gegner bereit ist.
     *
     * @param istBereit - boolean Wert der angibt ob der Gegner bereit ist oder nicht
     */
    public void zeigeGegnerIstBereit(boolean istBereit) {
        runOnUiThread(() -> {
            if (istBereit) {
                mButtonGegnerBereit.setText(getString(R.string.bereit_label));
                mButtonGegnerBereit.setBackground(gibButtonGruen());
            } else {
                mButtonGegnerBereit.setText(getString(R.string.nicht_bereit_label));
                mButtonGegnerBereit.setBackground(gibButtonRot());
            }
        });
    }

    private Drawable gibButtonRot() {
        return ContextCompat.getDrawable(this, R.drawable.button_red);
    }

    private Drawable gibButtonGruen() {
        return ContextCompat.getDrawable(this, R.drawable.button_green);
    }

    private Drawable gibButtonGrau() {
        return ContextCompat.getDrawable(this, R.drawable.button_gray);
    }

    @Override
    public void zeigeNeueGegnerMinuten(int minutenIndex) {
        runOnUiThread(() -> mSpinnerMinutenGegner.setSelection(minutenIndex));
    }

    @Override
    public void zeigeNeueGegnerSekunden(int sekundenIndex) {
        runOnUiThread(() -> mSpinnerSekundenGegner.setSelection(sekundenIndex));
    }

    @Override
    public void zeigeGegnerHatEinstellungenVerlassen() {
        runOnUiThread(() -> {
            mAktuellerDialog = SpieleinstellungenWurdenVerlassenDialog.newInstance(gegnername);
            mAktuellerDialog.show(getSupportFragmentManager(), "spieleinstellungen_wurden_verlassen_tag");
        });
    }

    @Override
    public void zeigeSpielerZeittyp(String zeitTyp) {
        runOnUiThread(() -> {
            switch (zeitTyp) {
                case Konstanten.SPIELZEIT_OHNE_ZEIT:
                    zeigeZeittypOhneZeit(true);
                    break;

                case Konstanten.SPIELZEIT_RUNDENDAUER:
                    zeigeZeittypRundenzeit(true);
                    break;

                case Konstanten.SPIELZEIT_SPIELDAUER:
                    zeigeZeittypSpieldauer(true);
                    break;
            }
        });
    }


    /**
     * Diese Methode wird ausgeführt, wenn die Activity nicht mehr sichtbar ist.
     */
    @Override
    public void onPause() {
        super.onPause();
        presenter.stop();
    }

    /**
     * Diese Methode wird ausgeführt, wenn die Activity wieder sichtbar wird.
     */
    @Override
    public void onResume() {
        super.onResume();
        Intent extraInfo = getIntent();
        new SpieleinstellungenPresenter(this,
                extraInfo.getStringExtra(Konstanten.EXTRA_RAUM),
                extraInfo.getStringExtra(Konstanten.EXTRA_GEGNER_NAME));
    }

    @Override
    public void starteSpiel(String zeittyp, String gegnername, String raum, int minutenIndex,
                            int sekundenIndex) {

        Intent startGame = new Intent(SpieleinstellungenActivity.this, BaoSpielActivity.class);
        startGame.putExtra(Konstanten.EXTRA_SPIELTYP, Konstanten.SPIELTYP_MEHRSPIELER);
        startGame.putExtra(Konstanten.EXTRA_SPIELMODUS, zeittyp);
        startGame.putExtra(Konstanten.EXTRA_GEGNER_NAME, gegnername);
        if (mSpinnerMinutenSpieler.getAdapter() != null) {
            startGame.putExtra(Konstanten.EXTRA_SPIELZEIT_MINUTEN,
                    Integer.valueOf(mSpinnerMinutenSpieler.getAdapter().getItem(minutenIndex).toString()));
            startGame.putExtra(Konstanten.EXTRA_SPIELZEIT_SEKUNDEN,
                    Integer.valueOf(mSpinnerSekundenSpieler.getAdapter().getItem(sekundenIndex).toString()));
        }
        startGame.putExtra(Konstanten.EXTRA_RAUM, raum);
        startActivity(startGame);
    }

    @Override
    public void onBackPressed() {
        if (mAktuellerDialog == null) {
            zeigeSpieleinstellungenVerlassenDialog();
        } else {
            mAktuellerDialog.dismissAllowingStateLoss();
            presenter.verlasseSpieleinstellungen();
            finish();
        }
    }

    /**
     * zeigt einen Dialog zur Bestaetigung, dass die Spieleinstellungen wirklich verlassen werden
     * sollen.
     */
    private void zeigeSpieleinstellungenVerlassenDialog() {
        mAktuellerDialog = SpieleinstellungenVerlassenDialog.newInstance();
        mAktuellerDialog.show(getSupportFragmentManager(), "spieleinstellungen_verlassen_dialog");
    }

    /*----------------------------------------------------------------------------------------------
                                        OnItemSelected Callbacks
         ---------------------------------------------------------------------------------------------*/
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {

        switch (parent.getId()) {
            case R.id.spinner_spieler_minuten:
                presenter.sendeMinutenauswahl(position);
                break;
            case R.id.spinner_spieler_sekunden:
                presenter.sendeSekundenauswahl(position);
                break;
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
        // wird nicht gebraucht
    }


    /*----------------------------------------------------------------------------------------------
                                    OnlineActivity Callbacks
    --------------------------------------------------------------------------------------------- */
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        BaoService baoServiceInterface = ((BaoService.LocalBinder) service).getService();
        SpieleinstellungenRemoteDataSource serviceInteractor = new SpieleinstellungenServiceInteractor(baoServiceInterface);
        presenter.setzeServiceInteractor(serviceInteractor);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }

    /*----------------------------------------------------------------------------------------------
                             SpieleinstellungenVerlassenDialog Callbacks
     ---------------------------------------------------------------------------------------------*/
    @Override
    public void onVerlassenBestaetigt() {
        mAktuellerDialog = null;
        presenter.verlasseSpieleinstellungen();
        finish();
    }

    @Override
    public void onVerlassenAbgebrochen() {
        // nichts tun
    }

    @Override
    public void onDialogBestaetigt() {
        mAktuellerDialog = null;
        presenter.verlasseSpieleinstellungen();
        finish();
    }
}