package com.thibaudperso.sonycamera.timelapse.ui.connection;

import android.Manifest;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.thibaudperso.sonycamera.R;
import com.thibaudperso.sonycamera.sdk.model.Device;
import com.thibaudperso.sonycamera.timelapse.TimelapseApplication;
import com.thibaudperso.sonycamera.timelapse.control.DeviceManager;
import com.thibaudperso.sonycamera.timelapse.control.connection.NFCHandler;
import com.thibaudperso.sonycamera.timelapse.control.connection.StateMachineConnection;
import com.thibaudperso.sonycamera.timelapse.ui.adjustments.AdjustmentsActivity;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import static com.thibaudperso.sonycamera.timelapse.Constants.PREF_AUTOMATIC_CONTINUE;
import static com.thibaudperso.sonycamera.timelapse.control.connection.StateMachineConnection.State.BAD_API_ACCESS;
import static com.thibaudperso.sonycamera.timelapse.control.connection.StateMachineConnection.State.CHECK_API;
import static com.thibaudperso.sonycamera.timelapse.control.connection.StateMachineConnection.State.GOOD_API_ACCESS;
import static com.thibaudperso.sonycamera.timelapse.control.connection.StateMachineConnection.State.WIFI_DISABLED;

public class ConnectionFragment extends Fragment {

    private final static int ADJUSTMENTS_ACTIVITY_RESULT = 0x1;
    private static final int PERMISSIONS_REQUEST_COARSE_LOCATION = 1;
    public static final String EXTRA_EXIT = "exit";

    private TimelapseApplication mApplication;

    private DeviceManager mDeviceManager;
    private StateMachineConnection mStateMachineConnection;

    private NfcAdapter mNfcAdapter;

    private AlertDialog mAlertDialogChooseNetworkConnection,
            mAlertDialogAskForPassword,
            mAlertDialogChooseNetworkCreation;

    private Spinner mCameraSpinner;
    private ArrayAdapter<Device> mAdapter;

    private ImageView mConnectionInfoWifiEnabled, mConnectionInfoNetworkState,
            mConnectionInfoAPIState;
    private ProgressBar mConnectionInfoWifiEnabledProgress, mConnectionInfoNetworkStateProgress,
            mConnectionInfoAPIStateProgress;
    private ImageView mConnectionDeviceListUpdateButton;
    private ProgressBar mConnectionDeviceListUpdateProgress;

    private boolean mAutomaticContinue = true;
    private boolean mSkipNextState = false;

    private CheckBox mConnectionAutomaticCheckbox;
    private FloatingActionButton mConnectionContinueButton;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mApplication = (TimelapseApplication) getActivity().getApplication();

        mDeviceManager = mApplication.getDeviceManager();
        mStateMachineConnection = mApplication.getStateMachineConnection();

        mNfcAdapter = NfcAdapter.getDefaultAdapter(getContext());
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View viewResult = inflater.inflate(R.layout.fragment_connection, container, false);

        mConnectionDeviceListUpdateButton = viewResult.findViewById(R.id.connection_camera_list_update);
        mConnectionDeviceListUpdateProgress = viewResult.findViewById(R.id.connection_camera_list_update_progress);
        mConnectionInfoWifiEnabled = viewResult.findViewById(R.id.connection_info_wifi_enabled_icon);
        mConnectionInfoNetworkState = viewResult.findViewById(R.id.connection_info_network_state);
        mConnectionInfoAPIState = viewResult.findViewById(R.id.connection_info_api_state);
        mConnectionInfoWifiEnabledProgress = viewResult.findViewById(R.id.connection_info_wifi_enabled_progress);
        mConnectionInfoNetworkStateProgress = viewResult.findViewById(R.id.connection_info_network_state_progress);
        mConnectionInfoAPIStateProgress = viewResult.findViewById(R.id.connection_info_api_state_progress);
        ((TextView) viewResult.findViewById(R.id.connection_info_message)).setText(
                Html.fromHtml(getString(R.string.connection_information_message)));


        /*
         * Handle Camera spinner
         */
        mCameraSpinner = viewResult.findViewById(R.id.connection_camera_spinner);

        mAdapter = new ArrayAdapter<>(getActivity(),
                android.R.layout.simple_list_item_1, mDeviceManager.getDevices());
        mCameraSpinner.setAdapter(mAdapter);

        updateList();

        // http://stackoverflow.com/a/9375624/2239938
        mCameraSpinner.post(new Runnable() {
            @Override
            public void run() {
                mCameraSpinner.setOnItemSelectedListener(new OnItemSelectedListener() {

                    @Override
                    public void onItemSelected(AdapterView<?> arg0, View view,
                                               int position, long id) {
                        mDeviceManager.setSelectedDevice((Device) mCameraSpinner.getItemAtPosition(position));
                    }

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

        viewResult.findViewById(R.id.connection_camera_list_update).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mConnectionDeviceListUpdateButton.setVisibility(View.GONE);
                mConnectionDeviceListUpdateProgress.setVisibility(View.VISIBLE);
                mDeviceManager.getLastListOfDevicesFromInternet(new DeviceManager.Listener() {
                    @Override
                    public void onDevicesListChanged(List<Device> devices) {
                        mAdapter.notifyDataSetChanged();
                        updateList();

                        if(getContext() != null) {
                            Toast.makeText(getContext(), R.string.connection_camera_list_updated,
                                    Toast.LENGTH_SHORT).show();
                        }

                        if (mConnectionDeviceListUpdateButton != null &&
                                mConnectionDeviceListUpdateProgress != null) {
                            mConnectionDeviceListUpdateButton.setVisibility(View.VISIBLE);
                            mConnectionDeviceListUpdateProgress.setVisibility(View.GONE);
                        }
                    }
                });
            }
        });


        mConnectionAutomaticCheckbox = viewResult.findViewById(R.id.connection_automatic_checkbox);
        mConnectionAutomaticCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                final SharedPreferences.Editor editor = PreferenceManager
                        .getDefaultSharedPreferences(getContext()).edit();
                editor.putBoolean(PREF_AUTOMATIC_CONTINUE, isChecked);
                editor.apply();
            }
        });
        mConnectionContinueButton = viewResult.findViewById(R.id.connection_settings_button);
        mConnectionContinueButton.setVisibility(View.GONE);
        mConnectionContinueButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                nextStep();
            }
        });

        initConnectionInfo();

        return viewResult;
    }

    private void updateList() {
        mAdapter.sort(Device.COMPARE_BY_DEVICEMODEL);

        int defaultPosition = mAdapter.getPosition(mDeviceManager.getSelectedDevice());
        mCameraSpinner.setSelection(defaultPosition, false);
    }

    @Override
    public void onResume() {
        super.onResume();

        final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mApplication);
        mAutomaticContinue = preferences.getBoolean(PREF_AUTOMATIC_CONTINUE, mAutomaticContinue);
        mConnectionAutomaticCheckbox.setChecked(mAutomaticContinue);


        initConnectionInfo();

        updateUI(mStateMachineConnection.getCurrentState());

        mStateMachineConnection.addListener(mStateMachineListener);

        if (mNfcAdapter != null) {
            mNfcAdapter.enableForegroundDispatch(getActivity(),
                    NFCHandler.getPendingIntent(getActivity()),
                    NFCHandler.getIntentFilterArray(), NFCHandler.getTechListArray());
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        mStateMachineConnection.removeListener(mStateMachineListener);

        if (mNfcAdapter != null) {
            mNfcAdapter.disableForegroundDispatch(getActivity());
        }
    }

    @Override
    public void onStop() {
        super.onStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        if (mAlertDialogChooseNetworkConnection != null) {
            mAlertDialogChooseNetworkConnection.cancel();
        }

        if (mAlertDialogAskForPassword != null) {
            mAlertDialogAskForPassword.cancel();
        }

        if (mAlertDialogChooseNetworkCreation != null) {
            mAlertDialogChooseNetworkCreation.cancel();
        }
    }

    private StateMachineConnection.Listener
            mStateMachineListener = new StateMachineConnection.Listener() {
        @Override
        public void onNewState(StateMachineConnection.State previousState,
                               StateMachineConnection.State newState) {
            updateUI(newState);
        }
    };

    private void updateUI(StateMachineConnection.State newState) {

        if (mSkipNextState) {
            mSkipNextState = false;
            return;
        }

        if (Collections.singletonList(GOOD_API_ACCESS).contains(newState)) {
            apiOk();
        }

        if (Arrays.asList(GOOD_API_ACCESS, BAD_API_ACCESS, CHECK_API).contains(newState)) {
            wifiOk();
        }

        if (!Collections.singletonList(WIFI_DISABLED).contains(newState)) {
            wifiEnabledOk();
        }

        switch (newState) {

            case WIFI_ENABLED:
                wifiEnabledOk();
                break;

            case WIFI_DISABLED:
                wifiEnabledError();
                break;

            case WIFI_CONNECTED:
                wifiError();
                apiError();
                break;

            case WIFI_DISCONNECTED:
                wifiError();
                apiError();
                break;

            case SONY_WIFI:
                wifiOk();
                break;

            case CHECK_API:
                apiProgress();
                break;

            case NOT_SONY_WIFI:
                wifiError();
                break;

            case BAD_API_ACCESS:
                apiError();
                break;

            case GOOD_API_ACCESS:
                apiOk();
                stepFinished();
                break;

            case WIFI_SCAN:
                wifiProgress();
                break;

            case NO_WIFI_SCAN_PERMISSION:
                askForScanPermission();
                break;

            case MULTIPLE_SONY_CONF_DETECTED:
                selectNetworkForConnection(mStateMachineConnection.getWifiConfigurations());
                break;

            case ASK_PASSWORD_FOR_WIFI:
                askForNetworkPasswordThenConnect(mStateMachineConnection.getScanResults().get(0));
                break;

            case MULTIPLE_SONY_SCAN_DETECTED:
                selectNetworkForCreation(mStateMachineConnection.getScanResults());
                break;


        }
    }



	/*
     * Wifi
	 */

    private void askForScanPermission() {

        String permission = Manifest.permission.ACCESS_COARSE_LOCATION;

        if (!ActivityCompat.shouldShowRequestPermissionRationale(getActivity(), permission)) {

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setMessage(R.string.connection_permission_message);
            builder.setPositiveButton(R.string.connection_permission_ok,
                    new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                                    PERMISSIONS_REQUEST_COARSE_LOCATION);
                        }
                    });
            builder.setNegativeButton(R.string.connection_permission_cancel,
                    new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Toast.makeText(getContext(), R.string.connection_permission_cancel_message,
                                    Toast.LENGTH_LONG).show();
                        }
                    });

            builder.create().show();

        } else {
            requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},
                    PERMISSIONS_REQUEST_COARSE_LOCATION);
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {

        if (requestCode == PERMISSIONS_REQUEST_COARSE_LOCATION
                && grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

            mStateMachineConnection.notifyWifiScanPermissionAccepted();

        } else {
            Toast.makeText(getContext(), R.string.connection_permission_cancel_message,
                    Toast.LENGTH_LONG).show();
        }

    }



    /*
     * Handle network prompts
	 */

    private void selectNetworkForConnection(final List<WifiConfiguration> wifiConfigurations) {

        final ListView listView = new ListView(getActivity());

        ListAdapter adapter = new ArrayAdapter<WifiConfiguration>(getActivity(),
                android.R.layout.simple_list_item_1, wifiConfigurations) {

            @NonNull
            public View getView(int position, View convertView, @NonNull ViewGroup parent) {

                View view = super.getView(position, convertView, parent);
                TextView textView = view.findViewById(android.R.id.text1);
                WifiConfiguration network = getItem(position);
                if (network != null) {
                    textView.setText(network.SSID);
                }
                return textView;
            }
        };

        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, final View view,
                                    int position, long id) {

                WifiConfiguration wc = (WifiConfiguration) parent.getItemAtPosition(position);
                mStateMachineConnection.tryToConnectToNetworkId(wc.networkId);
            }
        });

        mAlertDialogChooseNetworkConnection = new AlertDialog.Builder(getActivity())
                .setTitle(R.string.connection_choose_network)
                .setView(listView)
                .setOnCancelListener(new DialogInterface.OnCancelListener() {
                    @Override
                    public void onCancel(DialogInterface dialog) {
                        wifiError();
                    }
                })
                .setNegativeButton(R.string.connection_choose_network_cancel,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                wifiError();
                            }
                        }).show();

    }

    private void askForNetworkPasswordThenConnect(final ScanResult scanResult) {

        final EditText input = new EditText(getActivity());

        mAlertDialogAskForPassword = new AlertDialog.Builder(getActivity())
                .setTitle(String.format(getString(R.string.connection_enter_password), scanResult.SSID))
                .setView(input)
                .setPositiveButton(R.string.connection_enter_password_ok,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {

                                String value = input.getText().toString();
                                mStateMachineConnection.createNetwork(scanResult.SSID, value);
                            }
                        })
                .setNegativeButton(R.string.connection_enter_password_cancel,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                // Do nothing.
                            }
                        }).show();

    }

    private void selectNetworkForCreation(final List<ScanResult> scanResults) {

        final ListView listView = new ListView(getActivity());

        ListAdapter adapter = new ArrayAdapter<ScanResult>(getActivity(),
                android.R.layout.simple_list_item_1, scanResults) {

            @NonNull
            public View getView(int position, View convertView, @NonNull ViewGroup parent) {

                View view = super.getView(position, convertView, parent);
                TextView textView = view.findViewById(android.R.id.text1);
                textView.setText((getItem(position)).SSID);
                return textView;
            }
        };

        listView.setAdapter(adapter);
        listView.setOnItemClickListener(new OnItemClickListener() {

            @Override
            public void onItemClick(AdapterView<?> parent, final View view,
                                    int position, long id) {

                ScanResult scanResult = (ScanResult) parent.getItemAtPosition(position);
                askForNetworkPasswordThenConnect(scanResult);
            }
        });

        mAlertDialogChooseNetworkCreation = new AlertDialog.Builder(getActivity())
                .setTitle(R.string.connection_choose_network)
                .setView(listView)
                .setNegativeButton(R.string.connection_choose_network_cancel,
                        new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface dialog, int whichButton) {
                                // Do nothing.
                            }
                        }).show();

    }


    private void initConnectionInfo() {
        wifiEnabledError();
        wifiError();
        apiError();
        mConnectionContinueButton.setVisibility(View.GONE);
    }


    private void wifiEnabledError() {
        mConnectionInfoWifiEnabledProgress.setVisibility(View.GONE);
        mConnectionInfoWifiEnabled.setVisibility(View.VISIBLE);
        mConnectionInfoWifiEnabled.setImageResource(R.drawable.error);
    }

    private void wifiEnabledOk() {
        mConnectionInfoWifiEnabledProgress.setVisibility(View.GONE);
        mConnectionInfoWifiEnabled.setVisibility(View.VISIBLE);
        mConnectionInfoWifiEnabled.setImageResource(R.drawable.ok);
    }

    private void wifiProgress() {
        mConnectionInfoNetworkState.setVisibility(View.GONE);
        mConnectionInfoNetworkStateProgress.setVisibility(View.VISIBLE);
    }

    private void wifiError() {
        mConnectionInfoNetworkStateProgress.setVisibility(View.GONE);
        mConnectionInfoNetworkState.setVisibility(View.VISIBLE);
        mConnectionInfoNetworkState.setImageResource(R.drawable.error);
    }

    private void wifiOk() {
        mConnectionInfoNetworkStateProgress.setVisibility(View.GONE);
        mConnectionInfoNetworkState.setVisibility(View.VISIBLE);
        mConnectionInfoNetworkState.setImageResource(R.drawable.ok);
    }


    private void apiProgress() {
        mConnectionInfoAPIState.setVisibility(View.GONE);
        mConnectionInfoAPIStateProgress.setVisibility(View.VISIBLE);
    }

    private void apiError() {
        mConnectionInfoAPIStateProgress.setVisibility(View.GONE);
        mConnectionInfoAPIState.setVisibility(View.VISIBLE);
        mConnectionInfoAPIState.setImageResource(R.drawable.error);
    }

    private void apiOk() {
        mConnectionInfoAPIStateProgress.setVisibility(View.GONE);
        mConnectionInfoAPIState.setVisibility(View.VISIBLE);
        mConnectionInfoAPIState.setImageResource(R.drawable.ok);
    }


    private void stepFinished() {
        if (mConnectionAutomaticCheckbox.isChecked()) {
            nextStep();
        } else {
            mConnectionContinueButton.setVisibility(View.VISIBLE);
        }
    }

    private void nextStep() {
        ((TimelapseApplication) getActivity().getApplication()).getCameraAPI().initializeWS();
        startActivityForResult(new Intent(getContext(), AdjustmentsActivity.class),
                ADJUSTMENTS_ACTIVITY_RESULT);
    }

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

        if (requestCode == ADJUSTMENTS_ACTIVITY_RESULT) {
            if (data != null && data.hasExtra(EXTRA_EXIT)
                    && data.getExtras().getBoolean(EXTRA_EXIT, false)) {
                getActivity().finish();
            }
        }

        // This variable is used as a workaround for the long asynchronous time of disconnection
        mSkipNextState = true;
    }

}