/* This work has been placed into the public domain. */

package kiyut.alkitab.bookviewer;

import java.awt.Component;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import javax.swing.AbstractListModel;
import javax.swing.JOptionPane;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import kiyut.alkitab.navigator.BibleKeyTreeModel;
import kiyut.alkitab.navigator.BibleKeyTreeNode;
import kiyut.alkitab.navigator.KeyTree;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.KeyFactory;
import org.crosswire.jsword.passage.NoSuchKeyException;
import org.crosswire.jsword.passage.Passage;
import org.crosswire.jsword.passage.PassageKeyFactory;
import org.crosswire.jsword.passage.RestrictionType;
import org.crosswire.jsword.passage.VerseRange;

/**
 * PassageChooser
 * 
 * @author Tonny Kohar <tonny.kohar@gmail.com>
 */
public class PassageChooser extends javax.swing.JPanel {
    protected ResourceBundle bundle = ResourceBundle.getBundle(this.getClass().getName());    
    
    protected KeyTree passageTree;
    protected PassageListModel passageListModel;
            
    protected KeyFactory keyFactory;
    protected Passage passage;
    //protected Versification v11n;
    
    protected boolean textAdjusting;
    
    /** Creates new PassageChooser */
    public PassageChooser() {
        initComponents();
        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() {
        java.awt.GridBagConstraints gridBagConstraints;

        jPanel1 = new javax.swing.JPanel();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        keyScrollPane = new javax.swing.JScrollPane();
        keyTree = new KeyTree();
        jToolBar1 = new javax.swing.JToolBar();
        addButton = new javax.swing.JButton();
        removeButton = new javax.swing.JButton();
        listScrollPane = new javax.swing.JScrollPane();
        passageList = new javax.swing.JList<>();
        jPanel2 = new javax.swing.JPanel();
        summaryLabel = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        passageTextArea = new javax.swing.JTextArea();

        setLayout(new java.awt.GridBagLayout());

        jPanel1.setLayout(new java.awt.GridBagLayout());

        jLabel2.setText(bundle.getString("CTL_AllVerses.Text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        jPanel1.add(jLabel2, gridBagConstraints);

        jLabel3.setText(bundle.getString("CTL_SelectedVerses.Text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        jPanel1.add(jLabel3, gridBagConstraints);

        keyScrollPane.setPreferredSize(new java.awt.Dimension(230, 430));
        keyScrollPane.setViewportView(keyTree);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        gridBagConstraints.weightx = 0.4;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
        jPanel1.add(keyScrollPane, gridBagConstraints);

        jToolBar1.setFloatable(false);
        jToolBar1.setOrientation(javax.swing.SwingConstants.VERTICAL);
        jToolBar1.setRollover(true);

        addButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kiyut/alkitab/bookviewer/right.png"))); // NOI18N
        addButton.setToolTipText(bundle.getString("HINT_AddVerses.Text")); // NOI18N
        addButton.setFocusPainted(false);
        addButton.setFocusable(false);
        addButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        addButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jToolBar1.add(addButton);

        removeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/kiyut/alkitab/bookviewer/left.png"))); // NOI18N
        removeButton.setToolTipText(bundle.getString("HINT_RemoveVerses.Text")); // NOI18N
        removeButton.setFocusPainted(false);
        removeButton.setFocusable(false);
        removeButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
        removeButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
        jToolBar1.add(removeButton);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 1;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.insets = new java.awt.Insets(6, 6, 6, 6);
        jPanel1.add(jToolBar1, gridBagConstraints);

        listScrollPane.setPreferredSize(new java.awt.Dimension(230, 100));
        listScrollPane.setViewportView(passageList);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 2;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        gridBagConstraints.weightx = 0.4;
        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
        jPanel1.add(listScrollPane, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 0.8;
        add(jPanel1, gridBagConstraints);

        jPanel2.setLayout(new java.awt.GridBagLayout());

        summaryLabel.setText(bundle.getString("CTL_Summary.Text")); // NOI18N
        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 0;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        jPanel2.add(summaryLabel, gridBagConstraints);

        passageTextArea.setLineWrap(true);
        passageTextArea.setRows(5);
        passageTextArea.setWrapStyleWord(true);
        jScrollPane1.setViewportView(passageTextArea);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.LINE_START;
        gridBagConstraints.weightx = 1.0;
        gridBagConstraints.weighty = 1.0;
        gridBagConstraints.insets = new java.awt.Insets(6, 0, 0, 0);
        jPanel2.add(jScrollPane1, gridBagConstraints);

        gridBagConstraints = new java.awt.GridBagConstraints();
        gridBagConstraints.gridx = 0;
        gridBagConstraints.gridy = 1;
        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
        gridBagConstraints.weighty = 0.2;
        gridBagConstraints.insets = new java.awt.Insets(8, 0, 0, 0);
        add(jPanel2, gridBagConstraints);
    }// </editor-fold>//GEN-END:initComponents
    
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton addButton;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JToolBar jToolBar1;
    private javax.swing.JScrollPane keyScrollPane;
    private javax.swing.JTree keyTree;
    private javax.swing.JScrollPane listScrollPane;
    private javax.swing.JList<VerseRange> passageList;
    private javax.swing.JTextArea passageTextArea;
    private javax.swing.JButton removeButton;
    private javax.swing.JLabel summaryLabel;
    // End of variables declaration//GEN-END:variables
    
    protected void initCustom() {
        textAdjusting = false;
        passageTree = (KeyTree)keyTree;
        passageTree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
        passageTree.setModel(new BibleKeyTreeModel(BibleKeyTreeModel.LEVEL_VERSE));
        
        passageListModel = new PassageListModel();
        passageList.setModel(passageListModel);
        
        keyFactory = PassageKeyFactory.instance();
        passage = (Passage)keyFactory.createEmptyKeyList();
        //v11n = Versifications.instance().getDefaultVersification();
        //passage = (Passage)keyFactory.createEmptyKeyList(v11n);
        
        summaryLabel.setIcon(UIManager.getIcon("OptionPane.informationIcon"));

        // XXX workaround for Windows Plaf for button margin
        Insets insets = new Insets(2, 2, 2, 2);
        addButton.setMargin(insets);
        removeButton.setMargin(insets);
        
        /*
        //Disable this, UI Gesture does not match, because double click is tree expand
        passageList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                if (evt.getClickCount() != 2) { return; }
                removePassage();
            }
        });
        */
        
        passageTextArea.getDocument().addDocumentListener(new DocumentListener() {
            @Override
            public void insertUpdate(DocumentEvent e) {
                textUpdated();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                textUpdated();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                textUpdated();
            }
            
            private void textUpdated() {
                if (textAdjusting) { return; }
                textAdjusting = true;
                updatePassage();
                textAdjusting = false;
            }
        });
        
        addButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                addPassage();
            }
        });
        
        removeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                removePassage();
            }
        });
    }
    
    /** 
     * @param keyString Passage Key as String or null
     */
    public void setPassage(String keyString) {
        if (keyString != null) {
            passageTextArea.setText(keyString);
        }
    }
    
    /** Show as Dialog 
     * @param parentComponent {@code Component}
     * @return JOptionPane.showConfirmDialog
     */
    public int showDialog(Component parentComponent) {
        int choice = JOptionPane.showConfirmDialog(parentComponent,this,bundle.getString("CTL_Title.Text"),
                JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE);
        return choice;
    }
    
    public Key getKey() {
        return this.passage;
    }
    
    protected void updatePassage() {
        String invalidMsg = null;
        try {
            if (textAdjusting) {
                Passage tPassage = (Passage) keyFactory.getKey(passageTextArea.getText());
                passage.clear();
                passage.addAll(tPassage);
            } else {
                passageTextArea.setText(passage.getName());
            }
            passageListModel.setPassage(passage);
        } catch (NoSuchKeyException ex) {
            invalidMsg = ex.getMessage();
        } 
        
        String msg = bundle.getString("CTL_Summary.Text");
        if (invalidMsg == null) {
            if (passage.countVerses() > 0) {
                msg = msg + ": " + passage.getOverview();
            }
            summaryLabel.setIcon(UIManager.getIcon("OptionPane.informationIcon"));
        } else {
            msg = msg + ": " + invalidMsg;
            summaryLabel.setIcon(UIManager.getIcon("OptionPane.errorIcon"));
        }
        
        summaryLabel.setText(msg);
    }
    
    protected void addPassage() {
        TreePath[] selection = passageTree.getSelectionPaths();
        if (selection == null) { return; }
            
        for (int i = 0; i < selection.length; i++) {
            BibleKeyTreeNode node = (BibleKeyTreeNode)selection[i].getLastPathComponent();
            if (node.getCategory() == BibleKeyTreeNode.BIBLE) {
                continue;
            }
            VerseRange verseRange = (VerseRange)node.getUserObject();
            passage.add(verseRange);
        }
        updatePassage();
    }
    
    protected void removePassage() {
        List<VerseRange> selection = passageList.getSelectedValuesList();
        if (selection == null) { return; }
        
        for (int i = 0; i < selection.size(); i++) {
            VerseRange verseRange = selection.get(i);
            passage.remove(verseRange);
        }

        int leadIndex = passageList.getSelectionModel().getLeadSelectionIndex();
        
        updatePassage();

        // try to position the selection
        if (leadIndex > 0) {
            leadIndex--;
        } 

        //System.out.println("index: " + leadIndex);

        if (leadIndex > passageListModel.getSize()-1) {
            leadIndex = passageListModel.getSize()-1;
        }

        if (leadIndex >= 0) {
            passageList.setSelectedIndex(leadIndex);
            passageList.repaint();
        }
    }
    
    public class PassageListModel extends AbstractListModel<VerseRange> {
        protected Passage passage;
        protected List<VerseRange> data;
        
        public PassageListModel() {
            data = new ArrayList<>();
        }

        public void setPassage(Passage passage) {
            fireIntervalRemoved(this, 0, getSize());
            
            this.passage = passage;
            data.clear();
            
            if (passage == null) {
                return;
            }
            
            passage.optimizeReads();
            
            @SuppressWarnings("unchecked")
            Iterator<VerseRange> iter = passage.rangeIterator(RestrictionType.CHAPTER);
            while (iter.hasNext()) {
                data.add(iter.next());
            }
            
            fireIntervalAdded(this, 0, getSize());
        }
        
        public Passage getPassage() {
            return this.passage;
        }
        
        @Override
        public int getSize() {
            return data.size();
        }

        @Override
        public VerseRange getElementAt(int index) {
            return data.get(index);
        }
        
    }
}
