/*
 * Copyright (C) 2002-2003 Stefan Stiller
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package com.kiwisoft.idea;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import com.intellij.openapi.project.Project;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowAnchor;
import com.kiwisoft.db.Database;
import com.kiwisoft.db.DatabaseManager;
import com.kiwisoft.db.Query;
import com.kiwisoft.db.QueryManager;
import com.kiwisoft.db.driver.MySQLDriver;
import com.kiwisoft.utils.NotifyObject;
import com.kiwisoft.utils.StringUtils;
import com.kiwisoft.utils.gui.ExtentionFileFilter;
import com.kiwisoft.utils.gui.IconManager;
import com.kiwisoft.utils.gui.MessageDialog;

/**
 * @author Stefan Stiller
 * @version $Revision: 1.11 $, $Date: 2003/05/21 13:24:24 $
 */
public class MainPanel extends JPanel {
    private JTabbedPane tabs;
    private JComboBox cbxDatabases;
    private Project project;
    private CopyQueryAction copyQueryAction;
    private BrowserAction browserAction;
    private SaveAction saveAction;
    private OpenAction openAction;

    public MainPanel(Project project) {
        // Copies the input map from TextArea to get the same action keys as in the editor window
        UIManager.put("EditorPane.focusInputMap", UIManager.get("TextArea.focusInputMap"));
        UIManager.put("EditorPane.font", UIManager.get("TextArea.font"));

        this.project = project;
        setLayout(new BorderLayout());

        DatabaseManager databaseManager = DatabaseManager.getInstance(project);

        cbxDatabases = new JComboBox(databaseManager.getDatabases().toArray());
        cbxDatabases.addActionListener(new CurrentDatabaseListener());
        Database currentDatabase = databaseManager.getCurrentDatabase();
        if (currentDatabase != null) cbxDatabases.setSelectedItem(currentDatabase);

        JToolBar toolbar = new JToolBar();
        toolbar.setFloatable(false);
        openAction = new OpenAction();
        toolbar.add(Utils.createButton(openAction));
        saveAction = new SaveAction();
        toolbar.add(Utils.createButton(saveAction));
        toolbar.addSeparator();
        toolbar.add(Utils.createButton(new CreateQueryAction()));
        copyQueryAction = new CopyQueryAction();
        toolbar.add(Utils.createButton(copyQueryAction));
        toolbar.addSeparator();
        toolbar.add(cbxDatabases);
        toolbar.add(Utils.createButton(new PropertiesAction()));
        browserAction = new BrowserAction();
        toolbar.add(Utils.createButton(browserAction));
        toolbar.add(Utils.createButton(new DisconnectAction()));
        toolbar.add(Utils.createButton(new CommitAction()));
        toolbar.add(Utils.createButton(new RollbackAction()));
        toolbar.addSeparator();
        toolbar.add(Utils.createButton(new AboutAction()));

        tabs = new JTabbedPane();
        tabs.setTabPlacement(JTabbedPane.BOTTOM);
        tabs.addChangeListener(new QuerySelectionListener());

        add(toolbar, BorderLayout.NORTH);
        add(tabs, BorderLayout.CENTER);

        QueryManager queryList = QueryManager.getInstance(project);
        queryList.addObserver(new QueryListObserver());
        databaseManager.addObserver(new DatabaseManagerObserver());

        queryList.createQuery();
        updateActions();

        tabs.addMouseListener(new TabsMouseListener());
    }

    private Query getSelectedQuery() {
        if (tabs != null) {
            QueryPanel queryPanel = (QueryPanel)tabs.getSelectedComponent();
            if (queryPanel != null) return queryPanel.getQuery();
        }
        return null;
    }

    private QueryPanel getQueryPanel(Query query) {
        Component[] panels = tabs.getComponents();
        for (int i = 0; i < panels.length; i++) {
            if (panels[i] instanceof QueryPanel) {
                QueryPanel queryPanel = (QueryPanel)panels[i];
                if (queryPanel.getQuery() == query) return queryPanel;
            }
        }
        return null;
    }

    private class QuerySelectionListener implements ChangeListener {
        public void stateChanged(ChangeEvent e) {
            updateActions();
        }
    }

    private String getTabName(Query query) {
        return query.getName() + (query.isSaveable() ? "*" : "");
    }

    private class QueryListObserver implements Observer {
        public void update(Observable o, Object arg) {
            NotifyObject note = (NotifyObject)arg;
            Object type = note.getArgument(0);
            if (type.equals("query added")) {
                Query newQuery = (Query)note.getArgument(1);
                QueryPanel queryPanel = new QueryPanel(project, newQuery);
                tabs.add(getTabName(newQuery), queryPanel);
                tabs.setSelectedComponent(queryPanel);
                updateActions();
            } else if (type.equals("query removed")) {
                Query query = (Query)note.getArgument(1);
                for (int i = 0; i < tabs.getTabCount(); i++) {
                    QueryPanel queryPanel = (QueryPanel)tabs.getComponentAt(i);
                    if (queryPanel.getQuery() == query) {
                        tabs.remove(queryPanel);
                    }
                }
                updateActions();
            } else if (type.equals("query changed") || type.equals("query saved")
                    || type.equals("query name changed")) {
                Query query = (Query)note.getArgument(1);
                QueryPanel queryPanel = getQueryPanel(query);
                if (queryPanel != null) {
                    int index = tabs.indexOfComponent(queryPanel);
                    tabs.setTitleAt(index, getTabName(query));
                }
                updateActions();
            }
        }
    }

    private void updateActions() {
        Query selectedQuery = getSelectedQuery();
        if (copyQueryAction != null) {
            copyQueryAction.setEnabled(selectedQuery != null);
        }
        if (browserAction != null) {
            browserAction.setEnabled(cbxDatabases.getSelectedItem() != null);
        }
        if (saveAction != null) {
            saveAction.setEnabled(selectedQuery != null && selectedQuery.isSaveable());
        }
    }

    private class CurrentDatabaseListener implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            Database current = (Database)cbxDatabases.getSelectedItem();
            DatabaseManager.getInstance(project).setCurrentDatabase(current);
            updateActions();
        }
    }

    private class DatabaseManagerObserver implements Observer {
        public void update(Observable o, Object arg) {
            NotifyObject note = (NotifyObject)arg;
            if (note.getArgument(0).equals("databases changed")) {
                Object selected = cbxDatabases.getSelectedItem();
                cbxDatabases.removeAllItems();
                Iterator it = DatabaseManager.getInstance(project).getDatabases().iterator();
                while (it.hasNext()) cbxDatabases.addItem(it.next());
                if (selected != null) cbxDatabases.setSelectedItem(selected);
            }
            updateActions();
        }
    }

    public static void main(String[] args) {
        Locale.setDefault(Locale.ENGLISH);
        Utils.setDialogWrapper(false);

        DatabaseManager databaseManager = DatabaseManager.getInstance(null);

        Database database = new Database("test");
        database.setDriver("jdbc:mysql");
        database.setProperty(MySQLDriver.HOST, "localhost");
        database.setProperty(MySQLDriver.PORT, new Integer(3306));
        database.setProperty(MySQLDriver.DATABASE, "tv");
        database.setProperty(MySQLDriver.USER, "mysql");
        databaseManager.addDatabase(database);

        databaseManager.setCurrentDatabase(database);

        JFrame frame = new JFrame("SQL");
        frame.getContentPane().add(new MainPanel(null));
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.setIconImage(
                IconManager.getIcon(MainPanel.class.getClassLoader(), "icons/database.gif")
                .getImage());

        frame.pack();
        frame.setVisible(true);
    }

    public void removeNotify() {
        DatabaseManager.getInstance(project).closeAllConnections();
        super.removeNotify();
    }

    private class CreateQueryAction extends AbstractAction {
        public CreateQueryAction() {
            super(null, IconManager.getIcon(MainPanel.class.getClassLoader(), "icons/add.png"));
            putValue(Action.SHORT_DESCRIPTION, "Open a new empty query.");
        }

        public void actionPerformed(ActionEvent e) {
            QueryManager.getInstance(project).createQuery();
        }
    }

    private class CopyQueryAction extends AbstractAction {
        public CopyQueryAction() {
            super(null, IconManager.getIcon(MainPanel.class.getClassLoader(), "icons/copy.png"));
            putValue(Action.SHORT_DESCRIPTION,
                    "Create a new query and copy the statement from the selected.");
        }

        public void actionPerformed(ActionEvent e) {
            Query query = getSelectedQuery();
            if (query != null) QueryManager.getInstance(project).createQuery(query);
        }
    }

    private class PropertiesAction extends AbstractAction {
        public PropertiesAction() {
            super(null,
                    IconManager.getIcon(MainPanel.class.getClassLoader(), "icons/properties.png"));
            putValue(Action.SHORT_DESCRIPTION, "Edit database connections.");
        }

        public void actionPerformed(ActionEvent e) {
            Utils.wrapDialog(project, new PropertiesDialogStub(project));
        }
    }

    private class OpenAction extends AbstractAction {
        public OpenAction() {
            super("Open", IconManager.getIcon(QueryPanel.class.getClassLoader(), "icons/open.png"));
        }

        public void actionPerformed(ActionEvent e) {
            JFileChooser chooser = new JFileChooser();
            chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
            chooser.setDialogType(JFileChooser.SAVE_DIALOG);
            chooser.setDialogTitle("Load Query");
            chooser.setMultiSelectionEnabled(false);
            chooser.setCurrentDirectory(
                    Utils.getExistingPath(CommonConfiguration.getInstance(project).getQueryPath()));

            ExtentionFileFilter sqlFileFilter = new ExtentionFileFilter("SQL Sources",
                    new String[]{"sql", "ddl"});
            ExtentionFileFilter txtFileFilter = new ExtentionFileFilter("Text Files", "txt");
            chooser.addChoosableFileFilter(sqlFileFilter);
            chooser.addChoosableFileFilter(txtFileFilter);
            chooser.setFileFilter(sqlFileFilter);

            int res = chooser.showDialog(MainPanel.this, "Load");
            if (res == JFileChooser.APPROVE_OPTION) {
                File file = chooser.getSelectedFile();
                CommonConfiguration.getInstance(project).setQueryPath(file.getParent());
                if (file.canRead()) {
                    FileInputStream fis = null;
                    try {
                        fis = new FileInputStream(file);
                        byte[] buf = new byte[fis.available()];
                        fis.read(buf);
                        QueryManager.getInstance(project).createQuery(file, new String(buf));
                    } catch (IOException e1) {
                        JOptionPane.showMessageDialog(MainPanel.this, e1.getMessage(), "Error",
                                JOptionPane.ERROR_MESSAGE);
                    } finally {
                        if (fis != null) {
                            try {
                                fis.close();
                            } catch (IOException e1) {
                                JOptionPane.showMessageDialog(MainPanel.this, e1.getMessage(),
                                        "Error", JOptionPane.ERROR_MESSAGE);
                            }
                        }
                    }
                } else {
                    JOptionPane.showMessageDialog(MainPanel.this, "Can't read file '" + file + "'",
                            "Error", JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    private class SaveAction extends AbstractAction {
        public SaveAction() {
            super("Save", IconManager.getIcon(QueryPanel.class.getClassLoader(), "icons/save.png"));
            setEnabled(false);
        }

        public void actionPerformed(ActionEvent e) {
            Query selectedQuery = getSelectedQuery();
            if (selectedQuery != null) {
                JFileChooser chooser = new JFileChooser();
                chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
                chooser.setDialogType(JFileChooser.SAVE_DIALOG);
                chooser.setDialogTitle("Save Query as ...");
                chooser.setMultiSelectionEnabled(false);
                if (selectedQuery.getFile() != null)
                    chooser.setSelectedFile(selectedQuery.getFile());
                else
                    chooser.setCurrentDirectory(
                            Utils.getExistingPath(
                                    CommonConfiguration.getInstance(project).getQueryPath()));

                ExtentionFileFilter sqlFileFilter = new ExtentionFileFilter("SQL Sources",
                        new String[]{"sql", "ddl"});
                ExtentionFileFilter txtFileFilter = new ExtentionFileFilter("Text Files", "txt");

                sqlFileFilter.setShowReadOnly(false);
                txtFileFilter.setShowReadOnly(false);

                chooser.addChoosableFileFilter(sqlFileFilter);
                chooser.addChoosableFileFilter(txtFileFilter);
                chooser.setFileFilter(sqlFileFilter);

                int res = chooser.showDialog(MainPanel.this, "Save As");
                if (res == JFileChooser.APPROVE_OPTION) {
                    File file = chooser.getSelectedFile();
                    CommonConfiguration.getInstance(project).setQueryPath(file.getParent());
                    try {
                        if (file.exists()) {
                            String message = "File '" + file.getName()
                                    + "' exists. Do you want override the file ?";
                            MessageDialog dlg = new MessageDialog(project, true, message, false);
                            dlg.setTitle("Warning");
                            dlg.show();
                            if (dlg.getExitCode() == MessageDialog.OK_EXIT_CODE) {
                                save(file, selectedQuery);
                            }
                        } else {
                            if (file.createNewFile()) {
                                save(file, selectedQuery);
                            }
                        }
                    } catch (Exception e1) {
                        JOptionPane.showMessageDialog(MainPanel.this, e1.getMessage(), "Error",
                                JOptionPane.ERROR_MESSAGE);
                    }
                }
            }
        }

        private void save(File file, Query query) throws Exception {
            FileWriter fw = null;
            try {
                fw = new FileWriter(file);
                fw.write(query.getStatement());
                query.setFile(file);
                query.setChanged(false);
            } finally {
                if (fw != null) {
                    fw.close();
                }
            }
        }
    }

    private class BrowserAction extends AbstractAction {
        private BrowserDialogStub stub;

        public BrowserAction() {
            super("Browser",
                    IconManager.getIcon(QueryPanel.class.getClassLoader(), "icons/browser.png"));
            putValue(Action.SHORT_DESCRIPTION, "Show/hide database struture.");
        }

        private Map consoles = new HashMap();
        private List managers = new ArrayList();

        private final Icon ICON = IconManager.getIcon(QueryPanel.class.getClassLoader(),
                "icons/database2.gif");

        public void actionPerformed(ActionEvent e) {
            if (project != null) {
                final ToolWindowManager manager = ToolWindowManager.getInstance(project);
                ToolWindow console = (ToolWindow)consoles.get(project);
                if (console == null) {
                    Database currentDatabase = DatabaseManager.getInstance(project)
                            .getCurrentDatabase();
                    stub = BrowserDialogStub.create(project, currentDatabase);
                    JComponent content = stub.getContentPanel();
                    // Show window
                    manager.registerToolWindow("Schema", content, ToolWindowAnchor.RIGHT);
                    console = manager.getToolWindow("Schema");
                    if (console != null) {
                        // Add manager to array to do unregister on finalize
                        managers.add(manager);
                        consoles.put(project, console);
                        console.setIcon(ICON);
                        console.show(null);
                        console.activate(null);
                    }
                } else {
                    console.hide(null);
                    manager.unregisterToolWindow("Schema");
                    managers.remove(manager);
                    consoles.remove(project);
                }
            }
        }
    }

    private class DisconnectAction extends AbstractAction {
        public DisconnectAction() {
            super("Disconnect",
                    IconManager.getIcon(QueryPanel.class.getClassLoader(), "icons/sync.png"));
            putValue(Action.SHORT_DESCRIPTION, "Refresh all connections.");
        }

        public void actionPerformed(ActionEvent e) {
            DatabaseManager.getInstance(project).closeAllConnections();
        }
    }

    private class CommitAction extends AbstractAction {
        public CommitAction() {
            super("Commit",
                    IconManager.getIcon(QueryPanel.class.getClassLoader(), "icons/commit.gif"));
            putValue(Action.SHORT_DESCRIPTION, "Commit all changes.");
        }

        public void actionPerformed(ActionEvent e) {
            Database database = DatabaseManager.getInstance(project).getCurrentDatabase();
            if (database != null) {
                Connection connection = database.getConnection();
                try {
                    if (connection != null) {
                        connection.commit();
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                    JOptionPane.showMessageDialog(MainPanel.this, e1.getMessage(), "Error",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    private class RollbackAction extends AbstractAction {
        public RollbackAction() {
            super("Rollback",
                    IconManager.getIcon(QueryPanel.class.getClassLoader(),
                            "icons/rollback.gif"));
            putValue(Action.SHORT_DESCRIPTION, "Rollback all changes.");
        }

        public void actionPerformed(ActionEvent e) {
            Database database = DatabaseManager.getInstance(project).getCurrentDatabase();
            if (database != null) {
                Connection connection = database.getConnection();
                try {
                    if (connection != null) {
                        connection.rollback();
                    }
                } catch (SQLException e1) {
                    e1.printStackTrace();
                    JOptionPane.showMessageDialog(MainPanel.this, e1.getMessage(), "Error",
                            JOptionPane.ERROR_MESSAGE);
                }
            }
        }
    }

    private class AboutAction extends AbstractAction {
        public AboutAction() {
            super("About",
                    IconManager.getIcon(QueryPanel.class.getClassLoader(),
                            "icons/information.png"));
            putValue(Action.SHORT_DESCRIPTION, "Information about this plugin.");
        }

        public void actionPerformed(ActionEvent e) {
            new AboutWindow((Window)getTopLevelAncestor()).show();
        }
    }

    private class TabsMouseListener extends MouseAdapter {
        public void mousePressed(MouseEvent e) {
            if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
                int index = tabs.getUI().tabForCoordinate(tabs, e.getX(), e.getY());
                if (index >= 0) {
                    Component component = tabs.getComponentAt(index);
                    if (component instanceof QueryPanel) {
                        QueryPanel panel = (QueryPanel)component;
                        JPopupMenu popup = new JPopupMenu();
                        popup.add(new JMenuItem(new RenamedQueryAction(panel.getQuery())));
                        popup.show(tabs, e.getX(), e.getY() - 20);
                    }
                }
            }
        }
    }

    private class RenamedQueryAction extends AbstractAction {
        private Query query;

        public RenamedQueryAction(Query query) {
            super("Rename query");
            this.query = query;
            setEnabled(query != null);
        }

        public void actionPerformed(ActionEvent e) {
            RenameDialogStub dialogStub = new RenameDialogStub("Rename Query", query.getName());
            Utils.wrapDialog(project, dialogStub, true);
            String newName = dialogStub.getReturnValue();
            if (!StringUtils.isEmpty(newName)) query.setName(newName);
        }
    }
}
