/*
 *  Kontalk Java client
 *  Copyright (C) 2016 Kontalk Devteam <[email protected]>
 *
 *  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 3 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, see <http://www.gnu.org/licenses/>.
 */

package org.kontalk.view;

import javax.swing.Icon;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.DefaultFocusTraversalPolicy;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.SystemTray;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Optional;

import com.alee.extended.label.WebLinkLabel;
import com.alee.extended.label.WebVerticalLabel;
import com.alee.extended.painter.BorderPainter;
import com.alee.extended.panel.GroupPanel;
import com.alee.extended.panel.GroupingType;
import com.alee.extended.panel.WebOverlay;
import com.alee.laf.button.WebToggleButton;
import com.alee.laf.label.WebLabel;
import com.alee.laf.menu.WebMenu;
import com.alee.laf.menu.WebMenuBar;
import com.alee.laf.menu.WebMenuItem;
import com.alee.laf.optionpane.WebOptionPane;
import com.alee.laf.panel.WebPanel;
import com.alee.laf.rootpane.WebDialog;
import com.alee.laf.rootpane.WebFrame;
import com.alee.laf.scroll.WebScrollBar;
import com.alee.laf.scroll.WebScrollPane;
import com.alee.laf.tabbedpane.WebTabbedPane;
import com.alee.laf.table.WebTable;
import com.alee.laf.text.WebTextArea;
import com.alee.managers.hotkey.Hotkey;
import com.alee.managers.tooltip.TooltipManager;
import com.alee.utils.SwingUtils;
import com.alee.utils.WebUtils;
import org.kontalk.Kontalk;
import org.kontalk.model.Model;
import org.kontalk.persistence.Config;
import org.kontalk.system.Control;
import org.kontalk.util.Tr;

/**
 * The application window.
 * @author Alexander Bikadorov {@literal <[email protected]>}
 */
final class MainFrame extends WebFrame {

    enum Tab {CHATS, CONTACT}

    private static final int SCROLL_BAR_WIDTH =
            (int) new WebScrollBar(Adjustable.VERTICAL).getPreferredSize().getWidth();

    private final View mView;
    private final WebMenuItem mConnectMenuItem;
    private final WebMenuItem mDisconnectMenuItem;
    private final WebTabbedPane mTabbedPane;
    private final WebToggleButton mAddContactButton;

    MainFrame(View view,
            Model model,
            ListView contactList,
            ListView chatList,
            Component content,
            WebPanel searchPanel,
            Component statusBar) {
        mView = view;

        final Config conf = Config.getInstance();

        // general view + behaviour
        this.setTitle("Kontalk Java Client");
        this.setSize(conf.getInt(Config.VIEW_FRAME_WIDTH),
                conf.getInt(Config.VIEW_FRAME_HEIGHT));

        this.setIconImage(Utils.getImage("kontalk.png"));

        // closing behaviour
        this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                if (conf.getBoolean(Config.MAIN_TRAY_CLOSE) &&
                        SystemTray.getSystemTray().getTrayIcons().length > 0)
                    MainFrame.this.toggleState();
                else
                    mView.callShutDown();
            }
        });

        // menu
        WebMenuBar menubar = new WebMenuBar();
        this.setJMenuBar(menubar);

        WebMenu konNetMenu = new WebMenu("KonNet");
        konNetMenu.setMnemonic(KeyEvent.VK_K);

        mConnectMenuItem = new WebMenuItem(Tr.tr("Connect"));
        mConnectMenuItem.setAccelerator(Hotkey.ALT_C);
        mConnectMenuItem.setToolTipText(Tr.tr("Connect to Server"));
        mConnectMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                mView.getControl().connect();
            }
        });
        konNetMenu.add(mConnectMenuItem);

        mDisconnectMenuItem = new WebMenuItem(Tr.tr("Disconnect"));
        mDisconnectMenuItem.setAccelerator(Hotkey.ALT_D);
        mDisconnectMenuItem.setToolTipText(Tr.tr("Disconnect from Server"));
        mDisconnectMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                mView.getControl().disconnect();
            }
        });
        konNetMenu.add(mDisconnectMenuItem);
        konNetMenu.addSeparator();

        WebMenuItem statusMenuItem = new WebMenuItem(Tr.tr("Profile"));
        statusMenuItem.setAccelerator(Hotkey.ALT_S);
        statusMenuItem.setToolTipText(Tr.tr("Set your user profile"));
        statusMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                WebDialog statusDialog = new ProfileDialog(mView);
                statusDialog.setVisible(true);
            }
        });
        konNetMenu.add(statusMenuItem);
        konNetMenu.addSeparator();

        WebMenuItem exitMenuItem = new WebMenuItem(Tr.tr("Exit"));
        exitMenuItem.setAccelerator(Hotkey.ALT_E);
        exitMenuItem.setToolTipText(Tr.tr("Exit application"));
        exitMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                mView.callShutDown();
            }
        });
        konNetMenu.add(exitMenuItem);

        menubar.add(konNetMenu);

        WebMenu optionsMenu = new WebMenu(Tr.tr("Options"));
        optionsMenu.setMnemonic(KeyEvent.VK_O);

        WebMenuItem attMenuItem = new WebMenuItem(Tr.tr("Attachments"));
        attMenuItem.setAccelerator(Hotkey.ALT_A);
        attMenuItem.setToolTipText(Tr.tr("Open Attachment directory"));
        attMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Utils.createLinkRunnable(mView.getControl().getAttachmentDir()).run();
            }
        });
        optionsMenu.add(attMenuItem);
        optionsMenu.addSeparator();

        WebMenuItem confMenuItem = new WebMenuItem(Tr.tr("Preferences"));
        confMenuItem.setAccelerator(Hotkey.ALT_P);
        confMenuItem.setToolTipText(Tr.tr("Set application preferences"));
        confMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                mView.showConfig();
            }
        });
        optionsMenu.add(confMenuItem);

        menubar.add(optionsMenu);

        WebMenu helpMenu = new WebMenu(Tr.tr("Help"));
        helpMenu.setMnemonic(KeyEvent.VK_H);

        WebMenuItem wikiItem = new WebMenuItem(Tr.tr("Online Wiki"));
        wikiItem.setToolTipText(Tr.tr("Visit the wiki"));
        wikiItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                WebUtils.browseSiteSafely(Tr.getLocalizedWikiLink());
            }
        });
        helpMenu.add(wikiItem);
        WebMenuItem forumItem = new WebMenuItem(Tr.tr("Community Forum"));
        forumItem.setToolTipText(Tr.tr("Visit the Kontalk webforum"));
        forumItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                WebUtils.browseSiteSafely(View.KONTALK_FORUM);
            }
        });
        helpMenu.add(forumItem);
        WebMenuItem releaseItem = new WebMenuItem(Tr.tr("Release Website"));
        releaseItem.setToolTipText(Tr.tr("Check out the release website"));
        releaseItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                WebUtils.browseSiteSafely(View.KONTALK_RELEASES);
            }
        });
        helpMenu.add(releaseItem);

        helpMenu.addSeparator();

        WebMenuItem aboutMenuItem = new WebMenuItem(Tr.tr("About"));
        aboutMenuItem.setToolTipText(Tr.tr("About Kontalk"));
        aboutMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                MainFrame.this.showAboutDialog();
            }
        });
        helpMenu.add(aboutMenuItem);

        menubar.add(helpMenu);

        // Layout...
        this.setLayout(new BorderLayout(View.GAP_SMALL, View.GAP_SMALL));

        // ...left...
        mTabbedPane = new WebTabbedPane(WebTabbedPane.LEFT);
        String chatOverlayText =
                Tr.tr("No chats to display. You can create a new chat from the contact list.");
        ComponentUtils.PopupPanel addGroupChatPanel = new ComponentUtils.AddGroupChatPanel(mView, model);
        WebToggleButton addGroupButton = new ComponentUtils.ToggleButton(
                Utils.getIcon("ic_ui_add_group.png"), Tr.tr("Create a new group chat")) {
            @Override
            Optional<ComponentUtils.PopupPanel> getPanel() {
                return Optional.of(addGroupChatPanel);
            }
        };
        WebPanel chatListPanel = createListPanel(chatList, chatOverlayText, addGroupButton);
        mTabbedPane.addTab("", chatListPanel);
        mTabbedPane.setTabComponentAt(Tab.CHATS.ordinal(),
                new WebVerticalLabel(Tr.tr("Chats")));

        String contactOverlayText = Tr.tr("No contacts to display.");
        ComponentUtils.PopupPanel addContactPanel = new ComponentUtils.AddContactPanel(mView);
        mAddContactButton = new ComponentUtils.ToggleButton(
                Utils.getIcon("ic_ui_add_contact.png"),
                Tr.tr("Add a new contact")) {
            @Override
            Optional<ComponentUtils.PopupPanel> getPanel() {
                return Optional.of(addContactPanel);
            }
        };
        WebPanel contactListPanel = createListPanel(contactList, contactOverlayText, mAddContactButton);
        //contactListPanel.setPaintSides(false, false, false, false);
        mTabbedPane.addTab("", contactListPanel);
        mTabbedPane.setTabComponentAt(Tab.CONTACT.ordinal(),
                new WebVerticalLabel(Tr.tr("Contacts")));

        // setSize() does not work, whatever
        mTabbedPane.setPreferredSize(new Dimension(View.LISTS_WIDTH, -1));
        mTabbedPane.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                mView.tabPaneChanged(Tab.values()[mTabbedPane.getSelectedIndex()]);
            }
        });
        this.add(new GroupPanel(GroupingType.fillLast, false,
                searchPanel, mTabbedPane),
                BorderLayout.WEST);

        // ...right...
        this.add(content, BorderLayout.CENTER);

        this.setFocusTraversalPolicy(new DefaultFocusTraversalPolicy() {
            @Override
            public Component getDefaultComponent(Container aContainer) {
                return content;
            }
        });

        // ...bottom
        this.add(statusBar, BorderLayout.SOUTH);
    }

    Tab getCurrentTab() {
        return Tab.values()[mTabbedPane.getSelectedIndex()];
    }

    void selectTab(Tab tab) {
        mTabbedPane.setSelectedIndex(tab.ordinal());
    }

    void save() {
        Config conf = Config.getInstance();
        conf.setProperty(Config.VIEW_FRAME_WIDTH, this.getWidth());
        conf.setProperty(Config.VIEW_FRAME_HEIGHT, this.getHeight());
    }

    void toggleState() {
        if (this.getState() == Frame.NORMAL) {
            this.setState(Frame.ICONIFIED);
            this.setVisible(false);
        } else {
            this.setState(Frame.NORMAL);
            this.setVisible(true);
        }
    }

    final void onStatusChanged(Control.Status status) {
        switch (status) {
            case CONNECTING:
                mConnectMenuItem.setEnabled(false);
                break;
            case CONNECTED:
                mConnectMenuItem.setEnabled(false);
                mDisconnectMenuItem.setEnabled(true);
                mAddContactButton.setEnabled(true);
                break;
            case DISCONNECTING:
                mDisconnectMenuItem.setEnabled(false);
                mAddContactButton.setEnabled(false);
                break;
            case DISCONNECTED:
                // fallthrough
            case FAILED:
                // fallthrough
            case ERROR:
                mConnectMenuItem.setEnabled(true);
                mDisconnectMenuItem.setEnabled(false);
                mAddContactButton.setEnabled(false);
            break;
        }
    }

    private void showAboutDialog() {
        WebPanel aboutPanel = new WebPanel(new GridLayout(0, 1, View.GAP_SMALL, View.GAP_SMALL));
        aboutPanel.add(new WebLabel("Kontalk Java Client v" + Kontalk.VERSION));
        WebLinkLabel linkLabel = new WebLinkLabel();
        linkLabel.setLink(View.KONTALK_SITE);
        linkLabel.setText(Tr.tr("Visit kontalk.org"));
        aboutPanel.add(linkLabel);
        WebLabel soundLabel = new WebLabel(Tr.tr("Notification sound by")+" FxProSound");
        aboutPanel.add(soundLabel);
        Icon icon = Utils.getIcon("kontalk.png");
        WebOptionPane.showMessageDialog(this,
                aboutPanel,
                Tr.tr("About"),
                WebOptionPane.INFORMATION_MESSAGE,
                icon);
    }

    private static WebPanel createListPanel(final WebTable list,
                                            String overlayText,
                                            WebToggleButton button) {
        // overlay for empty list
        WebOverlay listOverlay = new WebOverlay(list);
        listOverlay.setOverlayMargin(20);
        final WebTextArea overlayArea = new WebTextArea();
        overlayArea.setText(overlayText);
        overlayArea.setLineWrap(true);
        overlayArea.setWrapStyleWord(true);
        overlayArea.setMargin(View.MARGIN_DEFAULT);
        overlayArea.setFontSize(View.FONT_SIZE_BIG);
        overlayArea.setEditable(false);
        BorderPainter<WebTextArea> borderPainter = new BorderPainter<>(Color.LIGHT_GRAY);
        borderPainter.setRound(15);
        overlayArea.setPainter(borderPainter);
        list.getModel().addTableModelListener(new TableModelListener() {
            @Override
            public void tableChanged(TableModelEvent e) {
                overlayArea.setVisible(list.getModel().getRowCount() == 0);
            }
        });
        overlayArea.setVisible(list.getModel().getRowCount() == 0);
        listOverlay.addOverlay(overlayArea, SwingConstants.CENTER, SwingConstants.CENTER);

        WebScrollPane scrollPane = new ComponentUtils.ScrollPane(listOverlay);
        scrollPane.setDrawBorder(false);

        // button as overlay
        button.setOpaque(false);
        button.setUndecorated(true);
        WebOverlay chatListOverlay = new WebOverlay(scrollPane,
                button, SwingConstants.TRAILING, SwingConstants.BOTTOM);
        chatListOverlay.setOverlayMargin(0, 0, View.GAP_BIG, View.GAP_BIG + SCROLL_BAR_WIDTH);
        // fixing overlay button paint bug on startup, dont wanna know whats happening here
        SwingUtils.delayInvokeLater(0, new Runnable() {
            @Override
            public void run() {
                TooltipManager.showOneTimeTooltip(list, new Point(1,1), "");
            }
        });

        return chatListOverlay;
    }
}