/*****************************************************************
 *   Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    https://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 ****************************************************************/
package org.apache.cayenne.modeler.editor.dbimport;

import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.border.EmptyBorder;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;
import org.apache.cayenne.dbsync.reverse.dbimport.ReverseEngineering;
import org.apache.cayenne.map.DataMap;
import org.apache.cayenne.modeler.ProjectController;
import org.apache.cayenne.modeler.action.LoadDbSchemaAction;
import org.apache.cayenne.modeler.action.ReverseEngineeringAction;
import org.apache.cayenne.modeler.dialog.db.load.DbImportTreeNode;
import org.apache.cayenne.modeler.dialog.db.load.TransferableNode;
import org.apache.cayenne.modeler.editor.dbimport.tree.ColorTreeRenderer;
import org.apache.cayenne.modeler.util.CayenneAction;
import org.apache.cayenne.modeler.util.ModelerUtil;

/**
 * @since 4.1
 */
public class DbImportView extends JPanel {

    private static final String MAIN_LAYOUT = "fill:160dlu:grow, 5dlu, fill:50dlu, 5dlu, fill:160dlu:grow";
    private static final String HEADER_LAYOUT = "fill:80dlu:grow";
    private static final String BUTTON_PANEL_LAYOUT = "fill:50dlu";
    private static final String PROGRESS_PANEL_LAYOUT = "fill:160dlu:grow, 60dlu, fill:160dlu:grow";
    private static final int ALL_LINE_SPAN = 5;
    private static final ImageIcon rightArrow = ModelerUtil.buildIcon("icon-arrow-closed.png");
    private static final ImageIcon downArrow = ModelerUtil.buildIcon("icon-arrow-open.png");

    private TreeToolbarPanel treeToolbar;
    private ReverseEngineeringTreePanel treePanel;
    private ReverseEngineeringConfigPanel configPanel;
    private DraggableTreePanel draggableTreePanel;
    private JProgressBar loadDbSchemaProgress;
    private JProgressBar reverseEngineeringProgress;
    private CayenneAction.CayenneToolbarButton loadDbSchemaButton;

    private ProjectController projectController;

    private boolean initFromModel;

    public DbImportView(ProjectController projectController) {
        this.projectController = projectController;
        this.initFromModel = false;
        initFormElements();
        buildForm();
        draggableTreePanel.getSourceTree().repaint();
    }

    private void buildForm() {
        FormLayout buttonPanelLayout = new FormLayout(BUTTON_PANEL_LAYOUT);
        DefaultFormBuilder buttonBuilder = new DefaultFormBuilder(buttonPanelLayout);
        buttonBuilder.append(draggableTreePanel.getMoveButton());
        buttonBuilder.append(draggableTreePanel.getMoveInvertButton());

        FormLayout layout = new FormLayout(MAIN_LAYOUT);
        DefaultFormBuilder builder = new DefaultFormBuilder(layout);
        builder.append(treeToolbar, ALL_LINE_SPAN);
        FormLayout headerLayout = new FormLayout(HEADER_LAYOUT);

        DefaultFormBuilder reverseEngineeringHeaderBuilder = new DefaultFormBuilder(headerLayout);
        JLabel importLabel = new JLabel("Database Import Configuration");
        importLabel.setBorder(new EmptyBorder(0, 5, 0,0));
        reverseEngineeringHeaderBuilder.append(importLabel);
        builder.append(reverseEngineeringHeaderBuilder.getPanel());

        DefaultFormBuilder databaseHeaderBuilder = new DefaultFormBuilder(headerLayout);
        JLabel schemaLabel = new JLabel("Database Schema");
        schemaLabel.setBorder(new EmptyBorder(0, 5, 0,0));
        databaseHeaderBuilder.append(schemaLabel);
        LoadDbSchemaAction loadDbSchemaAction = projectController.getApplication().getActionManager().
                getAction(LoadDbSchemaAction.class);
        loadDbSchemaAction.setDraggableTreePanel(draggableTreePanel);
        loadDbSchemaButton = (CayenneAction.CayenneToolbarButton) loadDbSchemaAction.buildButton(0);
        loadDbSchemaButton.setShowingText(false);
        loadDbSchemaButton.setText("Refresh DB Schema");
        treeToolbar.add(loadDbSchemaButton);

        ReverseEngineeringAction reverseEngineeringAction = projectController.getApplication().getActionManager().
                getAction(ReverseEngineeringAction.class);
        reverseEngineeringAction.setView(this);
        CayenneAction.CayenneToolbarButton reverseEngineeringButton = (CayenneAction.CayenneToolbarButton)
                reverseEngineeringAction.buildButton(0);
        reverseEngineeringButton.setShowingText(true);
        reverseEngineeringButton.setText("Run Import");
        JPanel reverseEngineeringButtonPanel = new JPanel();
        reverseEngineeringButtonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        reverseEngineeringButtonPanel.add(reverseEngineeringButton);
        treeToolbar.addSeparator();
        treeToolbar.add(reverseEngineeringButtonPanel);

        builder.append("");
        builder.append(databaseHeaderBuilder.getPanel());

        builder.append(treePanel);
        builder.append(buttonBuilder.getPanel());
        builder.append(draggableTreePanel);

        loadDbSchemaProgress = new JProgressBar();
        reverseEngineeringProgress = new JProgressBar();
        loadDbSchemaProgress.setIndeterminate(true);
        loadDbSchemaProgress.setVisible(false);
        reverseEngineeringProgress.setIndeterminate(true);
        reverseEngineeringProgress.setVisible(false);
        FormLayout progressLayout = new FormLayout(PROGRESS_PANEL_LAYOUT, "fill:10dlu");
        DefaultFormBuilder progressBarBuilder = new DefaultFormBuilder(progressLayout);
        progressBarBuilder.append(reverseEngineeringProgress);
        progressBarBuilder.append(loadDbSchemaProgress);
        builder.append(progressBarBuilder.getPanel(), ALL_LINE_SPAN);

        createAdvancedOptionsHiderPanel(builder);

        builder.append(configPanel, ALL_LINE_SPAN);
        this.setLayout(new BorderLayout());
        add(builder.getPanel(), BorderLayout.CENTER);
    }

    private void createAdvancedOptionsHiderPanel(DefaultFormBuilder builder) {
        JPanel advancedOptionsPanel = new JPanel();
        advancedOptionsPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        JButton hideButton = new JButton("Advanced Options");
        hideButton.setIcon(configPanel.isVisible() ? downArrow : rightArrow);
        hideButton.setBorderPainted(false);
        hideButton.addActionListener(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                configPanel.setVisible(!configPanel.isVisible());
                hideButton.setIcon(configPanel.isVisible() ? downArrow : rightArrow);
            }
        });
        advancedOptionsPanel.add(hideButton);

        builder.append(advancedOptionsPanel, ALL_LINE_SPAN);
    }

    private void initFormElements() {
        DbImportTreeNode root = new DbImportTreeNode(new ReverseEngineering());
        DbImportTreeNode draggableTreeRoot = new DbImportTreeNode(new ReverseEngineering());
        DbImportTree reverseEngineeringTree = new DbImportTree(root);
        DbImportTree draggableTree = new DbImportTree(new TransferableNode(draggableTreeRoot));
        DbImportModel model = new DbImportModel(root);
        model.setCanBeCleaned(true);
        DbImportModel draggableTreeModel = new DbImportModel(draggableTreeRoot);
        draggableTreeModel.setCanBeCleaned(false);

        draggableTree.setRootVisible(false);
        draggableTree.setShowsRootHandles(true);
        draggableTree.setModel(draggableTreeModel);
        reverseEngineeringTree.setRootVisible(false);
        reverseEngineeringTree.setModel(model);
        reverseEngineeringTree.setShowsRootHandles(true);

        draggableTreePanel = new DraggableTreePanel(projectController, draggableTree, reverseEngineeringTree);
        treeToolbar = new TreeToolbarPanel(projectController, reverseEngineeringTree, draggableTreePanel);
        treePanel = new ReverseEngineeringTreePanel(projectController, reverseEngineeringTree, draggableTree);
        treePanel.setTreeToolbar(treeToolbar);
        model.setDbSchemaTree(draggableTree);
        draggableTreeModel.setDbSchemaTree(draggableTree);
        ((ColorTreeRenderer) draggableTreePanel.getSourceTree().getCellRenderer()).
                setReverseEngineeringTree(reverseEngineeringTree);

        configPanel = new ReverseEngineeringConfigPanel(projectController, this);
        configPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        configPanel.setVisible(false);
    }

    public void initFromModel() {
        DataMap map = projectController.getCurrentDataMap();
        treePanel.getReverseEngineeringTree().stopEditing();
        if (map != null) {
            initFromModel = true;
            treeToolbar.unlockButtons();
            ReverseEngineering reverseEngineering = DbImportView.this.projectController.getApplication()
                    .getMetaData().get(map, ReverseEngineering.class);
            if(reverseEngineering == null) {
                // create config with default values
                reverseEngineering = new ReverseEngineering();
                DbImportView.this.projectController.getApplication().getMetaData().add(map, reverseEngineering);
            }
            configPanel.fillCheckboxes(reverseEngineering);
            configPanel.initializeTextFields(reverseEngineering);
            configPanel.initStrategy(reverseEngineering);
            String[] tableTypes = reverseEngineering.getTableTypes();
            if(tableTypes.length != 0) {
                configPanel.getTableTypes().setText(String.join(",", tableTypes));
            } else {
                configPanel.getTableTypes().setText("TABLE, VIEW");
                configPanel.getTableTypes().updateModel();
            }
            treePanel.updateTree();
            DbImportTreeNode root = draggableTreePanel.getSourceTree().getRootNode();
            root.removeAllChildren();
            draggableTreePanel.updateTree(projectController.getCurrentDataMap());
            draggableTreePanel.getMoveButton().setEnabled(false);
            draggableTreePanel.getMoveInvertButton().setEnabled(false);
        }
        initFromModel = false;
    }

    public boolean isSkipRelationshipsLoading() {
        return configPanel.getSkipRelationshipsLoading().isSelected();
    }

    public boolean isSkipPrimaryKeyLoading() {
        return configPanel.getSkipPrimaryKeyLoading().isSelected();
    }

    public boolean isForceDataMapCatalog() {
        return configPanel.getForceDataMapCatalog().isSelected();
    }

    public boolean isForceDataMapSchema() {
        return configPanel.getForceDataMapSchema().isSelected();
    }

    public boolean isUsePrimitives() {
        return configPanel.getUsePrimitives().isSelected();
    }

    public boolean isUseJava7Typed() {
        return configPanel.getUseJava7Types().isSelected();
    }

    public String getMeaningfulPk() {
        return "".equals(configPanel.getMeaningfulPk().getComponent().getText())
                ? null : configPanel.getMeaningfulPk().getComponent().getText();
    }

    public String getNamingStrategy() {
        return (String) configPanel.getStrategyCombo().getSelectedItem();
    }

    public String getStripFromTableNames() {
        return configPanel.getStripFromTableNames().getComponent().getText();
    }

    public JProgressBar getLoadDbSchemaProgress() {
        return loadDbSchemaProgress;
    }

    public void lockToolbarButtons() {
        treeToolbar.changeToolbarButtonsState(false);
    }

    public void unlockToolbarButtons() {
        treeToolbar.unlockButtons();
    }

    public JProgressBar getReverseEngineeringProgress() {
        return reverseEngineeringProgress;
    }

    public JButton getLoadDbSchemaButton() {
        return loadDbSchemaButton;
    }

    public boolean isInitFromModel() {
        return initFromModel;
    }

    public String[] getTableTypes() {
        return configPanel
                .getReverseEngineeringBySelectedMap()
                .getTableTypes();
    }
}
