package ch.spherIC.recurvebowsight;

import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;

import android.content.Context;
import android.content.Intent;

import android.content.pm.ActivityInfo;

import android.os.Bundle;
import android.os.Handler;

import android.text.Editable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextWatcher;

import android.text.style.TabStopSpan;

import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;

import android.view.View.OnClickListener;
import android.view.View.OnTouchListener;

import android.view.inputmethod.InputMethodManager;

import android.widget.AdapterView;

import android.widget.AdapterView.OnItemSelectedListener;

import android.widget.Button;
import android.widget.LinearLayout;

import android.widget.LinearLayout.LayoutParams;

import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;

import ch.spherIC.recurvebowsight.components.FlightCurveChart;
import ch.spherIC.recurvebowsight.components.FontArrayAdapter;
import ch.spherIC.recurvebowsight.components.FontTextView;
import ch.spherIC.recurvebowsight.components.RiserSpinner;
import ch.spherIC.recurvebowsight.components.SightSpinner;
import ch.spherIC.recurvebowsight.components.XSpinner;
import ch.spherIC.recurvebowsight.components.XTextView;
import ch.spherIC.recurvebowsight.configuration.MainConfigurationActivity;
import ch.spherIC.recurvebowsight.database.RBSDatabaseHelper;
import ch.spherIC.recurvebowsight.database.dao.ArcherySetupDao;
import ch.spherIC.recurvebowsight.database.dao.RiserDao;
import ch.spherIC.recurvebowsight.database.dao.SightDao;
import ch.spherIC.recurvebowsight.dialog.ChooseArcherySetupDlg.ChooseArcherySetupDlgDismissListener;
import ch.spherIC.recurvebowsight.logic.CalculationTask;
import ch.spherIC.recurvebowsight.logic.TrajectoryCalculator;
import ch.spherIC.recurvebowsight.model.IArcherySetup;
import ch.spherIC.recurvebowsight.model.IRiser;
import ch.spherIC.recurvebowsight.model.ISight;
import ch.spherIC.recurvebowsight.model.Riser;
import ch.spherIC.recurvebowsight.model.Sight;
import ch.spherIC.recurvebowsight.utilities.CompatibilityUtil;
import ch.spherIC.recurvebowsight.utilities.PlatformUtils;
import ch.spherIC.recurvebowsight.utilities.UnitConverter;

import org.achartengine.GraphicalView;

import org.achartengine.model.XYSeries;

import java.text.DecimalFormat;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;


public class RBSMainActivity extends Activity implements ChooseArcherySetupDlgDismissListener, TrajectoryCalculation {

    public static final DecimalFormat DF = new DecimalFormat("0.##");
    public static final DecimalFormat DF_FC = new DecimalFormat("0.000");
    public static final DecimalFormat DF_1 = new DecimalFormat("0.00");
    public static final DecimalFormat DF_EVS = new DecimalFormat("0.#");

    public static final int DLG_CHOOSE_ARCHERYSETUP = 0;
    public static final int DLG_USERGUIDE_PARAMS = 1;
    public static final int DLG_USERGUIDE_SIGHT = 2;
    public static final int DLG_ABOUT = 3;

    private static final Double ANGLE_90 = 90d;
    private static final int MIN_DISTANCE = 50;
    private static final double CENTI = 0.01;
    private static final double MILLI = 0.001;
    private static final int MAX_TRAJ_POINTS = 200;
    private static final String DEF_TEMP = "23";
    private static final String DEF_HUMIDITY = "50";
    private static final String DEF_DISTANCE = "18";

    private ViewFlipper viewFlipper;
    private ScrollView paramsScrollView;
    private ScrollView sightCfgScrollView;
    private float lastX;
    private XTextView txtViewParams;
    private XTextView txtViewResults;
    private XTextView txtViewSight;

    private TextView arrowDiameterTxtFld;
    private TextView arrowCwTxtFld;
    private TextView arrowWeightTxtFld;
    private TextView bowPulloutTxtFld;
    private TextView arrowV0TxtFld;
    private TextView hNATxtFld;
    private TextView nockRaisingTxtFld;
    private TextView braceHeightTxtFld;
    private TextView airTemperatureTxtFld;
    private TextView airRelativeHumidityTxtFld;
    private TextView shootingDistanzTxtFld;
    private TextView arrowNockHeightTxtFld;
    private TextView targetCenterHeightTxtFld;
    private XSpinner calcTimeIntervalCboBox;
    private XSpinner calcAccuracyCboBox;

    private RiserSpinner scRiserSpinner;
    private TextView scVvisHTxtFld;
    private TextView scVvisVTxtFld;
    private SightSpinner scSightSpinner;
    private XSpinner scHeVSSpinner;
    private TextView scHeVSTxtFld;
    private TextView scaVTxtFld;
    private TextView scaHTxtFld;
    private TextView scVertScaleRngTxtFld;
    private TextView scVertScaleMiddleTxtFld;
    private FontTextView scEvSValue;
    private Button calcSettingBtn;
    private XTextView flightCurveTxtFld;

    private boolean showInfoMenu;
    private IArcherySetup selectedArcherySetup;
    private Double startAngle;
    private String selectedHeVS;

    private ProgressDialog progressDialog;
    private FlightCurveChart flightCurveChart;


    @Override
    public void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.rbs_main);
        PlatformUtils.getPlatformSelectionColor(getApplicationContext());
        if (CompatibilityUtil.isTablet(getApplicationContext())) {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        } else {
            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }

        UnitConverter.setApplication(getApplication());
        RBSDatabaseHelper.setContext(this);
        RBSDatabaseHelper.getInstance().initializeDB();

        initializeComponents();
        addTouchListeners();
        addValueChangeListeners();

        xTextViewUp(this.txtViewParams);

        this.calcTimeIntervalCboBox.setAdapter(new FontArrayAdapter<String>(this, R.layout.rbs_spinner,
                                                                            getResources().getStringArray(R.array.CalculationTimeIntervals),
                                                                            this.calcTimeIntervalCboBox));
        this.calcAccuracyCboBox.setAdapter(new FontArrayAdapter<String>(this, R.layout.rbs_spinner,
                                                                        getResources().getStringArray(R.array.CalculationAccuracies),
                                                                        this.calcAccuracyCboBox));

        loadRiserSpinner();
        loadSightSpinner();
        addRiserAndSightSpinnerListeners();

        GraphicalView flightCurveChartView;
        LinearLayout layout;
        this.flightCurveChart = new FlightCurveChart();
        flightCurveChartView = this.flightCurveChart.getView(this);
        layout = (LinearLayout) findViewById(R.id.ballisticResults);
        layout.addView(flightCurveChartView, 0, new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, 0, 1f));
    }


    private void loadRiserSpinner() {
        IRiser selectedRiser = this.scRiserSpinner.getSelectedRiser();
        List<IRiser> risers = RiserDao.getInstance().loadAll();
        risers.add(0, new Riser(-999, getResources().getString(R.string.caption_NoSelection), "", null, null, null));
        this.scRiserSpinner.setAdapter(new FontArrayAdapter<IRiser>(this, R.layout.rbs_spinner, risers, this.scRiserSpinner));
        if (selectedRiser != null) {
            this.scRiserSpinner.setSelectedRiser(selectedRiser);
        }
    }

    private void loadSightSpinner() {
        ISight selectedSight = this.scSightSpinner.getSelectedSight();
        List<ISight> sights = SightDao.getInstance().loadAll();
        sights.add(0, new Sight(-999, getResources().getString(R.string.caption_NoSelection), "", null, null, "", null, null));
        this.scSightSpinner.setAdapter(new FontArrayAdapter<ISight>(this, R.layout.rbs_spinner, sights, this.scSightSpinner));
        if (selectedSight != null) {
            this.scSightSpinner.setSelectedSight(selectedSight);
        }
    }

    @Override
    protected void onSaveInstanceState(final Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("ViewFlipper.SelectedView", this.viewFlipper.getDisplayedChild());
        if (this.selectedArcherySetup != null) {
            outState.putLong("SelectedArcherySetup", this.selectedArcherySetup.getId());
        }
        if (this.startAngle != null) {
            outState.putDouble("StartAngle", this.startAngle);
        }
        if (this.scHeVSSpinner.getSelectedItem() != null) {
            outState.putString("SelectedHeVS", this.scHeVSSpinner.getSelectedItem().toString());
        }
    }


    @Override
    protected void onRestoreInstanceState(final Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        int selView = savedInstanceState.getInt("ViewFlipper.SelectedView", 0);
        this.viewFlipper.setDisplayedChild(selView);
        if (savedInstanceState.containsKey("SelectedArcherySetup")) {
            this.selectedArcherySetup = ArcherySetupDao.getInstance().loadById(savedInstanceState.getLong("SelectedArcherySetup"));
        }
        this.startAngle = savedInstanceState.getDouble("StartAngle");
        this.selectedHeVS = savedInstanceState.getString("SelectedHeVS");

        deactivateAllXTextView();

        switch (selView) {
        case 0:
            xTextViewUp(this.txtViewParams);
            break;
        case 1:
            xTextViewUp(this.txtViewResults);
            break;
        case 2:
            xTextViewUp(this.txtViewSight);
            break;

        default:
            break;
        }
    }


    private void initializeComponents() {

        this.viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
        this.paramsScrollView = (ScrollView) findViewById(R.id.paramsScrollView);
        this.sightCfgScrollView = (ScrollView) findViewById(R.id.sightCfgScrollView);
        this.txtViewParams = (XTextView) findViewById(R.id.viewParams);
        this.txtViewResults = (XTextView) findViewById(R.id.viewResults);
        this.txtViewSight = (XTextView) findViewById(R.id.viewSight);

        this.arrowDiameterTxtFld = (TextView) findViewById(R.id.arrowDiameterTxtFld);
        this.arrowCwTxtFld = (TextView) findViewById(R.id.arrowCwTxtFld);
        this.arrowWeightTxtFld = (TextView) findViewById(R.id.arrowWeightTxtFld);
        this.bowPulloutTxtFld = (TextView) findViewById(R.id.bowPulloutTxtFld);
        this.arrowV0TxtFld = (TextView) findViewById(R.id.arrowV0TxtFld);
        this.hNATxtFld = (TextView) findViewById(R.id.hNAFld);
        this.nockRaisingTxtFld = (TextView) findViewById(R.id.nockRaisingFld);
        this.braceHeightTxtFld = (TextView) findViewById(R.id.braceHeightFld);
        this.airTemperatureTxtFld = (TextView) findViewById(R.id.airTemperatureTxtFld);
        this.airRelativeHumidityTxtFld = (TextView) findViewById(R.id.airRelativeHumidityTxtFld);
        this.shootingDistanzTxtFld = (TextView) findViewById(R.id.shootingDistanzTxtFld);
        this.arrowNockHeightTxtFld = (TextView) findViewById(R.id.arrowNockHeightTxtFld);
        this.targetCenterHeightTxtFld = (TextView) findViewById(R.id.targetCenterHeightTxtFld);
        this.calcTimeIntervalCboBox = (XSpinner) findViewById(R.id.deltaTimeCboBox);
        this.calcAccuracyCboBox = (XSpinner) findViewById(R.id.calcPrecisionCboBox);

        this.scRiserSpinner = (RiserSpinner) findViewById(R.id.scRiserSpinner);
        this.scVvisHTxtFld = (TextView) findViewById(R.id.scVvisHTxtFld);
        this.scVvisVTxtFld = (TextView) findViewById(R.id.scVvisVTxtFld);
        this.scSightSpinner = (SightSpinner) findViewById(R.id.scSightSpinner);
        this.scHeVSSpinner = (XSpinner) findViewById(R.id.scHeVSSpinner);
        this.scHeVSTxtFld = (TextView) findViewById(R.id.scHeVSTxtFld);
        this.scaVTxtFld = (TextView) findViewById(R.id.scaVTxtFld);
        this.scaHTxtFld = (TextView) findViewById(R.id.scaHTxtFld);
        this.scVertScaleRngTxtFld = (TextView) findViewById(R.id.scVertScaleRngTxtFld);
        this.scVertScaleMiddleTxtFld = (TextView) findViewById(R.id.scVertSkalaMiddleTxtFld);
        this.scEvSValue = (FontTextView) findViewById(R.id.scEvSValue);

        this.flightCurveTxtFld = (XTextView) findViewById(R.id.flightCurveTxtFld);

        this.calcSettingBtn = (Button) findViewById(R.id.scBtnCalc);
    }


    private void addRiserAndSightSpinnerListeners() {

        this.scRiserSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(final AdapterView<?> parent, final View view, final int position,
                                           final long id) {
                    IRiser riser = RBSMainActivity.this.scRiserSpinner.getSelectedRiser();
                    if (riser.getId() != -999) {
                        RBSMainActivity.this.scVvisHTxtFld.setText(DF.format(riser.getVVisH()));
                        RBSMainActivity.this.scVvisVTxtFld.setText(DF.format(riser.getVVisV()));
                    } else {
                        RBSMainActivity.this.scVvisHTxtFld.setText("");
                        RBSMainActivity.this.scVvisVTxtFld.setText("");
                    }
                    RBSMainActivity.this.scVvisHTxtFld.setEnabled(riser.getId() == -999);
                    RBSMainActivity.this.scVvisVTxtFld.setEnabled(riser.getId() == -999);
                }

                @Override
                public void onNothingSelected(final AdapterView<?> parent) { }
            });

        this.scSightSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                @Override
                public void onItemSelected(final AdapterView<?> parent, final View view, final int position,
                                           final long id) {
                    ISight sight = RBSMainActivity.this.scSightSpinner.getSelectedSight();
                    if (sight.getId() != -999) {
                        List<String> listHeVis = new ArrayList<String>();
                        String heVis = sight.getHeVis();
                        StringTokenizer t = new StringTokenizer(heVis, ",");
                        while (t.hasMoreTokens()) {
                            listHeVis.add(t.nextToken());
                        }

                        RBSMainActivity.this.scHeVSTxtFld.setVisibility(View.INVISIBLE);
                        RBSMainActivity.this.scHeVSTxtFld.setLayoutParams(new LinearLayout.LayoutParams(RBSMainActivity.this.scHeVSTxtFld
                                                                                                        .getLayoutParams().width,
                                                                                                        RBSMainActivity.this.scHeVSTxtFld
                                                                                                        .getLayoutParams().height, 0f));
                        RBSMainActivity.this.scHeVSSpinner.setVisibility(View.VISIBLE);

                        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(RBSMainActivity.this.scHeVSSpinner.getLayoutParams().width,
                                                                                         RBSMainActivity.this.scHeVSSpinner.getLayoutParams().height,
                                                                                         1f);
                        params.gravity = Gravity.FILL_VERTICAL;
                        RBSMainActivity.this.scHeVSSpinner.setLayoutParams(params);
                        RBSMainActivity.this.scHeVSSpinner.setAdapter(new FontArrayAdapter<String>(RBSMainActivity.this, R.layout.rbs_spinner,
                                                                                                   listHeVis, RBSMainActivity.this.scHeVSSpinner));
                        if (RBSMainActivity.this.selectedHeVS == null) {
                            if (RBSMainActivity.this.selectedArcherySetup != null
                                && RBSMainActivity.this.selectedArcherySetup.getSight().getId() == sight.getId()) {
                                RBSMainActivity.this.scHeVSSpinner.setSelectionByVal(DF.format(RBSMainActivity.this.selectedArcherySetup
                                                                                               .getSightHorzSetting()));
                            }
                        } else {
                            RBSMainActivity.this.scHeVSSpinner.setSelectionByVal(RBSMainActivity.this.selectedHeVS);
                        }
                        RBSMainActivity.this.scaVTxtFld.setText(DF.format(sight.getAV()));
                        RBSMainActivity.this.scaHTxtFld.setText(DF.format(sight.getAH()));
                        RBSMainActivity.this.scVertScaleRngTxtFld.setText(DF.format(sight.getVertSkalaRangeMin()) + "-"
                                                                          + DF.format(sight.getVertSkalaRangeMax()));
                    } else {
                        RBSMainActivity.this.scHeVSSpinner.setVisibility(View.INVISIBLE);
                        RBSMainActivity.this.scHeVSSpinner.setLayoutParams(new LinearLayout.LayoutParams(RBSMainActivity.this.scHeVSSpinner
                                                                                                         .getLayoutParams().width,
                                                                                                         RBSMainActivity.this.scHeVSSpinner
                                                                                                         .getLayoutParams().height, 0f));
                        RBSMainActivity.this.scHeVSTxtFld.setVisibility(View.VISIBLE);
                        RBSMainActivity.this.scHeVSTxtFld.setLayoutParams(new LinearLayout.LayoutParams(RBSMainActivity.this.scHeVSTxtFld
                                                                                                        .getLayoutParams().width,
                                                                                                        RBSMainActivity.this.scHeVSTxtFld
                                                                                                        .getLayoutParams().height, 1f));
                        RBSMainActivity.this.scHeVSSpinner.setAdapter(new FontArrayAdapter<String>(RBSMainActivity.this, R.layout.rbs_spinner,
                                                                                                   new ArrayList<String>(),
                                                                                                   RBSMainActivity.this.scHeVSSpinner));
                        RBSMainActivity.this.scaVTxtFld.setText("");
                        RBSMainActivity.this.scaHTxtFld.setText("");
                        RBSMainActivity.this.scVertScaleRngTxtFld.setText("");
                    }

                    RBSMainActivity.this.scaVTxtFld.setEnabled(sight.getId() == -999);
                    RBSMainActivity.this.scaHTxtFld.setEnabled(sight.getId() == -999);
                    RBSMainActivity.this.scVertScaleRngTxtFld.setEnabled(sight.getId() == -999);
                }

                @Override
                public void onNothingSelected(final AdapterView<?> parent) { }
            });
    }

    private void addTouchListeners() {

        // Scrollview muss den Swipe selbst handeln
        this.paramsScrollView.setOnTouchListener(new OnTouchListener() {

                private float downX, downY, upX, upY;

                @Override
                public boolean onTouch(final View v, final MotionEvent event) {

                    switch (event.getAction()) {

                    case MotionEvent.ACTION_DOWN: {
                        this.downX = event.getX();
                        this.downY = event.getY();
                    }

                    case MotionEvent.ACTION_UP: {
                        this.upX = event.getX();
                        this.upY = event.getY();

                        float deltaX = this.upX - this.downX;
                        float deltaY = this.upY - this.downY;

                        // swipe from right to left ?
                        if (deltaX < 0 && Math.abs(deltaX) > MIN_DISTANCE && Math.abs(deltaY) < MIN_DISTANCE) {

                            // set the required Animation type to ViewFlipper
                            // The Next screen will come in form Right and current Screen will go OUT to Left
                            RBSMainActivity.this.viewFlipper.setInAnimation(RBSMainActivity.this, R.anim.in_from_right);
                            RBSMainActivity.this.viewFlipper.setOutAnimation(RBSMainActivity.this, R.anim.out_to_left);

                            // Show The Next Screen
                            RBSMainActivity.this.viewFlipper.showNext();
                            deactivateXTextView(RBSMainActivity.this.txtViewParams);
                            xTextViewUp(RBSMainActivity.this.txtViewResults);
                            deactivateXTextView(RBSMainActivity.this.txtViewSight);
                        }
                    }

                    }

                    return false;
                }
            });

        // Scrollview muss den Swipe selbst handeln
        this.sightCfgScrollView.setOnTouchListener(new OnTouchListener() {

                private float downX, downY, upX, upY;

                @Override
                public boolean onTouch(final View v, final MotionEvent event) {

                    switch (event.getAction()) {

                    case MotionEvent.ACTION_DOWN: {
                        this.downX = event.getX();
                        this.downY = event.getY();
                    }

                    case MotionEvent.ACTION_UP: {
                        this.upX = event.getX();
                        this.upY = event.getY();

                        float deltaX = this.upX - this.downX;
                        float deltaY = this.upY - this.downY;

                        // swipe from left to right?
                        if (deltaX > 0 && Math.abs(deltaX) > MIN_DISTANCE && Math.abs(deltaY) < MIN_DISTANCE) {

                            // set the required Animation type to ViewFlipper
                            // The Next screen will come in form Right and current Screen will go OUT to Left
                            RBSMainActivity.this.viewFlipper.setInAnimation(RBSMainActivity.this, R.anim.in_from_left);
                            RBSMainActivity.this.viewFlipper.setOutAnimation(RBSMainActivity.this, R.anim.out_to_right);

                            // Show The Next Screen
                            RBSMainActivity.this.viewFlipper.showPrevious();
                            deactivateXTextView(RBSMainActivity.this.txtViewSight);
                            xTextViewUp(RBSMainActivity.this.txtViewResults);
                            deactivateXTextView(RBSMainActivity.this.txtViewParams);
                        }
                    }

                    }

                    return false;
                }
            });


        // Touchlistener für die oberen 3 Tabs
        OnTouchListener tabListener = new OnTouchListener() {

            @Override
            public boolean onTouch(final View v, final MotionEvent event) {

                switch (event.getAction()) {

                case MotionEvent.ACTION_DOWN: {
                    deactivateAllXTextView();
                    xTextViewDown((XTextView) v);
                    return true;
                }

                case MotionEvent.ACTION_UP: {

                    RBSMainActivity.this.viewFlipper.setInAnimation(RBSMainActivity.this, R.anim.in_from_right);
                    RBSMainActivity.this.viewFlipper.setOutAnimation(RBSMainActivity.this, R.anim.out_to_left);

                    if (v == RBSMainActivity.this.txtViewParams) {
                        if (RBSMainActivity.this.viewFlipper.getDisplayedChild() != 0) {
                            RBSMainActivity.this.viewFlipper.setDisplayedChild(0);
                        }
                    } else if (v == RBSMainActivity.this.txtViewResults) {
                        if (RBSMainActivity.this.viewFlipper.getDisplayedChild() != 1) {
                            RBSMainActivity.this.viewFlipper.setDisplayedChild(1);
                        }
                    } else {
                        if (RBSMainActivity.this.viewFlipper.getDisplayedChild() != 2) {
                            RBSMainActivity.this.viewFlipper.setDisplayedChild(2);
                        }
                    }

                    xTextViewUp((XTextView) v);
                    return true;
                }

                }

                return false;
            }
        };

        // Touchlistener an Tabs anhängen
        this.txtViewParams.setOnTouchListener(tabListener);
        this.txtViewResults.setOnTouchListener(tabListener);
        this.txtViewSight.setOnTouchListener(tabListener);

        this.calcSettingBtn.setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(final View v) {
                    calculateSettings();
                }
            });
    }

    private void addValueChangeListeners() {
        this.arrowV0TxtFld.addTextChangedListener(new TextWatcher() {

                @Override
                public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { }

                @Override
                public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { }

                @Override
                public void afterTextChanged(final Editable s) {
                    if (!RBSMainActivity.this.arrowV0TxtFld.getText().toString().isEmpty()
                        && !RBSMainActivity.this.shootingDistanzTxtFld.getText().toString().isEmpty()) {
                        calculateAndSetPerformantAccuracy(Double.valueOf(RBSMainActivity.this.arrowV0TxtFld.getText().toString()),
                                                          Double.valueOf(RBSMainActivity.this.shootingDistanzTxtFld.getText().toString()));
                    }
                }
            });

        this.shootingDistanzTxtFld.addTextChangedListener(new TextWatcher() {

                @Override
                public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { }

                @Override
                public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { }

                @Override
                public void afterTextChanged(final Editable s) {
                    if (!RBSMainActivity.this.arrowV0TxtFld.getText().toString().isEmpty()
                        && !RBSMainActivity.this.shootingDistanzTxtFld.getText().toString().isEmpty()) {
                        calculateAndSetPerformantAccuracy(Double.valueOf(RBSMainActivity.this.arrowV0TxtFld.getText().toString()),
                                                          Double.valueOf(RBSMainActivity.this.shootingDistanzTxtFld.getText().toString()));
                    }
                }
            });
    }

    @Override
    public boolean onPrepareOptionsMenu(final Menu menu) {

        int size = menu.size();
        for (int i = size - 1; i >= 0; i--) {
            menu.removeItem(menu.getItem(i).getItemId());
        }

        if (this.showInfoMenu) {
            this.showInfoMenu = false;
            getMenuInflater().inflate(R.menu.rbs_info_menu, menu);
        } else {
            getMenuInflater().inflate(R.menu.rbs_main, menu);
        }

        return true;
    }


    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {

        // Handle item selection
        switch (item.getItemId()) {

        case R.id.chooseConfiguration:
            showDialog(DLG_CHOOSE_ARCHERYSETUP);
            return true;

        case R.id.doCalculation:
            calculate();
            return true;

        case R.id.addConfiguration:

            Intent intent = new Intent(this, MainConfigurationActivity.class);
            startActivityForResult(intent, 1);
            return true;

        case R.id.moreItems:
            this.showInfoMenu = true;
            closeOptionsMenu();

            new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        openOptionsMenu();
                    }
                });


            return true;

        case R.id.itemAbout:
            showDialog(DLG_ABOUT);
            return true;

        default:
            return super.onOptionsItemSelected(item);
        }
    }


    @Override
    public boolean onTouchEvent(final MotionEvent touchevent) {
        switch (touchevent.getAction()) {

        // when user first touches the screen to swap
        case MotionEvent.ACTION_DOWN: {
            this.lastX = touchevent.getX();
            break;
        }

        case MotionEvent.ACTION_UP: {
            float currentX = touchevent.getX();

            // if right to left swipe on screen
            if (this.lastX > currentX) {
                if (this.viewFlipper.getDisplayedChild() == 2) {
                    break;
                }

                // set the required Animation type to ViewFlipper
                // The Next screen will come in form Right and current Screen will go OUT to Left
                this.viewFlipper.setInAnimation(this, R.anim.in_from_right);
                this.viewFlipper.setOutAnimation(this, R.anim.out_to_left);

                // Show The Next Screen
                this.viewFlipper.showNext();

            } else if (this.lastX < currentX) { // if left to right swipe on screen

                // If no more View/Child to flip
                if (this.viewFlipper.getDisplayedChild() == 0) {
                    break;
                }

                // set the required Animation type to ViewFlipper
                // The Next screen will come in form Left and current Screen will go OUT to Right
                this.viewFlipper.setInAnimation(this, R.anim.in_from_left);
                this.viewFlipper.setOutAnimation(this, R.anim.out_to_right);

                // Show the previous Screen
                this.viewFlipper.showPrevious();
            }

            deactivateAllXTextView();

            switch (this.viewFlipper.getDisplayedChild()) {
            case 0:
                xTextViewUp(this.txtViewParams);
                break;
            case 1:
                xTextViewUp(this.txtViewResults);
                break;
            case 2:
                xTextViewUp(this.txtViewSight);
                break;
            default:
                break;
            }

            break;
        }
        }
        return false;
    }


    private void xTextViewDown(final XTextView view) {
        view.setDrawborderColor(getResources().getColor(R.color.android_yellow_dark));
        view.setTextColor(getResources().getColor(R.color.android_yellow_dark));
    }

    private void xTextViewUp(final XTextView view) {
        view.setDrawborderColor(getResources().getColor(R.color.android_yellow));
        view.setTextColor(getResources().getColor(R.color.android_yellow));
    }

    private void deactivateXTextView(final XTextView view) {
        view.setDrawborderColor(getResources().getColor(R.color.white));
        view.setTextColor(getResources().getColor(R.color.white));

    }

    private void deactivateAllXTextView() {
        deactivateXTextView(this.txtViewParams);
        deactivateXTextView(this.txtViewResults);
        deactivateXTextView(this.txtViewSight);
    }

    @Override
    protected Dialog onCreateDialog(final int id) {
        return DialogFactory.createDialog(id, this);
    }

    @Override
    public void onArcherySetupDlgDismissed(final IArcherySetup archerySetup) {
        this.selectedArcherySetup = archerySetup;
        if (archerySetup != null && archerySetup.getId() != -999) {
            this.arrowDiameterTxtFld.setText(DF.format(archerySetup.getArrowDiameter()));
            this.arrowCwTxtFld.setText(DF.format(archerySetup.getArrowCw()));
            this.arrowWeightTxtFld.setText(DF.format(archerySetup.getArrowWeight()));
            this.bowPulloutTxtFld.setText(DF.format(archerySetup.getBowPullout()));
            this.arrowV0TxtFld.setText(DF.format(archerySetup.getArrowV0()));
            this.hNATxtFld.setText(DF.format(archerySetup.getHNA()));
            this.nockRaisingTxtFld.setText(DF.format(archerySetup.getNockRaising()));
            this.braceHeightTxtFld.setText(DF.format(archerySetup.getBraceHeight()));
            this.arrowNockHeightTxtFld.setText(DF_1.format(archerySetup.getArrowNockHeight()));
            this.targetCenterHeightTxtFld.setText(DF_1.format(archerySetup.getArrowCenterHeight()));
            this.calcTimeIntervalCboBox.setSelectionByVal(archerySetup.getDeltaTime().toString());
            this.calcAccuracyCboBox.setSelectionByVal(archerySetup.getCalcPrecision().toString());
            this.scRiserSpinner.setSelectedRiser(archerySetup.getRiser());
            this.scRiserSpinner.fireSelectionChangedEvent();
            this.scSightSpinner.setSelectedSight(archerySetup.getSight());
            this.scSightSpinner.fireSelectionChangedEvent();
            this.scVertScaleMiddleTxtFld.setText(DF.format(archerySetup.getSightVertSkalaMiddle()));

            this.airTemperatureTxtFld.setText(DEF_TEMP);
            this.airRelativeHumidityTxtFld.setText(DEF_HUMIDITY);
            this.shootingDistanzTxtFld.setText(DEF_DISTANCE);

        } else {
            this.arrowDiameterTxtFld.setText("");
            this.arrowDiameterTxtFld.setText("");
            this.arrowCwTxtFld.setText("");
            this.arrowWeightTxtFld.setText("");
            this.bowPulloutTxtFld.setText("");
            this.arrowV0TxtFld.setText("");
            this.hNATxtFld.setText("");
            this.nockRaisingTxtFld.setText("");
            this.braceHeightTxtFld.setText("");
            this.arrowNockHeightTxtFld.setText("");
            this.targetCenterHeightTxtFld.setText("");
            this.calcTimeIntervalCboBox.setSelectionByVal("");
            this.calcAccuracyCboBox.setSelectionByVal("");
            this.scRiserSpinner.setSelection(0);
            this.scRiserSpinner.fireSelectionChangedEvent();
            this.scSightSpinner.setSelection(0);
            this.scSightSpinner.fireSelectionChangedEvent();
            this.scVertScaleMiddleTxtFld.setText("");
        }
    }

    private boolean allParamFieldsValid() {

        return !this.arrowDiameterTxtFld.getText().toString().isEmpty()
               && !this.arrowCwTxtFld.getText().toString().isEmpty()
               && !this.arrowWeightTxtFld.getText().toString().isEmpty()
               && !this.bowPulloutTxtFld.getText().toString().isEmpty()
               && !this.arrowV0TxtFld.getText().toString().isEmpty()
               && !this.hNATxtFld.getText().toString().isEmpty()
               && !this.nockRaisingTxtFld.getText().toString().isEmpty()
               && !this.braceHeightTxtFld.getText().toString().isEmpty()
               && !this.airTemperatureTxtFld.getText().toString().isEmpty()
               && !this.airRelativeHumidityTxtFld.getText().toString().isEmpty()
               && !this.shootingDistanzTxtFld.getText().toString().isEmpty()
               && !this.arrowNockHeightTxtFld.getText().toString().isEmpty()
               && !this.targetCenterHeightTxtFld.getText().toString().isEmpty()
               && this.calcTimeIntervalCboBox.getSelectedItem() != null
               && this.calcAccuracyCboBox.getSelectedItem() != null
               && allSightParamFieldsValid();
    }

    private boolean allSightParamFieldsValid() {
        return !this.scVvisHTxtFld.getText().toString().isEmpty()
               && !this.scVvisVTxtFld.getText().toString().isEmpty()
               && !this.scaHTxtFld.getText().toString().isEmpty()
               && !this.scaVTxtFld.getText().toString().isEmpty()
               && !this.scVertScaleRngTxtFld.getText().toString().isEmpty()
               && ((this.scHeVSSpinner.getVisibility() == View.VISIBLE && this.scHeVSSpinner.getSelectedItem() != null)
                   || (this.scHeVSTxtFld.getVisibility() == View.VISIBLE && !this.scHeVSTxtFld.getText().toString().isEmpty()))
               && !this.scVertScaleMiddleTxtFld.getText().toString().isEmpty();
    }

    /**
     * Startet die Berechnung wenn alle notwendigen Felder gefüllt sind.
     */
    private void calculate() {

        if (allParamFieldsValid()) {

            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            if (getCurrentFocus() != null) {
                imm.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
            }

            this.progressDialog = ProgressDialog.show(this, null, getResources().getString(R.string.caption_Calculating), true, false);

            CalculationTask calcTask = new CalculationTask();
            TrajectoryCalculator calculator = new TrajectoryCalculator(Double.valueOf(this.arrowDiameterTxtFld.getText().toString().trim()) * MILLI, //
                                                                       Double.valueOf(this.arrowCwTxtFld.getText().toString().trim()), //
                                                                       Double.valueOf(this.arrowWeightTxtFld.getText().toString().trim()) * MILLI, //
                                                                       Double.valueOf(this.arrowV0TxtFld.getText().toString().trim()), //
                                                                       Double.valueOf(this.shootingDistanzTxtFld.getText().toString().trim()), //
                                                                       Double.valueOf(this.targetCenterHeightTxtFld.getText().toString().trim())
                                                                       - Double.valueOf(this.arrowNockHeightTxtFld.getText().toString().trim()), //
                                                                       Double.valueOf(this.airTemperatureTxtFld.getText().toString().trim()), //
                                                                       Double.valueOf(this.airRelativeHumidityTxtFld.getText().toString().trim())
                                                                       * CENTI,                      //
                                                                       Double.valueOf(this.calcTimeIntervalCboBox.getSelectedItem().toString().trim()), //
                                                                       Double.valueOf(this.calcAccuracyCboBox.getSelectedItem().toString().trim())
                                                                       * CENTI,                      //
                                                                       this);
            calculator.setStartAngle(0d);
            calcTask.execute(calculator);

        } else {
            Toast toast = Toast.makeText(this, getResources().getString(R.string.caption_FillAllMandatoryFields), Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            toast.show();
        }
    }

    @Override
    public void updateTrajectoryTxtFld(final Integer step, final Double[] flightCurveRow, final Double startAngle, final Double[] maxHCurve) {

        int width = this.flightCurveTxtFld.getMeasuredWidth();
        Double[] r1 = flightCurveRow;

        switch (step) {

        case 0:
            this.flightCurveTxtFld.setText("");
            this.flightCurveTxtFld.append(getResources().getString(R.string.fcChart_StartAngle) + DF_FC.format(startAngle) + " °\n\n");
            this.flightCurveTxtFld.append(getResources().getString(R.string.fcChart_MaxHeight) + DF_FC.format(maxHCurve[0])
                                          + " m / "
                                          + DF_FC.format(maxHCurve[0] + Double.valueOf(this.arrowNockHeightTxtFld.getText().toString().trim()))
                                          + " m,  [t = " + DF_FC.format(maxHCurve[1]) + " s]\n\n");
            this.flightCurveTxtFld.append(getResources().getString(R.string.fcChart_FlightDuration)
                                          + DF_FC.format(r1[TrajectoryCalculator.IDX_T])
                                          + " sec\n\n");


            SpannableString line = new SpannableString("t [sec]\tVx [m/s]\tVy [m/s]\tx [m]\ty [m]\n\n");
            line.setSpan(new TabStopSpan.Standard(width / 5), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 2), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 3), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 4), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            this.flightCurveTxtFld.append(line);
            break;

        case 1:
            line = new SpannableString(DF_FC.format(r1[TrajectoryCalculator.IDX_T]) + '\t'
                                       + DF_FC.format(r1[TrajectoryCalculator.IDX_VX]) + '\t'
                                       + DF_FC.format(r1[TrajectoryCalculator.IDX_VY]) + '\t'
                                       + DF_FC.format(r1[TrajectoryCalculator.IDX_X]) + '\t'
                                       + DF_FC.format(r1[TrajectoryCalculator.IDX_Y]) + "\n");
            line.setSpan(new TabStopSpan.Standard(width / 5), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 2), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 3), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            line.setSpan(new TabStopSpan.Standard(width / 5 * 4), 0, line.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            this.flightCurveTxtFld.append(line);
            break;

        default:
            break;
        }
    }

    @Override
    public void onCalculationDone(final Double[][] flightCurve, final Double startAngle, final Double[] maxHCurve) {
        this.startAngle = startAngle;

        updateFlightCurveChart(flightCurve);
        calculateSettings();

        if (this.progressDialog != null) {
            this.progressDialog.dismiss();
        }

        this.txtViewSight.doSingleTouch();
    }


    /**
     * @param  flightCurve
     */
    private void updateFlightCurveChart(final Double[][] flightCurve) {

        XYSeries serie = new XYSeries(getApplicationContext().getResources().getString(R.string.fcChart_TitleLable));

        for (int i = 0; i < flightCurve.length; i++) {
            serie.add(flightCurve[i][TrajectoryCalculator.IDX_X], flightCurve[i][TrajectoryCalculator.IDX_Y]);
        }

        this.flightCurveChart.getDataSet().removeSeries(0);
        this.flightCurveChart.getDataSet().addSeries(0, serie);
        this.flightCurveChart.updateRendererAfterCalculation(flightCurve[flightCurve.length - 1][TrajectoryCalculator.IDX_X], flightCurve);
    }


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

        if (allSightParamFieldsValid() && this.selectedArcherySetup != null && this.startAngle != null) {
            Double x0;
            Double y0;
            Double x1;
            Double y1;
            Double x2;
            Double y2;
            Double x3;
            Double y3;
            Double r;
            Double s;
            Double vVh = Double.valueOf(this.scVvisHTxtFld.getText().toString()) + Double.valueOf(this.scaHTxtFld.getText().toString());
            Double vVv = Double.valueOf(this.scVvisVTxtFld.getText().toString()) + Double.valueOf(this.scaVTxtFld.getText().toString());
            Double a = Double.valueOf(this.bowPulloutTxtFld.getText().toString()) * 10;
            Double heVs = this.scHeVSSpinner.getVisibility() == View.VISIBLE ? Double.valueOf(this.scHeVSSpinner.getSelectedItem().toString())
                                                                             : Double.valueOf(this.scHeVSTxtFld.getText().toString());
            Double lhNA = this.selectedArcherySetup.getHNA();
            Double dltH = (Double.valueOf(this.targetCenterHeightTxtFld.getText().toString())
                           - Double.valueOf(this.arrowNockHeightTxtFld.getText().toString())) * Math.pow(10, 3);
            Double ldistH = Double.valueOf(this.shootingDistanzTxtFld.getText().toString()) * Math.pow(10, 3);
            Double minSkala = this.scVertScaleRngTxtFld.getText().toString().contains("-")
                              ? Double.valueOf(this.scVertScaleRngTxtFld.getText().toString().substring(0,
                                                                                                        this.scVertScaleRngTxtFld.getText().toString()
                                                                                                                                 .indexOf('-'))) : 0d;
            Double maxSkala = this.scVertScaleRngTxtFld.getText().toString().contains("-")
                              ? Double.valueOf(this.scVertScaleRngTxtFld.getText().toString().substring(this.scVertScaleRngTxtFld.getText().toString()
                                                                                                                                 .indexOf('-') + 1,
                                                                                                        this.scVertScaleRngTxtFld.getText().toString()
                                                                                                                                 .length())) : 0d;
            Double nockRaising = Double.valueOf(this.nockRaisingTxtFld.getText().toString())
                                 - Double.valueOf(this.arrowDiameterTxtFld.getText().toString());
            Double braceHeight = Double.valueOf(this.braceHeightTxtFld.getText().toString());
            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(this.startAngle)) * x01 - Math.sin(Math.toRadians(this.startAngle)) * y01;
            y0 = Math.sin(Math.toRadians(this.startAngle)) * x01 + Math.cos(Math.toRadians(this.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(this.startAngle)) * x02 - Math.sin(Math.toRadians(this.startAngle)) * y02;
            y2 = Math.sin(Math.toRadians(this.startAngle)) * x02 + Math.cos(Math.toRadians(this.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(this.startAngle)) * x03 - Math.sin(Math.toRadians(this.startAngle)) * y03;
            y3 = Math.sin(Math.toRadians(this.startAngle)) * x03 + Math.cos(Math.toRadians(this.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 = Double.valueOf(this.scVertScaleMiddleTxtFld.getText().toString()) + d;

            this.scEvSValue.setText(DF_EVS.format(sV));

            if (minSkala <= sV && sV <= maxSkala) {
                this.scEvSValue.setBackgroundDrawable(getResources().getDrawable(R.drawable.evs_ok_background));
            } else {
                this.scEvSValue.setBackgroundDrawable(getResources().getDrawable(R.drawable.evs_nok_background));
            }

        } else {
            this.scEvSValue.setText("");

            Toast toast = Toast.makeText(this, getResources().getString(R.string.caption_FillAllMandatoryFields), Toast.LENGTH_LONG);
            toast.setGravity(Gravity.CENTER, 0, 0);
            toast.show();
        }
    }

    private void calculateAndSetPerformantAccuracy(final Double v0, final Double shootingDistance) {

        String[] calcAccuracies = getResources().getStringArray(R.array.CalculationTimeIntervals);
        String calcAccuracyToSet = calcAccuracies[calcAccuracies.length - 1];
        Double approxFlightTime = shootingDistance / v0;
        Double approxIntervalTime = approxFlightTime / MAX_TRAJ_POINTS;

        for (int i = 0; i < calcAccuracies.length - 1; i++) {
            Double accLow = Double.valueOf(calcAccuracies[i]);
            Double accHigh = Double.valueOf(calcAccuracies[i + 1]);
            if (accHigh < approxIntervalTime && approxIntervalTime <= accLow) {
                calcAccuracyToSet = calcAccuracies[i];
                break;
            }
        }

        this.calcTimeIntervalCboBox.setSelectionByVal(calcAccuracyToSet);
    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == 1) {
            loadRiserSpinner();
            loadSightSpinner();
        }
    }

}
