/*
 * Copyright (C) 2022 - Florindo Smilari (spherIC)
 */
package ch.spherIC.recurvebowsight.ui.calcsight;

import android.content.Context;
import android.content.res.ColorStateList;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Toast;

import java.text.DecimalFormat;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import ch.spherIC.recurvebowsight.R;
import ch.spherIC.recurvebowsight.components.NumberAutoCompleteTextView;
import ch.spherIC.recurvebowsight.database.dao.RiserDao;
import ch.spherIC.recurvebowsight.database.dao.SightDao;
import ch.spherIC.recurvebowsight.databinding.FragmentCalcsightSightsettingBinding;
import ch.spherIC.recurvebowsight.model.IArcherySetup;
import ch.spherIC.recurvebowsight.model.IRiser;
import ch.spherIC.recurvebowsight.model.ISight;
import ch.spherIC.recurvebowsight.utilities.NoFilterArrayAdapter;
import ch.spherIC.recurvebowsight.utilities.PlatformUtils;

public class CalculateSightSightSettingFragment extends Fragment {

    public static final DecimalFormat DF = new DecimalFormat("0.##");
    public static final DecimalFormat DF_EVS = new DecimalFormat("0.#");

    private FragmentCalcsightSightsettingBinding binding;
    private CalculateSightSightSettingViewModel calcSightSettingModel;
    private ColorStateList defaultTextColors;
    private int defaultBoxBackgroundColor;

    public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        this.calcSightSettingModel = new ViewModelProvider(this).get(CalculateSightSightSettingViewModel.class);
        this.binding = FragmentCalcsightSightsettingBinding.inflate(inflater, container, false);
        View root = this.binding.getRoot();

        this.defaultTextColors = this.binding.ssEvSTxt.getTextColors();
        this.defaultBoxBackgroundColor = this.binding.ssEvSLayout.getBoxBackgroundColor();
        NumberAutoCompleteTextView horizontalSightSettingTxt = root.findViewById(R.id.ssHorizontalSightSettingTxt);
        horizontalSightSettingTxt.setFormatter(new DecimalFormat("0.#"));

        this.binding.fabCalculate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                updateModelFromUI();
                calculateSettings();
            }
        });

        getParentFragmentManager().setFragmentResultListener("selectedArcherySetup", this, (requestKey, bundle) -> {
            if (requestKey.equals("selectedArcherySetup")) {
                IArcherySetup selectedArcherySetup = bundle.getParcelable("selectedArcherySetup");
                if (selectedArcherySetup != null) {
                    this.calcSightSettingModel.setSelectedArcherySetup(selectedArcherySetup);
                    this.calcSightSettingModel.setSelectedRiser(selectedArcherySetup.getRiser());
                    this.calcSightSettingModel.setSelectedSight(selectedArcherySetup.getSight());
                    this.calcSightSettingModel.setHeVis(selectedArcherySetup.getSightHorzSetting());
                    this.calcSightSettingModel.setVertSkalaMiddle(selectedArcherySetup.getSightVertSkalaMiddle());
                    loadHeVis(selectedArcherySetup.getSight());
                }
            }
        });

        getParentFragmentManager().setFragmentResultListener("startAngle", this, (requestKey, bundle) -> {
            if (requestKey.equals("startAngle")) {
                Double startAngle = bundle.getDouble("startAngle");
                Double shootingDist = bundle.getDouble("shootingDistance");
                this.calcSightSettingModel.setStartAngle(startAngle);
                this.calcSightSettingModel.setShootingDistance(shootingDist);
            }
        });

        loadRisers();
        loadSights();
        addRiserSpinnerListener();
        addSightSpinnerListener();
        addHeVisSpinnerListener();
        addObservers();
        return root;
    }

    @Override
    public void onResume() {
        super.onResume();
        calculateSettings(false);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        this.binding = null;
    }

    private void addObservers() {
        this.calcSightSettingModel.getSelectedRiser().observe(getViewLifecycleOwner(), riser -> this.binding.ssRiserSelectionTxt.setText(riser != null ? riser.toString() : ""));
        this.calcSightSettingModel.getSelectedSight().observe(getViewLifecycleOwner(), sight -> this.binding.ssSightSelectionTxt.setText(sight != null ? sight.toString() : ""));
        this.calcSightSettingModel.getVVisV().observe(getViewLifecycleOwner(), vVisV -> this.binding.ssvVisVTxt.setText(vVisV != null ? DF.format(vVisV) : ""));
        this.calcSightSettingModel.getVVisH().observe(getViewLifecycleOwner(), vVisH -> this.binding.ssvVisHTxt.setText(vVisH != null ? DF.format(vVisH) : ""));
        this.calcSightSettingModel.getHeVis().observe(getViewLifecycleOwner(), heVis -> this.binding.ssHorizontalSightSettingTxt.setText(heVis != null ? DF.format(heVis) : ""));
        this.calcSightSettingModel.getAV().observe(getViewLifecycleOwner(), aV -> this.binding.ssaVTxt.setText(aV != null ? DF.format(aV) : ""));
        this.calcSightSettingModel.getAH().observe(getViewLifecycleOwner(), aH -> this.binding.ssaHTxt.setText(aH != null ? DF.format(aH) : ""));
        this.calcSightSettingModel.getVertScaleRngMin().observe(getViewLifecycleOwner(), vertSclRngMin -> this.binding.ssVertScaleRngTxt.setText(getVertScaleRangeText(vertSclRngMin, null)));
        this.calcSightSettingModel.getVertScaleRngMax().observe(getViewLifecycleOwner(), vertSclRngMax -> this.binding.ssVertScaleRngTxt.setText(getVertScaleRangeText(null, vertSclRngMax)));
        this.calcSightSettingModel.getScalePointerOffset().observe(getViewLifecycleOwner(), scalePtrOffset -> this.binding.ssVertScalePointerOffsetTxt.setText(scalePtrOffset != null ? DF.format(scalePtrOffset) : ""));
        this.calcSightSettingModel.getVertSkalaMiddle().observe(getViewLifecycleOwner(), vertScaleMiddle -> this.binding.ssVertSkalaMiddleTxt.setText(vertScaleMiddle != null ? DF.format(vertScaleMiddle) : ""));
        this.calcSightSettingModel.getEvS().observe(getViewLifecycleOwner(), evs -> this.binding.ssEvSTxt.setText(evs != null ? DF_EVS.format(evs) : ""));
    }

    private void updateModelFromUI() {
        this.calcSightSettingModel.setVertSkalaMiddle(PlatformUtils.parseDouble(this.binding.ssVertSkalaMiddleTxt.getText().toString()));
    }

    private String getVertScaleRangeText(Double vertSclRngMin, Double vertSclRngMax) {
        if (vertSclRngMin != null && vertSclRngMax != null) {
            return DF.format(vertSclRngMin) + "-" + DF.format(vertSclRngMax);
        } else if (vertSclRngMin != null && vertSclRngMax == null) {
            return DF.format(vertSclRngMin) + "-" + (this.calcSightSettingModel.getVertScaleRngMax().getValue() != null ?
                                                     DF.format(this.calcSightSettingModel.getVertScaleRngMax().getValue()) : "");
        } else if (vertSclRngMin == null && vertSclRngMax != null) {
            return (this.calcSightSettingModel.getVertScaleRngMin().getValue() != null ?
                    DF.format(this.calcSightSettingModel.getVertScaleRngMin().getValue()) : "") + "-" + DF.format(vertSclRngMax);
        }
        return "";
    }

    private void loadRisers() {
        List<IRiser> risers = RiserDao.getInstance().loadAll();
        ArrayAdapter<IRiser> adapter = new NoFilterArrayAdapter<IRiser>(this.binding.getRoot().getContext(), android.R.layout.simple_dropdown_item_1line, risers);
        AutoCompleteTextView textView = this.binding.ssRiserSelectionTxt;
        textView.setAdapter(adapter);
        textView.invalidate();
    }

    private void loadSights() {
        List<ISight> sights = SightDao.getInstance().loadAll();
        ArrayAdapter<ISight> adapter = new NoFilterArrayAdapter<ISight>(this.binding.getRoot().getContext(), android.R.layout.simple_dropdown_item_1line, sights);
        AutoCompleteTextView textView = this.binding.ssSightSelectionTxt;
        textView.setAdapter(adapter);
        textView.invalidate();
    }

    private void loadHeVis(ISight selectedSight) {
        if (selectedSight != null) {
            ArrayAdapter<Double> adapter = new NoFilterArrayAdapter<Double>(this.binding.getRoot().getContext(), android.R.layout.simple_dropdown_item_1line,
                                                                            selectedSight.getHeVisList(), new DecimalFormat("0.#"));
            AutoCompleteTextView textView = this.binding.ssHorizontalSightSettingTxt;
            textView.setAdapter(adapter);
            textView.invalidate();
        }
    }

    private void addHeVisSpinnerListener() {
        AutoCompleteTextView textView = this.binding.getRoot().findViewById(R.id.ssHorizontalSightSettingTxt);
        textView.setOnItemClickListener((parent, view, position, id) -> {
            Double selectedHeVis = (Double) textView.getAdapter().getItem(position);
            if (selectedHeVis != null) {
                calcSightSettingModel.setHeVis(selectedHeVis);
            }
        });
    }

    private void addSightSpinnerListener() {
        AutoCompleteTextView textView = this.binding.getRoot().findViewById(R.id.ssSightSelectionTxt);
        textView.setOnItemClickListener((parent, view, position, id) -> {
            ISight selectedSight = (ISight) textView.getAdapter().getItem(position);
            if (selectedSight != null) {
                loadHeVis(selectedSight);
                calcSightSettingModel.setSelectedSight(selectedSight);
                calcSightSettingModel.setHeVis(selectedSight.getHeVisList().get(0));
            }
        });
    }

    private void addRiserSpinnerListener() {
        AutoCompleteTextView textView = this.binding.getRoot().findViewById(R.id.ssRiserSelectionTxt);
        textView.setOnItemClickListener((parent, view, position, id) -> {
            IRiser selectedRiser = (IRiser) textView.getAdapter().getItem(position);
            if (selectedRiser != null) {
                calcSightSettingModel.setSelectedRiser(selectedRiser);
            }
        });
    }

    /**
     * Berechnet die definitve Visiereinstellung anhand des berechneten Flugbahnwinkels.
     */
    private void calculateSettings() {
        calculateSettings(true);
    }

    private void calculateSettings(boolean hintOnValidationError) {
        Context context = this.binding.getRoot().getContext();
        InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        imm.hideSoftInputFromWindow(this.binding.getRoot().getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
        this.binding.ssEvSTxt.requestFocus();

        if (allSightParamFieldsValid() && this.calcSightSettingModel.getSelectedArcherySetup() != null && this.calcSightSettingModel.getStartAngle() != null
                && this.calcSightSettingModel.getShootingDistance() != null) {
            Double startAngle = this.calcSightSettingModel.getStartAngle();
            Double x0;
            Double y0;
            Double x1;
            Double y1;
            Double x2;
            Double y2;
            Double x3;
            Double y3;
            Double r;
            Double s;
            Double vVh = this.calcSightSettingModel.getVVisH().getValue() + this.calcSightSettingModel.getAH().getValue();
            Double vVv = this.calcSightSettingModel.getVVisV().getValue() + this.calcSightSettingModel.getAV().getValue();
            Double a = this.calcSightSettingModel.getSelectedArcherySetup().getBowPullout() * 10;
            Double heVs = this.calcSightSettingModel.getHeVis().getValue();

            Double lhNA = this.calcSightSettingModel.getSelectedArcherySetup().getHNA();
            Double dltH = (this.calcSightSettingModel.getSelectedArcherySetup().getTargetCenterHeight() - this.calcSightSettingModel.getSelectedArcherySetup().getArrowNockHeight()) * Math.pow(10, 3);
            Double ldistH = this.calcSightSettingModel.getShootingDistance() * Math.pow(10, 3);
            Double minSkala = this.calcSightSettingModel.getVertScaleRngMin().getValue();
            Double maxSkala = this.calcSightSettingModel.getVertScaleRngMax().getValue();
            Double pointerOffset = this.calcSightSettingModel.getScalePointerOffset().getValue();

            Double nockRaising = this.calcSightSettingModel.getSelectedArcherySetup().getNockRaising() - this.calcSightSettingModel.getSelectedArcherySetup().getArrowDiameter();
            Double braceHeight = this.calcSightSettingModel.getSelectedArcherySetup().getBraceHeight();
            Double phi = -Math.atan(nockRaising / braceHeight); // Winkel des Pfeils zur Auflage infolge Nockpunktüberhöhung

            // PIN auf Höhe UK Vertikalskala Visier, gedreht um den Abschusswinkel...
            Double x01 = a * Math.cos(phi) + vVh + heVs;
            Double y01 = a * Math.sin(phi) + vVv - maxSkala / 2;
            x0 = Math.cos(Math.toRadians(startAngle)) * x01 - Math.sin(Math.toRadians(startAngle)) * y01;
            y0 = Math.sin(Math.toRadians(startAngle)) * x01 + Math.cos(Math.toRadians(startAngle)) * y01;

            // PIN auf Höhe Horizontalschlitten Visier, gedreht um den Abschusswinkel...
            Double x02 = a * Math.cos(phi) + vVh + heVs;
            Double y02 = a * Math.sin(phi) + vVv;
            x2 = Math.cos(Math.toRadians(startAngle)) * x02 - Math.sin(Math.toRadians(startAngle)) * y02;
            y2 = Math.sin(Math.toRadians(startAngle)) * x02 + Math.cos(Math.toRadians(startAngle)) * y02;

            // Schnittpunkt der Achse Horizontalschlitten Visier mit Achse Befestigungslöcher Visier, gedreht um den Abschusswinkel...
            Double x03 = a * Math.cos(phi) + vVh;
            Double y03 = a * Math.sin(phi) + vVv;
            x3 = Math.cos(Math.toRadians(startAngle)) * x03 - Math.sin(Math.toRadians(startAngle)) * y03;
            y3 = Math.sin(Math.toRadians(startAngle)) * x03 + Math.cos(Math.toRadians(startAngle)) * y03;

            // Unbekannte der Vektorgeradengleichung
            r = (lhNA * (x0 - x2) - x0 * y2 + x2 * y0) / (ldistH * (y0 - y2) + (dltH - lhNA) * (x2 - x0));
            s = -((ldistH * (lhNA - y0) + (dltH - lhNA) * x0) / (ldistH * (y0 - y2) + (dltH - lhNA) * (x2 - x0)));

            // Pin auf Visierlinie
            x1 = r * ldistH;
            y1 = lhNA + r * (dltH - lhNA);

            // Unten (+) oder oben (-) von (x2/y2) ?
            Double n = ((y2 - y3) * (x1 - x3) - (x2 - x3) * (y1 - y3)) < 0 ? -1.0 : 1.0;

            // Abstand P1 - P2
            Double d = Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2)) * n;

            // Einstellung auf vert. Skala
            Double sV = this.calcSightSettingModel.getVertSkalaMiddle().getValue() + d - pointerOffset;

            this.binding.ssEvSTxt.setText(DF_EVS.format(sV));

            if (minSkala <= sV && sV <= maxSkala) {
                this.binding.ssEvSLayout.setBoxBackgroundColor(getResources().getColor(R.color.android_green, this.binding.getRoot().getContext().getTheme()));
                this.binding.ssEvSTxt.setTextColor(getResources().getColor(R.color.black, this.binding.getRoot().getContext().getTheme()));
            } else {
                this.binding.ssEvSLayout.setBoxBackgroundColor(getResources().getColor(R.color.android_red, this.binding.getRoot().getContext().getTheme()));
                this.binding.ssEvSTxt.setTextColor(getResources().getColor(R.color.white, this.binding.getRoot().getContext().getTheme()));
            }

        } else {
            this.binding.ssEvSLayout.setBoxBackgroundColor(this.defaultBoxBackgroundColor);
            this.binding.ssEvSTxt.setTextColor(this.defaultTextColors);
            this.binding.ssEvSTxt.setText("");
            this.binding.ssEvSTxt.clearFocus();
            if (hintOnValidationError) {
                Toast toast = Toast.makeText(this.binding.getRoot().getContext(), getResources().getString(R.string.caption_FillAllMandatoryFields), Toast.LENGTH_LONG);
                toast.setGravity(Gravity.CENTER, 0, 0);
                toast.show();
            }
        }
    }

    private boolean allSightParamFieldsValid() {
        return !this.binding.ssvVisHTxt.getText().toString().isEmpty()
                && !this.binding.ssvVisVTxt.getText().toString().isEmpty()
                && !this.binding.ssHorizontalSightSettingTxt.getText().toString().isEmpty()
                && !this.binding.ssaVTxt.getText().toString().isEmpty()
                && !this.binding.ssaHTxt.getText().toString().isEmpty()
                && !this.binding.ssVertScaleRngTxt.getText().toString().isEmpty()
                && !this.binding.ssVertScalePointerOffsetTxt.getText().toString().isEmpty()
                && !this.binding.ssVertSkalaMiddleTxt.getText().toString().isEmpty();
    }

}
