/* This work has been placed into the public domain. */
        
package kiyut.alkitab.windows;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.JTabbedPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.HyperlinkListener;
import kiyut.alkitab.bookviewer.BookViewerManager;
import kiyut.alkitab.bookviewer.DictionaryPane;
import kiyut.alkitab.bookviewer.SwordURI;
import kiyut.alkitab.options.BookViewerOptions;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookFilter;
import org.crosswire.jsword.book.BookFilters;
import org.crosswire.jsword.book.Books;
import org.crosswire.jsword.passage.Key;
import org.openide.awt.ActionID;
import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences;
import org.openide.awt.StatusDisplayer;
import org.openide.awt.TabbedPaneFactory;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent;

/**
 * TopComponent which displays {@link kiyut.alkitab.bookviewer.DictionaryPane DictionaryPane}.
 * 
 * @author Tonny Kohar <tonny.kohar@gmail.com>
 */
@TopComponent.Description(preferredID = "DefinitionsTopComponent",
    //iconBase="SET/PATH/TO/ICON/HERE", 
    persistenceType = TopComponent.PERSISTENCE_ALWAYS)
@TopComponent.Registration(mode = "output", openAtStartup = false, position=200)
@ActionID(category = "Window", id = "kiyut.alkitab.actions.DefinitionsAction")
@ActionReferences({
    @ActionReference(path = "Menu/Window", position = 140),
    @ActionReference(path = "Shortcuts", name = "DO-5")
})
@TopComponent.OpenActionRegistration(displayName = "#CTL_DefinitionsAction",
    preferredID = "DefinitionsTopComponent")
public final class DefinitionsTopComponent extends TopComponent {

    private JTabbedPane tabbedPane;
    
    private HyperlinkListener hyperlinkListener;

    protected PropertyChangeListener backgroundPropertyChangeListener;
    
    private DefinitionsTopComponent() {
        initComponents();
        setName(NbBundle.getMessage(DefinitionsTopComponent.class, "CTL_DefinitionsTopComponent"));
        setToolTipText(NbBundle.getMessage(DefinitionsTopComponent.class, "HINT_DefinitionsTopComponent"));
//        setIcon(Utilities.loadImage(ICON_PATH, true));
        
        initCustom();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setLayout(new java.awt.BorderLayout());
    }// </editor-fold>//GEN-END:initComponents
    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
    
    void writeProperties(java.util.Properties p) {
        // better to version settings since initial version as advocated at
        // http://wiki.apidesign.org/wiki/PropertyFiles
        p.setProperty("version", "1.0");
        // TODO store your settings
    }

    void readProperties(java.util.Properties p) {
        String version = p.getProperty("version");
        // TODO read your settings according to their version
    }
    
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        super.writeExternal(out);
        
        int count = tabbedPane.getTabCount();
        if (count <= 0) { 
            out.writeInt(count);
            return; 
        }
        
        List<String> bookNames  = new ArrayList<String>(count);
        for (int i = 0; i < count; i++) {
            DictionaryPane dicPane = (DictionaryPane) tabbedPane.getComponentAt(i);
            bookNames.add(dicPane.getBook().getInitials());
        }
        
        int selectedIndex = tabbedPane.getSelectedIndex();
        
        out.writeInt(count);
        out.writeObject(bookNames);
        out.writeInt(selectedIndex);
    }
    
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        super.readExternal(in);
        
        int count = in.readInt();
        if (count <= 0) { return; }
        
        Object obj = in.readObject();
        if (obj instanceof List) {
            List<String> bookNames = (List<String>)obj;
            for (int i=0; i<count; i++ ) {
                String name = bookNames.get(i);
                SwordURI uri = SwordURI.createURI(SwordURI.DICTIONARY_SCHEME, name, null);
                openURI(uri, null);
            }
        }
        
        int selectedIndex = in.readInt();
        tabbedPane.setSelectedIndex(selectedIndex);
    }
    
    @Override
    public javax.swing.Action[] getActions() {
        List<Action> actionList = new ArrayList<Action>();
        
        // add 
        actionList.add(new ViewSourceAction());
        actionList.add(null);  // separator
        actionList.addAll(Arrays.asList(super.getActions()));
        
        return actionList.toArray(new Action[0]);
    }
    
    private void initCustom() {
        tabbedPane = TabbedPaneFactory.createCloseButtonTabbedPane();
        tabbedPane.addPropertyChangeListener( TabbedPaneFactory.PROP_CLOSE, new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                JTabbedPane pane = (JTabbedPane)evt.getSource();
                Object obj = evt.getNewValue();
                if (obj == null || !(obj instanceof DictionaryPane)) {
                    return;
                }
                DictionaryPane dicPane = (DictionaryPane)obj;
                dicPane.removeHyperlinkListener(hyperlinkListener);
                pane.remove(dicPane);
            }
        });
        this.add(BorderLayout.CENTER, tabbedPane);
        
        hyperlinkListener = new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent evt) {
                DefinitionsTopComponent.this.hyperlinkUpdate(evt);
            }
        };

        backgroundPropertyChangeListener = new PropertyChangeListener() {
            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (!evt.getPropertyName().equals(BookViewerOptions.BACKGROUND)) {
                    return;
                }

                Color bg = null;
                if (evt.getNewValue() instanceof Color) {
                    bg = (Color) evt.getNewValue();
                }

                backgroundPropertyChange(bg);
            }
        };

        // this part make the loading slower
        BookViewerOptions opts = BookViewerOptions.getInstance();
        opts.addPropertyChangeListener(backgroundPropertyChangeListener);
        
        String name = opts.getDefaultDictionary();
        if (name == null) { return; }
        SwordURI uri = SwordURI.createURI(SwordURI.DICTIONARY_SCHEME, name, null);
        openURI(uri, null);
    }
    
    public void openURI(SwordURI uri, String info) {
        if (uri.getType() != SwordURI.Type.DICTIONARY 
                && uri.getType() != SwordURI.Type.GLOSSARY
                && uri.getType() != SwordURI.Type.GREEK_STRONGS
                && uri.getType() != SwordURI.Type.HEBREW_STRONGS
                && uri.getType() != SwordURI.Type.GREEK_MORPH) { 
            return; 
        }

        String name = uri.getPath();
        if (name.isEmpty() ) {
            switch (uri.getType()) {
                case GREEK_STRONGS:
                    name = getValidBookName(BookViewerOptions.getInstance().getDefaultGreekStrongs(), BookFilters.getGreekDefinitions());
                    break;
                case HEBREW_STRONGS:
                    name = getValidBookName(BookViewerOptions.getInstance().getDefaultHebrewStrongs(), BookFilters.getHebrewDefinitions());
                    break;
                case GREEK_MORPH:
                    name = getValidBookName(BookViewerOptions.getInstance().getDefaultGreekMorph(), BookFilters.getGreekParse());
                    break;
                default:
                    break;
            }
        }
        
        Book book = (name != null ? Books.installed().getBook(name) : null);
        if (book == null) {
            return;
        }
        
        int index = -1;
        
        // find if the dictionary already opened or not
        for (int i=0; i<tabbedPane.getTabCount(); i++) {
            DictionaryPane dicPane = (DictionaryPane)tabbedPane.getComponentAt(i);
            if (dicPane.getBook().getInitials().equals(book.getInitials())) {
                index = i;
                break;
            }
        }
        
        if (index == -1) {
            DictionaryPane dicPane = new DictionaryPane(book);
            dicPane.addHyperlinkListener(hyperlinkListener);
            dicPane.setName(book.getInitials());
            
            // XXX do not use addTab, there is bug for space between title and x button
            // tabbedPane.addTab(book.getInitials() + "   ", null, dicPane, book.getName());
            tabbedPane.add(dicPane);
            index = tabbedPane.getTabCount() - 1;
            tabbedPane.setToolTipTextAt(index, book.getName());

            //Color bg = BookViewerOptions.getInstance().getBackground();
            //backgroundPropertyChange(bg);
        }
        
        tabbedPane.setSelectedIndex(index);
        
        String keyString = uri.getFragment();
        if (keyString != null && !keyString.isEmpty()) {
            DictionaryPane dicPane = (DictionaryPane)tabbedPane.getSelectedComponent();
            Book theBook = dicPane.getBook();
            if (book != null) {
                Key key = theBook.getValidKey(keyString);
                dicPane.setKey(key);
            }
        }
    }

    @SuppressWarnings("unchecked")
    private String getValidBookName(String name, BookFilter filter) {
        boolean found = false;
        List<Book> books = Books.installed().getBooks(filter);
        
        if (name != null) {
            for (int i=0; i<books.size(); i++) {
                if (books.get(i).getInitials().equalsIgnoreCase(name)) {
                    found = true;
                    break;
                }
            }
        }
        
        if (!found && !books.isEmpty()) {
                name = books.get(0).getInitials();
        }
        
        return name;
    }
    
    protected void hyperlinkUpdate(HyperlinkEvent evt) {
        EventType eventType = evt.getEventType();
        String uri = evt.getDescription();
        SwordURI swordURI = SwordURI.createURI(uri);
        
        if (swordURI == null) {
            Logger logger = Logger.getLogger(DefinitionsTopComponent.class.getName());
            logger.log(Level.WARNING, "invalid SwordURI: {0}", uri);
            
        }
        
        if (eventType.equals(HyperlinkEvent.EventType.ACTIVATED)) {
            String fragment = swordURI.getFragment();
            if (fragment.length() > 0) {
                if (fragment.charAt(0) == '#') {
                    return;
                }
            }
            
            BookViewerManager.getInstance().openURI(swordURI);
        } else if (eventType.equals(HyperlinkEvent.EventType.ENTERED)) {
            //StatusDisplayer.getDefault().setStatusText(uri);
            StatusDisplayer.getDefault().setStatusText(swordURI.toString());
        }
    }

    protected void backgroundPropertyChange(Color bg) {
        for (int i=0; i<tabbedPane.getTabCount(); i++) {
            DictionaryPane dicPane = (DictionaryPane)tabbedPane.getComponentAt(i);
            //dicPane.getViewerComponent().setBackground(bg);
            dicPane.getBookRenderer().reload();
        }
    }
    
    private class ViewSourceAction extends AbstractAction {
        public ViewSourceAction() {
            putValue(Action.NAME, NbBundle.getMessage(ViewSourceAction.class, "CTL_ViewSourceAction"));
        }

        @Override
        public void actionPerformed(ActionEvent evt) {
            int i = tabbedPane.getSelectedIndex();
            if (i < 0) { return; }
            
            DictionaryPane dicPane = (DictionaryPane)tabbedPane.getComponentAt(i);
            dicPane.viewSource();
        }
    }
}
