package eu.dnetlib.client.adminpanel;

import com.github.gwtbootstrap.client.ui.*;
import com.github.gwtbootstrap.client.ui.constants.*;
import com.github.gwtbootstrap.client.ui.incubator.NameValuePair;
import com.github.gwtbootstrap.client.ui.incubator.NameValuePairImpl;
import com.github.gwtbootstrap.client.ui.incubator.PickList;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Label;
import com.sencha.gxt.widget.core.client.info.Info;
import eu.dnetlib.client.Admin;
import eu.dnetlib.espas.gui.client.FormFieldSet;
import eu.dnetlib.espas.gui.client.user.DataProviderAccessService;
import eu.dnetlib.espas.gui.client.user.DataProviderAccessServiceAsync;
import eu.dnetlib.espas.gui.shared.DataProvider;
import eu.dnetlib.espas.gui.shared.User;
import eu.dnetlib.espas.gui.shared.UserAccessException;

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

/**
 * Created by stefania on 10/2/15.
 */
public class DataProviderFormModal {

    private Modal dataProviderModal = new Modal();

    private FlowPanel dataProviderRegistrationPanel = new FlowPanel();

    private Alert errorLabel = new Alert();

    private Form dataProviderRegistrationForm = new Form();
    private TextBox nameTextBox = new TextBox();
    private TextBox shortNameTextBox = new TextBox();
    private TextBox namespaceTextBox = new TextBox();
    private TextBox wrapperURLTextBox = new TextBox();
    private TextBox sosServiceURLTextBox = new TextBox();
    private TextBox latitudeTextBox = new TextBox();
    private TextBox longitudeTextBox = new TextBox();
    private TextArea termsTextArea = new TextArea();
    private PickList administrators = new PickList();

    private List<NameValuePair> administratorsList = new ArrayList<NameValuePair>();

    private ModalFooter modalFooter = new ModalFooter();
    private FlowPanel actionButtons = new FlowPanel();
    private Button cancelButton = new Button();
    private Button saveButton = new Button();

    private DataProviderFormListener dataProviderFormListener;

    private DataProviderAccessServiceAsync dataProviderAccessService = GWT.create(DataProviderAccessService.class);

    public DataProviderFormModal(final DataProvider dataProvider) {

        if(dataProvider!=null)
            dataProviderModal.setTitle("Edit existing data provider");
        else
            dataProviderModal.setTitle("Add a new data provider");

        dataProviderModal.add(dataProviderRegistrationPanel);

        errorLabel.setType(AlertType.ERROR);
        errorLabel.setVisible(false);
        errorLabel.setClose(false);

        dataProviderRegistrationPanel.add(errorLabel);

        dataProviderRegistrationPanel.add(dataProviderRegistrationForm);

        nameTextBox.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("Name (*)", nameTextBox));

        shortNameTextBox.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("Short Name (*)", shortNameTextBox));

        namespaceTextBox.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("Namespace (*)", namespaceTextBox));

        wrapperURLTextBox.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("Wrapper URL (*)", wrapperURLTextBox));

        sosServiceURLTextBox.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("SOS Service URL", sosServiceURLTextBox));

        latitudeTextBox.setAlternateSize(AlternateSize.LARGE);
        FormFieldSet latitudeFormFieldSet = new FormFieldSet("Latitude (*)", latitudeTextBox);
        latitudeFormFieldSet.asWidget().addStyleName("inlineBlock");
        latitudeFormFieldSet.asWidget().addStyleName("firstInlineFieldSet");
        dataProviderRegistrationForm.add(latitudeFormFieldSet);

        longitudeTextBox.setAlternateSize(AlternateSize.LARGE);
        FormFieldSet longitudeFormFieldSet = new FormFieldSet("Longitude (*)", longitudeTextBox);
        longitudeFormFieldSet.asWidget().addStyleName("inlineBlock");
        dataProviderRegistrationForm.add(longitudeFormFieldSet);

        termsTextArea.setAlternateSize(AlternateSize.XXLARGE);
        dataProviderRegistrationForm.add(new FormFieldSet("Terms of Reference", termsTextArea));

        for (User adminUser : Admin.dataProviderAdministrators) {
            NameValuePairImpl adminUserNameValuePair = new NameValuePairImpl(adminUser.getName(), adminUser.getEmail());
            administratorsList.add(adminUserNameValuePair);
        }
        administrators.setLeftListElements(administratorsList);

        dataProviderRegistrationForm.add(new FormFieldSet("Administrators", administrators));

        actionButtons.addStyleName("confirmationModalButtons");

        modalFooter.add(actionButtons);
        dataProviderModal.add(modalFooter);

        cancelButton.setText("Cancel");
        cancelButton.setType(ButtonType.DEFAULT);
        cancelButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                hide();
            }
        });
        actionButtons.add(cancelButton);

        if(dataProvider!=null)
            saveButton.setText("Save changes");
        else
            saveButton.setText("Save");
        saveButton.setType(ButtonType.SUCCESS);
        saveButton.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {

                if(dataProvider!=null) {
                    if (!namespaceTextBox.getValue().trim().matches("[a-zA-Z0-9]+")) {
                        errorLabel.setText("Namespace should contain only characters and digits");
                        errorLabel.setVisible(true);
                    }
                }

                if (nameTextBox.getValue().trim().isEmpty() || wrapperURLTextBox.getValue().trim().isEmpty()
                        || latitudeTextBox.getValue().trim().isEmpty() || longitudeTextBox.getValue().trim().isEmpty()
                        || namespaceTextBox.getValue().trim().isEmpty() || shortNameTextBox.getValue().trim().isEmpty()) {

                    errorLabel.setText("All asterisk (*) fields are required.");
                    errorLabel.setVisible(true);
                }

                if (!nameTextBox.getValue().trim().isEmpty() && !wrapperURLTextBox.getValue().trim().isEmpty()
                        && !latitudeTextBox.getValue().trim().isEmpty() && !longitudeTextBox.getValue().trim().isEmpty()
                        && !namespaceTextBox.getValue().trim().isEmpty() && !shortNameTextBox.getValue().trim().isEmpty()) {

                    boolean isValidLatitude = true;
                    boolean isValidLongitude = true;

                    if (!latitudeTextBox.getValue().trim().isEmpty()) {
                        if (!isDouble(latitudeTextBox.getValue().trim())
                                || !isValidLatitude(Double.parseDouble(latitudeTextBox.getValue().trim()))) {
                            isValidLatitude = false;
                        }
                    }

                    if (!longitudeTextBox.getValue().trim().isEmpty()) {
                        if (!isDouble(longitudeTextBox.getValue().trim())
                                || !isValidLongitude(Double.parseDouble(longitudeTextBox.getValue().trim()))) {
                            isValidLongitude = false;
                        }
                    }

                    if(!isValidLatitude)
                        errorLabel.setText("Invalid value for latitude");
                    if(!isValidLongitude)
                        errorLabel.setText("Invalid value for longitude");
                    if(!isValidLatitude && !isValidLongitude)
                        errorLabel.setText("Invalid value for latitude and longitude");

                    if(!isValidLatitude || !isValidLongitude) {

                        errorLabel.setVisible(true);

                    } else {

                        final DataProvider newDataProvider = getDataProvider();

                        if(dataProvider!=null) {

                            dataProviderAccessService.updateDataProvider(newDataProvider, new AsyncCallback<Void>() {

                                @Override
                                public void onFailure(Throwable arg0) {
                                    errorLabel.setText("System error updating data provider.");
                                    errorLabel.setVisible(true);
                                }

                                @Override
                                public void onSuccess(Void arg0) {

                                    hide();
                                    if(dataProviderFormListener!=null)
                                        dataProviderFormListener.onSaved();
                                }
                            });

                        } else {

                            dataProviderAccessService.saveDataProvider(newDataProvider, new AsyncCallback<Void>() {

                                @Override
                                public void onFailure(Throwable t) {

                                    if (t instanceof UserAccessException) {
                                        UserAccessException uae = (UserAccessException) t;
                                        if (uae.getErrorCode().equals(UserAccessException.ErrorCode.USER_ALREADY_EXISTS)) {
                                            errorLabel.setText("Namespace already exists.");
                                            errorLabel.setVisible(true);
                                        } else {
                                            errorLabel.setText("System error saving data provider.");
                                            errorLabel.setVisible(true);
                                        }
                                    } else {
                                        errorLabel.setText("System error saving data provider.");
                                        errorLabel.setVisible(true);
                                    }
                                }

                                @Override
                                public void onSuccess(Void arg0) {

                                    hide();
                                    if(dataProviderFormListener!=null)
                                        dataProviderFormListener.onSaved();
                                }
                            });

                        }
                    }
                }
            }
        });
        actionButtons.add(saveButton);

        if(dataProvider!=null)
            loadDataProviderForm(dataProvider);

        dataProviderModal.addStyleName("formModal");
        dataProviderModal.setAnimation(true);
        dataProviderModal.setBackdrop(BackdropType.STATIC);
    }

    public void show() {
        dataProviderModal.show();
    }

    public void hide() {
        dataProviderModal.hide();
        dataProviderModal.removeFromParent();
    }

    public interface DataProviderFormListener {
        void onSaved();
    }

    public void setDataProviderFormListener(DataProviderFormListener dataProviderFormListener) {
        this.dataProviderFormListener = dataProviderFormListener;
    }

    public void loadDataProviderForm(DataProvider dataProvider) {

        nameTextBox.setValue(dataProvider.getName());
        shortNameTextBox.setValue(dataProvider.getShortName());
        namespaceTextBox.setValue(dataProvider.getNamespace());
        namespaceTextBox.setEnabled(false);
        wrapperURLTextBox.setValue(dataProvider.getWrapperURL());
        sosServiceURLTextBox.setValue(dataProvider.getSosServiceURL());
        latitudeTextBox.setValue(dataProvider.getLatitude() + "");
        longitudeTextBox.setValue(dataProvider.getLongitude() + "");
        termsTextArea.setValue(dataProvider.getTerms());

        List<NameValuePair> availableAdministrators = new ArrayList<NameValuePair>();
        List<NameValuePair> selectedAdministrators = new ArrayList<NameValuePair>();

        for (NameValuePair nameValuePair : administratorsList) {
            if (dataProvider.getAdministratorEmails().contains(nameValuePair.value())) {
                selectedAdministrators.add(nameValuePair);
            } else {
                availableAdministrators.add(nameValuePair);
            }
        }

        administrators.clearLeftList();
        administrators.setLeftListElements(availableAdministrators);
        administrators.clearRightList();
        administrators.setRightListElements(selectedAdministrators);
    }

    public DataProvider getDataProvider() {

        DataProvider dataProvider = new DataProvider();

        dataProvider.setName(nameTextBox.getValue().trim());
        dataProvider.setShortName(shortNameTextBox.getValue().trim());
        dataProvider.setNamespace(namespaceTextBox.getValue().trim());
        dataProvider.setWrapperURL(wrapperURLTextBox.getValue().trim());
        dataProvider.setSosServiceURL(sosServiceURLTextBox.getValue().trim());

        dataProvider.setLatitude(Double.parseDouble(latitudeTextBox.getValue().trim()));
        dataProvider.setLongitude(Double.parseDouble(longitudeTextBox.getValue().trim()));

        dataProvider.setTerms(termsTextArea.getValue().trim());

        List<String> administratorEmails = new ArrayList<String>();

        List<NameValuePair> administratorNameValuePairs = administrators.getRightListElements();
        for(NameValuePair administratorNameValuePair : administratorNameValuePairs) {
            administratorEmails.add(administratorNameValuePair.value());
        }

        dataProvider.setAdministratorEmails(administratorEmails);

        return dataProvider;
    }

    private boolean isDouble(String value) {

        try {
            Double.parseDouble(value);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    private boolean isValidLatitude(double latitude) {

        if(latitude >= -90 && latitude <= 90)
            return true;
        return false;
    }

    private boolean isValidLongitude(double longitude) {

        if(longitude >= -180 && longitude <= 360)
            return true;
        return false;
    }
}
