package com.rthoni.intellij.codefromds.ui.dialogs;

import com.intellij.database.model.DasObject;
import com.intellij.database.psi.DbDataSource;
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.ui.ValidationInfo;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.components.JBList;
import com.rthoni.intellij.codefromds.business.DataSourcesBusiness;
import com.rthoni.intellij.codefromds.business.Helper;
import com.rthoni.intellij.codefromds.dbo.options.*;
import com.rthoni.intellij.codefromds.dbo.template.ColumnDataSourceDbo;
import com.rthoni.intellij.codefromds.dbo.template.DataSourceDbo;
import com.rthoni.intellij.codefromds.dbo.template.StoredProcedureDbo;
import com.rthoni.intellij.codefromds.dbo.template.TableDataSourceDbo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionListener;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.List;
import java.util.Vector;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * Created by robin on 11/14/16.
 */
public class GenerateDialog extends DialogWrapper {

    private JPanel _panel;
    private JBList _listDatasources;
    private JBList _listTables;
    private JBList _listColumns;
    private TextFieldWithBrowseButton _textModels;
    private TextFieldWithBrowseButton _textDataSource;
    private TextFieldWithBrowseButton _textDataSourceTemplate;
    private TextFieldWithBrowseButton _textModelsTemplate;
    private TextFieldWithBrowseButton _textConfigPath;
    private JTextField _textFilesExtension;
    private JLabel _lblModelsPath;
    private JLabel _lblDataSourcePath;
    private JLabel _lblDataSourceTemplatePath;
    private JLabel _lblModelsTemplatePath;
    private TextFieldWithBrowseButton _textCastFile;
    private JLabel _lblCastFile;
    private JBList _listStoredProcedure;

    private GenerateOptions _options;

    private Project _project;

    public GenerateDialog(Project project) {
        super(project);
        _project = project;
        setTitle("Code FROM data source");
        setOptions(null);
        init();
    }

    public GenerateOptions getOptions() {
        return _options;
    }

    public void setOptions(GenerateOptions options) {
        _options = options;
        if (_options != null) {
            showSource(_options.getDataSource());
            _textModels.setText(Helper.getAbsolutePath(_project, _options.getModelsRelativePath()));
            _textDataSource.setText(Helper.getAbsolutePath(_project, _options.getDataSourceRelativePath()));
            _textDataSourceTemplate.setText(Helper.getAbsolutePath(_project, _options.getDataSourceTemplateRelativePath()));
            _textModelsTemplate.setText(Helper.getAbsolutePath(_project, _options.getModelsTemplateRelativePath()));
            _textFilesExtension.setText(_options.getFilesExtension());
            _textCastFile.setText(Helper.getAbsolutePath(_project, _options.getCastFileRelativePath()));
            _textConfigPath.setText(_options.getConfigAbsolutePath());
        }
        else {
            showSource(null);
            _textModels.setText("");
            _textDataSource.setText("");
            _textDataSourceTemplate.setText("");
            _textModelsTemplate.setText("");
            _textFilesExtension.setText("");
            _textCastFile.setText("");
            _textConfigPath.setText("");
        }
    }

    @Nullable
    @Override
    protected ValidationInfo doValidate() {
        ValidationInfo info = null;
        File modelDir = _options == null ? null : new File(Helper.getAbsolutePath(_project, _options.getModelsRelativePath()));
        File dataSourcePath = _options == null ? null : new File(Helper.getAbsolutePath(_project, _options.getDataSourceRelativePath()));
        File dataSourceDir = _options == null ? null : new File(dataSourcePath.getParent());
        File dataSourceTemplatePath = _options == null ? null : new File(Helper.getAbsolutePath(_project, _options.getDataSourceTemplateRelativePath()));
        File modelsTemplatePath = _options == null ? null : new File(Helper.getAbsolutePath(_project, _options.getModelsTemplateRelativePath()));
        File configPath = _options == null ? null : new File(_options.getConfigAbsolutePath());
        File configDir = _options == null ? null : new File(configPath.getParent());
        String extension = _options == null ? null : _options.getFilesExtension();
        File castFilePath = _options == null ? null : new File(Helper.getAbsolutePath(_project, _options.getCastFileRelativePath()));
        if (_options == null) {
            info = new ValidationInfo("No Data Source Selected", _listDatasources);
        }
        else if (!modelDir.exists() || !modelDir.isDirectory()) {
            info = new ValidationInfo("Models folder does not exists", _textModels.getTextField());
        }
        else if (!dataSourceDir.exists() || !dataSourceDir.isDirectory()) {
            info = new ValidationInfo("Data source folder does not exists", _textDataSource.getTextField());
        }
        else if (!dataSourceTemplatePath.exists() || !dataSourceTemplatePath.isFile()) {
            info = new ValidationInfo("Data source template file does not exists", _textDataSourceTemplate.getTextField());
        }
        else if (!modelsTemplatePath.exists() || !modelsTemplatePath.isFile()) {
            info = new ValidationInfo("Models template file does not exists", _textModelsTemplate.getTextField());
        }
        else if (!configDir.exists() || !configDir.isDirectory()) {
            info = new ValidationInfo("Configuration file parent folder does not exists", _textConfigPath.getTextField());
        }
        else if (extension == null || extension.isEmpty()) {
            info = new ValidationInfo("Files extension is required", _textFilesExtension);
        }
        else if (extension.startsWith(".")) {
            info = new ValidationInfo("Files extension must not include dot (.)", _textFilesExtension);
        }
        else if (!castFilePath.exists() || !castFilePath.isFile()) {
            info = new ValidationInfo("Cast file does not exists", _textCastFile.getTextField());
        }

        return info;
    }

    @Nullable
    @Override
    protected JComponent createCenterPanel() {

        List<DbDataSource> dataSources = Helper.getDataSources();

        _listDatasources.setListData(dataSources.stream().map(DasObject::getName).toArray(String[]::new));
        _listDatasources.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) {
                int index = _listDatasources.getSelectedIndex();
                if (index == -1) {
                    setOptions(null);
                }
                else {
                    setOptions(new GenerateOptions(DataSourcesBusiness.getDataSourcesDbo().get(index),
                            "code-from-ds" + File.separator + "code-from-ds.json"));
                }
            }
        });
        _listTables.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) {
                int index = _listTables.getSelectedIndex();
                if (index == -1) {
                    showTable(null);
                }
                else {
                    showTable(_options.getDataSource().getTables().get(index));
                }
            }
        });

        setupTextField(_textModels, null, true, "Models");
        setupTextField(_textDataSource, null, false, "Data Source");
        setupTextField(_textDataSourceTemplate, null, false, "Data Source Template");
        setupTextField(_textModelsTemplate, null, false, "Models Template");
        setupTextField(_textConfigPath, null, false, "Configuration");
        setupTextField(_textCastFile, null, false, "Types Cast");

        setupTextFieldListener(_textModels.getTextField(), s -> {
            _options.setModelsRelativePath(Helper.getRelativePath(_project, s));
            _lblModelsPath.setText("$ProjectRoot/" + _options.getModelsRelativePath());
        });

        setupTextFieldListener(_textDataSource.getTextField(), s -> {
            _options.setDataSourceRelativePath(Helper.getRelativePath(_project, s));
            _lblDataSourcePath.setText("$ProjectRoot/" + _options.getDataSourceRelativePath());
        });
        setupTextFieldListener(_textDataSourceTemplate.getTextField(), s -> {
            _options.setDataSourceTemplateRelativePath(Helper.getRelativePath(_project, s));
            _lblDataSourceTemplatePath.setText("$ProjectRoot/" + _options.getDataSourceTemplateRelativePath());
        });
        setupTextFieldListener(_textModelsTemplate.getTextField(), s -> {
            _options.setModelsTemplateRelativePath(Helper.getRelativePath(_project, s));
            _lblModelsTemplatePath.setText("$ProjectRoot/" + _options.getModelsTemplateRelativePath());
        });
        setupTextFieldListener(_textFilesExtension, s -> _options.setFilesExtension(s));
        setupTextFieldListener(_textConfigPath.getTextField(), s -> _options.setConfigAbsolutePath(s));
        setupTextFieldListener(_textCastFile.getTextField(), s -> {
            _options.setCastFileRelativePath(Helper.getRelativePath(_project, s));
            _lblCastFile.setText("$ProjectRoot/" + _options.getCastFileRelativePath());
        });

        _listTables.setCellRenderer(new ColoredListCellRenderer() {
            @Override
            protected void customizeCellRenderer(@NotNull JList jList, Object o, int i, boolean b, boolean b1) {
                TableDataSourceDbo tableSelection = _options.getDataSource().getTables().get(i);
                if (tableSelection.hasAll()) {
                    setBackground(JBColor.GREEN);
                }
                else if (!tableSelection.hasAny()) {
                    setBackground(JBColor.RED);
                }
                else {
                    setBackground(JBColor.ORANGE);
                }
                append(tableSelection.getName());
            }
        });

//        _listStoredProcedure.setCellRenderer(new ColoredListCellRenderer() {
//            @Override
//            protected void customizeCellRenderer(@NotNull JList jList, Object o, int i, boolean b, boolean b1) {
//                StoredProcedureSelection spSelection = _options.getDataSource().getStoredProcedures().get(i);
//                if (spSelection.isSelected()) {
//                    setBackground(JBColor.GREEN);
//                }
//                else {
//                    setBackground(JBColor.RED);
//                }
//                append(spSelection.getStoredProcedure().getText());
//            }
//        });

        if (dataSources.size() > 0) {
            _listDatasources.setSelectedIndices(new int[]{0});
        }

        return _panel;
    }

    private void setupTextFieldListener(JTextField field, Consumer<String> consumer)
    {
        field.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                changed();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                changed();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                changed();
            }

            public void changed() {
                consumer.accept(field.getText());
            }
        });
    }

    private void setupTextField(TextFieldWithBrowseButton field, Project project, boolean dirsOnly, String title)
    {
        for (ActionListener l : field.getButton().getActionListeners()) {
            field.getButton().removeActionListener(l);
        }
        field.addBrowseFolderListener("Choose " + title + " " + (dirsOnly ? "Folder" : "File"),
                "Choose " + (dirsOnly ? "Folder" : "File"), project,
                dirsOnly ? FileChooserDescriptorFactory.createSingleFolderDescriptor() : FileChooserDescriptorFactory.createSingleFileDescriptor());
    }

    private void showSource(DataSourceDbo source)
    {
        for (ListSelectionListener e : _listStoredProcedure.getListSelectionListeners()) {
            _listStoredProcedure.removeListSelectionListener(e);
        }
        if (source != null) {

            List<String> tables = source.getTables().stream().map(t -> t.getName()).collect(Collectors.toList());
            _listTables.setListData(tables.toArray(new String[tables.size()]));
            if (tables.size() > 0) {
                showTable(source.getTables().get(0));
            }
            else {
                showTable(null);
            }
            List<String> sps = source.getStoredProcedures().stream().map(t -> t.getFullName()).collect(Collectors.toList());
            _listStoredProcedure.setListData(sps.toArray(new String[sps.size()]));
            int[] indices = getSpSelectedIndices(source);
            _listStoredProcedure.setSelectedIndices(indices);


            _listStoredProcedure.addListSelectionListener(e -> {
                if (!e.getValueIsAdjusting()) {
                    _listStoredProcedure.updateUI();
                    updateSpSelection();
                }
            });
        }
        else {
            _listTables.setListData(new String[]{});
            _listStoredProcedure.setListData(new String[]{});
            showTable(null);
        }
    }

    private int[] getSpSelectedIndices(final DataSourceDbo dataSource)
    {
        List<StoredProcedureDbo> columns = dataSource.getStoredProcedures();
        List<Integer> indices = new Vector<>();
        for (int i = 0; i < columns.size(); ++i) {
            if (columns.get(i).isSelected()) {
                indices.add(i);
            }
        }
        return indices.stream().mapToInt(Integer::intValue).toArray();
    }

    private int[] getColumnsSelectedIndices(final TableDataSourceDbo table)
    {
        List<ColumnDataSourceDbo> columns = table.getColumns();
        List<Integer> indices = new Vector<>();
        for (int i = 0; i < columns.size(); ++i) {
            if (columns.get(i).isSelected()) {
                indices.add(i);
            }
        }
        return indices.stream().mapToInt(Integer::intValue).toArray();
    }

    private void updateColumnSelection(final TableDataSourceDbo table)
    {
        List<ColumnDataSourceDbo> columns = table.getColumns();
        for (int i = 0; i < columns.size(); ++i) {
            columns.get(i).setSelected(_listColumns.isSelectedIndex(i));
        }
    }

    private void updateSpSelection()
    {
        List<StoredProcedureDbo> sps = _options.getDataSource().getStoredProcedures();
        for (int i = 0; i < sps.size(); ++i) {
            sps.get(i).setSelected(_listStoredProcedure.isSelectedIndex(i));
        }
    }

    private void showTable(TableDataSourceDbo table)
    {
        for (ListSelectionListener e : _listColumns.getListSelectionListeners()) {
            _listColumns.removeListSelectionListener(e);
        }
        if (table != null) {
            _listColumns.setListData(table.getColumns().stream().map(c -> c.getName()).toArray(String[]::new));
            int[] indices = getColumnsSelectedIndices(table);
            _listColumns.setSelectedIndices(indices);
            _listColumns.addListSelectionListener(e -> {
                if (!e.getValueIsAdjusting()) {
                    _listTables.updateUI();
                    updateColumnSelection(table);
                }
            });
        }
        else {
            _listColumns.setListData(new String[]{});
        }
    }
}
