/*
 * Copyright (c) 2002-2007 TeamDev Ltd. All rights reserved.
 *
 * Use is subject to license terms.
 *
 * The complete licence text can be found at
 * http://www.teamdev.com/winpack/license.jsf
 */
package com.jniwrapper.win32.ie.samples.demo;

import com.jniwrapper.util.Logger;
import com.jniwrapper.win32.ie.Browser;
import com.jniwrapper.win32.ie.ContextMenuProvider;
import com.jniwrapper.win32.ie.WebBrowser;
import org.w3c.dom.Element;

import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GraphicsEnvironment;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.HashMap;
import java.util.Map;

/**
 * TabbedBrowsersPane class provides multi-tab browser support.
 *
 * @author Serge Piletsky
 */
class TabbedBrowsersPane extends TitledPane
{
    private static final Logger LOG = Logger.getInstance(TabbedBrowsersPane.class);

    public static final String PROPERTY_PROGRESS = "progress";
    public static final String PROPERTY_COMMAND_STATE = "commandState";

    private Map _component2BrowserMap = new HashMap();
    private Map _browserState2BrowserMap = new HashMap();

    private JTabbedPane _tabbedPane = new JTabbedPane();
    private JPopupMenu _actions;
    private JMenuItem _openLinkInNewBrowser;

    public TabbedBrowsersPane(JPopupMenu actions, final Action closeAction)
    {
        super();
        setBorder(null);
        _tabbedPane.setBorder(null);
        _tabbedPane.addMouseListener(new MouseAdapter()
        {
            public void mouseClicked(MouseEvent e)
            {
                if (e.getButton() == MouseEvent.BUTTON2)
                {
                    closeAction.actionPerformed(null);
                }
            }
        });
        _tabbedPane.addChangeListener(new ChangeListener()
        {
            public void stateChanged(ChangeEvent e)
            {
                setTitle(_tabbedPane.getTitleAt(_tabbedPane.getSelectedIndex()));
                updateCommandsState();
            }
        });

        _tabbedPane.setTabPlacement(JTabbedPane.BOTTOM);
        setComponent(_tabbedPane);

        assignCloseAction(closeAction);
        assignActions(actions);
        _actions = actions;
        _openLinkInNewBrowser = new JMenuItem(new OpenLinkInNewTab());
    }

    private class OpenLinkInNewTab extends AbstractAction
    {
        public OpenLinkInNewTab()
        {
            super("Open Link in New Tab");
        }

        public void actionPerformed(ActionEvent e)
        {
            WebBrowser webBrowser = getActiveBrowser();
            if (!(webBrowser instanceof Browser))
            {
                return;
            }

            Element element = ((Browser)webBrowser).getContextElement();
            if (element == null)
            {
                return;
            }

            if ("A".equalsIgnoreCase(element.getNodeName()))
            {
                WebBrowser browser = createBrowser();
                String url = element.getAttribute("href");
                browser.navigate(url);
            }
        }
    }

    private void updateCommandsState()
    {
        final WebBrowser activeBrowser = getActiveBrowser();

        if (activeBrowser == null)
        {
            return;
        }

        Boolean[] commandsState = (Boolean[])_browserState2BrowserMap.get(activeBrowser);
        if (commandsState == null)
        {
            commandsState = new Boolean[2];
            commandsState[0] = Boolean.FALSE;
            commandsState[1] = Boolean.FALSE;
        }

        fireCommandStateChanged(new CommandStateChangeEvent(activeBrowser, true, commandsState[0].booleanValue()));
        fireCommandStateChanged(new CommandStateChangeEvent(activeBrowser, false, commandsState[1].booleanValue()));
    }

    static Font getJapaneseFont()
    {
        Font[] allFonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
        String japaneseSample = "\u4e00";
        for (int i = 0; i < allFonts.length; i++)
        {
            if (allFonts[i].canDisplayUpTo(japaneseSample) == -1)
            {
                return new Font(allFonts[i].getName(), 0, 12);
            }
        }

        return null;
    }

    public void addBrowser(final Browser webBrowser)
    {
        webBrowser.setPreferredSize(new Dimension(100, 100));
        setupWebBrowser2EventsHandler(webBrowser);
        final Runnable runnable = new Runnable()
        {
            public void run()
            {
                ContextMenuProvider contextMenuProvider = new ContextMenuProvider()
                {
                    public JPopupMenu getPopupMenu(Element contextNode)
                    {
                        final boolean isLink = "A".equals(contextNode.getTagName());
                        if (isLink)
                        {
                            _actions.add(_openLinkInNewBrowser);
                        }
                        else
                        {
                            _actions.remove(_openLinkInNewBrowser);
                        }
                        return _actions;
                    }
                };
                webBrowser.setContextMenuProvider(contextMenuProvider);

                _tabbedPane.addTab(null, webBrowser);
                _tabbedPane.setSelectedComponent(webBrowser);

                Font font = getJapaneseFont();
                if (font != null)
                {
                    _tabbedPane.setFont(font);
                }
            }
        };

        SwingUtilities.invokeLater(runnable);

        _component2BrowserMap.put(webBrowser, webBrowser);
    }

    /**
     * Creates a new browser and appropriate tab for it.
     */
    public WebBrowser createBrowser()
    {
        final Browser webBrowser = new Browser();
        addBrowser(webBrowser);
        return webBrowser;
    }

    int getBrowserIndex(WebBrowser webBrowser)
    {
        return _tabbedPane.indexOfComponent((Component)webBrowser);
    }

    /**
     * Updates the title of the browser.
     *
     * @param webBrowser a browser to update the title for.
     * @param title      a new title.
     */
    public void updateBrowserTitle(WebBrowser webBrowser, String title)
    {
        final int index = getBrowserIndex(webBrowser);
        _tabbedPane.setTitleAt(index, title);
        _tabbedPane.setToolTipTextAt(index, title);
        setTitle(title);
    }

    /**
     * Removes a browser from the pane.
     *
     * @param webBrowser webBrowser to remove.
     */
    public void removeBrowser(final WebBrowser webBrowser)
    {
        final int index = setActiveBrowser(webBrowser);

        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                _tabbedPane.remove(index);
                if (index == 0)
                {
                    _tabbedPane.setSelectedIndex(index);
                }
                webBrowser.close();
                _component2BrowserMap.remove(webBrowser);
                updateCommandsState();
            }
        });
    }

    void removeBrowser()
    {
        final WebBrowser browser = getActiveBrowser();

        if (_tabbedPane.getTabCount() > 1)
        {
            setActiveBrowser(browser);
        }

        SwingUtilities.invokeLater(new Runnable()
        {
            public void run()
            {
                if (_tabbedPane.getTabCount() > 1)
                {
                    removeBrowser(browser);
                }
            }
        });
//        else if (_tabbedPane.getTabCount() == 1)
//        {
//            WebBrowser browserForClose = getActiveBrowser();
//            createBrowser();
//            removeBrowser(browserForClose);
//        }
    }

    private int setActiveBrowser(WebBrowser webBrowser)
    {
        int newIndex;
        int index = getBrowserIndex(webBrowser);
        if (index == (_tabbedPane.getTabCount() - 1))
        {
            newIndex = index - 1;
        }
        else
        {
            newIndex = index + 1;
        }

        _tabbedPane.setSelectedIndex(newIndex);

        return index;
    }

    /**
     * Returns an active browser.
     *
     * @return active browser.
     */
    public WebBrowser getActiveBrowser()
    {
        final Component selectedComponent = _tabbedPane.getSelectedComponent();
        return (WebBrowser)_component2BrowserMap.get(selectedComponent);
    }

    private void setupWebBrowser2EventsHandler(WebBrowser browser)
    {
        BrowserEventsListener eventHandler = new BrowserEventsListener();

        browser.addNavigationListener(eventHandler);
        browser.addStatusListener(eventHandler);
        browser.setEventHandler(eventHandler);

        eventHandler._tabbedBrowsersPane = this;
        eventHandler._webBrowser = browser;

        BrowserNewWindowEventHandler handler = new BrowserNewWindowEventHandler(browser);
        handler._tabbedBrowsersPane = this;

        browser.setNewWindowHandler(handler);
    }

    public JTabbedPane getTabbedPane()
    {
        return _tabbedPane;
    }

    private void updateButtonState(WebBrowser browser, boolean isBackButton, boolean enable)
    {
        Boolean[] commandsState = (Boolean[])_browserState2BrowserMap.get(browser);
        if (commandsState == null)
        {
            commandsState = new Boolean[2];
            commandsState[0] = Boolean.FALSE;
            commandsState[1] = Boolean.FALSE;
            _browserState2BrowserMap.put(browser, commandsState);
        }

        int index = isBackButton ? 0 : 1;
        commandsState[index] = new Boolean(enable);

        fireCommandStateChanged(new CommandStateChangeEvent(browser, isBackButton, enable));
    }

    public void backButtonEnabled(WebBrowser browser, boolean enabled)
    {
        updateButtonState(browser, true, enabled);
    }

    public void forwardButtonEnabled(WebBrowser browser, boolean enabled)
    {
        updateButtonState(browser, false, enabled);
    }

    public void progressChanged(int progress, int progressMax)
    {
        firePropertyChange(PROPERTY_PROGRESS, new Integer(progress), new Integer(progressMax));
    }

    protected void fireCommandStateChanged(CommandStateChangeEvent event)
    {
        firePropertyChange(PROPERTY_COMMAND_STATE, null, event);
    }
}
