/**
 *#########################################################################
 *
 * A component of the Gatherer application, part of the Greenstone digital
 * library suite from the New Zealand Digital Library Project at the
 * University of Waikato, New Zealand.
 *
 * <BR><BR>
 *
 * Author: Shaoqun Wu, Greenstone Digital Library, University of Waikato
 *
 * <BR><BR>
 *
 * Copyright (C) 2006 New Zealand Digital Library Project
 *
 * <BR><BR>
 *
 * 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.
 *
 * <BR><BR>
 *
 * 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.
 *
 * <BR><BR>
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 *########################################################################
 */
package org.greenstone.gatherer.gems;

import org.w3c.dom.Document;

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*; 

import java.util.Observer;
import java.util.Observable;
import java.util.ArrayList;

import org.greenstone.gatherer.Configuration;
import org.greenstone.gatherer.Dictionary;

/**
 * @author Shaoqun Wu, Greenstone Digital Library, University of Waikato
 * @version 2.4
 */
public class MetadataSetTree 
    extends JTree 
    implements Observer, ActionListener, MouseListener {
    
    private MetadataSetTree self;
    private MetadataSetModel metadata_model;
    protected JFrame parent_frame;
    public MetadataSetTree(JFrame parent){
        this.setComponentOrientation(Dictionary.getOrientation());
        setCellRenderer(new MetadataSetCellRenderer());
        setRowHeight(20);
        setEditable(false);
        getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
	setModel(null);
        self = this;
	parent_frame = parent;
	addMouseListener(this);	
    }
    
    public void mouseClicked(MouseEvent event) {
	// right click
	if (SwingUtilities.isRightMouseButton(event)) {
            new MetadataSetTreeRightClickMenu(this, event);
        } else {
	    TreePath tse = self.getSelectionPath();
	    if (tse!=null){
		DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
		
		Object data = snode.getUserObject();
		
		if (data instanceof MetadataElementModel){
		    
		    ((MetadataElementModel)data).notifyListeners(false);
		}
		else{
		    if (data instanceof MetadataSetInfo){
			((MetadataSetInfo)data).notifyListeners();
		    }
		}			  
	    }
	}
	
    }
    
    public void mouseEntered(MouseEvent event) { }

    public void mouseExited(MouseEvent event) { }

    public void mousePressed(MouseEvent event) { }

    public void mouseReleased(MouseEvent event) { }


    // Observer/Obserable became deprecated in JDK9 onwards, although with no set date for removal.
    // If looking to replace use of Observer/Obserable, then using java.bean.PropertyChangeLister
    // and PropertyChangeEvent is (StackOverflow) cited go to.
    // For a worked example comparing the two different approaches, see:
    //
    //   https://gist.github.com/mtorchiano/e69ac7e309fee81bd17f4f0740b9ffa9
    //   Replacing Observer-Observable (inheritance vs. composition)
    
    public void update(Observable o,Object arg){ 
        if (arg !=null){
	    Boolean replaceModel = (Boolean)arg;
            if (!replaceModel.booleanValue()){ 
                repaint();               
	 	expandAll();
	    }
	}      
        metadata_model = (MetadataSetModel)o;

	populateMetadataSetTree(metadata_model);

    }

    private void expandAll() { 
	int row = 0; 
	while (row < getRowCount()) {
	    expandRow(row);
	    row++;
	}
    } 



    public void actionPerformed(ActionEvent e){
        String command = e.getActionCommand();
   
        if (command.equals(GEMSConstants.ADD_ELEMENT)){
	    addElement(false);
 	    return ;
	}
        if (command.equals(GEMSConstants.ADD_SUBELEMENT)){
	    addElement(true);
 	    return ;
	}
       
	if (command.equals(GEMSConstants.DELETE_ELEMENT)){
	    deleteElement();
	}          


	if (command.equals(GEMSConstants.MOVE_UP)){
	    moveUpElement();
	}
       
	if (command.equals(GEMSConstants.MOVE_DOWN)){
	    moveDownElement(); 
	}
       
    }

     
    public void addElement(boolean subelement){
	TreePath tse = self.getSelectionPath();
	if (tse == null) return;
	
	DefaultTreeModel dtm = (DefaultTreeModel)getModel();
	DefaultMutableTreeNode snode=(DefaultMutableTreeNode)tse.getLastPathComponent();
	Object data = snode.getUserObject();
	if (data instanceof MetadataSetInfo){
	    data = metadata_model;
	}
	NewMetadataElementNamePrompt name_prompt = new NewMetadataElementNamePrompt(parent_frame, subelement, data);
	if (name_prompt.isCancelled()) {
	    return;
	}
	String new_name = name_prompt.getName();
	
	MetadataElementModel element_model = new MetadataElementModel(metadata_model.getMetadataSetInfo(), new_name);
	DefaultMutableTreeNode new_node = new DefaultMutableTreeNode(element_model);
	dtm.insertNodeInto(new_node,snode,snode.getChildCount());
	//update model
	if (data instanceof MetadataElementModel){
	    ((MetadataElementModel) data).addChild(element_model);
	}
	else if(data instanceof MetadataSetModel){
	    ((MetadataSetModel)data).addChild(element_model);
		
	}
	
	
	dtm.reload(snode);
	expandPath(tse);
	tse = tse.pathByAddingChild(new_node);
	self.setSelectionPath(tse);
	element_model.notifyListeners(false); 
    }
    
    

    public void deleteElement(){
	
	//int result = JOptionPane.showConfirmDialog(null, Dictionary.get("GEMS.Confirm_DeleteElement", "(Put name here)"), Dictionary.get("GEMS.Confirm_DeleteElement_Title"), JOptionPane.OK_CANCEL_OPTION);
	//if (result != JOptionPane.OK_OPTION) return;

	TreePath tse = self.getSelectionPath();
       	 
	if (tse!=null){
	    DefaultTreeModel dtm = (DefaultTreeModel)getModel();
	    DefaultMutableTreeNode snode=(DefaultMutableTreeNode)tse.getLastPathComponent();
	    DefaultMutableTreeNode parent = (DefaultMutableTreeNode) snode.getParent();
	    snode.removeFromParent();
            
	    Object parent_data = parent.getUserObject();
	    Object data = snode.getUserObject();
	    MetadataElementModel selected_model =  null;
	    if (parent_data instanceof MetadataElementModel){
		MetadataElementModel parent_model = (MetadataElementModel) parent_data;
		selected_model = (MetadataElementModel) data; 
		parent_model.removeChild(selected_model);
		parent_model.notifyListeners(false);
	    }
	    else{
		if(parent_data instanceof MetadataSetInfo){
		    selected_model = (MetadataElementModel) data; 
		    metadata_model.removeChild(selected_model);
		    ((MetadataSetInfo)parent_data).notifyListeners();
		    
		}
	    }
	     
	    dtm.reload(parent);
	    self.setSelectionPath(tse.getParentPath());
	    //selected_model.notifyListeners(true);// notify its listeners that it's been deleted
	}  

    } 

    private void moveUpElement(){
	TreePath tse = self.getSelectionPath();
        
	if (tse!=null){
	    DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
	    int row =  getSelectionRows()[0];                      
	    boolean moved = false;

	    Object data = snode.getUserObject();
	    
	    if (data instanceof MetadataElementModel){
		MetadataElementModel element_model = (MetadataElementModel)data;
                //top level element
                if (element_model.getParent() == null){
		    moved = metadata_model.moveUp(element_model);
                 
		}
                else{
		    MetadataElementModel parent = element_model.getParent();
		    moved = parent.moveUp(element_model);                
		} 
	       	if (moved){
                    DefaultTreeModel dtm = (DefaultTreeModel)getModel();
		    DefaultMutableTreeNode pnode= (DefaultMutableTreeNode) snode.getParent();
                    int index = pnode.getIndex(snode);
		    dtm.removeNodeFromParent(snode);
		    dtm.insertNodeInto(snode,pnode,--index);
                    dtm.reload(pnode);
		    setSelectionRow(--row);                         
		} 
	    }
	}

    }

    private void moveDownElement(){
	TreePath tse = self.getSelectionPath();
        
	if (tse!=null){         
	    boolean moved = false;
	    DefaultMutableTreeNode snode= (DefaultMutableTreeNode)tse.getLastPathComponent();
	    int row =  getSelectionRows()[0]; 	    
	    Object data = snode.getUserObject();
	    
	    if (data instanceof MetadataElementModel){
		MetadataElementModel element_model = (MetadataElementModel)data;
                //top level element
                if (element_model.getParent() == null){
		    moved = metadata_model.moveDown(element_model);
		}
                else{
		    MetadataElementModel parent = element_model.getParent();
		    moved = parent.moveDown(element_model);                
		} 
		// move the node down the tree     
		if (moved){
		    DefaultTreeModel dtm = (DefaultTreeModel)getModel();
		    DefaultMutableTreeNode pnode= (DefaultMutableTreeNode) snode.getParent();
                    int index = pnode.getIndex(snode);
		    dtm.removeNodeFromParent(snode);
		    dtm.insertNodeInto(snode,pnode,++index);
                    dtm.reload(pnode);
		    setSelectionRow(++row); 		                         
		} 
	    }
	}
    }
    

    private void populateMetadataSetTree(MetadataSetModel metadata_model){
	setModel(null);
      	MetadataSetInfo info= metadata_model.getMetadataSetInfo();
	if (info == null) {
	    repaint();
	    
	    return;
	}

        ArrayList metadata_elements = metadata_model.getMetadataSetModel();        	 

        DefaultTreeModel dtm = new DefaultTreeModel(new DefaultMutableTreeNode(info));
	DefaultMutableTreeNode root = (DefaultMutableTreeNode)dtm.getRoot();
       
        for(int i=0;i <metadata_elements.size();i++){
            MetadataElementModel element_model = (MetadataElementModel)metadata_elements.get(i);
	    DefaultMutableTreeNode element = new DefaultMutableTreeNode(element_model);            
	    dtm.insertNodeInto(element,root,root.getChildCount());
            ArrayList subelements = element_model.getChildElements();
	    for(int j=0;j <subelements.size();j++){
                MetadataElementModel subelement_model = (MetadataElementModel)subelements.get(j);
		DefaultMutableTreeNode subelement = new DefaultMutableTreeNode(subelement_model);            
		dtm.insertNodeInto(subelement,element,element.getChildCount());
            }
	} 	 
	 
	dtm.reload(root);
	setModel(dtm);       
             
    } 

    private class MetadataSetTreeRightClickMenu
	extends JPopupMenu {

	private MetadataSetTreeRightClickMenu(MetadataSetTree meta_set_tree, 
					      MouseEvent event)
	{

	    super();
	    
            // the right click position
	    boolean root_node = false;
	    boolean element_node = false;
	    TreePath root_path = meta_set_tree.getPathForRow(0);
            TreePath right_click_path = meta_set_tree.getPathForLocation(event.getX(), event.getY());
            if (right_click_path == null) {
		right_click_path = root_path;
		root_node = true;
	    } else if (right_click_path.equals(root_path)) {
		root_node = true;
	    } else if (right_click_path.getParentPath().equals(root_path)) {
		element_node = true;
	    } // else its a subelement
	    
	    meta_set_tree.clearSelection();
	    meta_set_tree.setSelectionPath(right_click_path);

	    JMenuItem add_element = new JMenuItem();
	    if (root_node) {
		add_element.setText(Dictionary.get("GEMS.Popup.AddElement"));
		add_element.setActionCommand(GEMSConstants.ADD_ELEMENT);
	    } else {
		add_element.setText(Dictionary.get("GEMS.Popup.AddSubElement"));
		add_element.setActionCommand(GEMSConstants.ADD_SUBELEMENT);

	    }
	    add_element.addActionListener(meta_set_tree);
	    add(add_element);
	    if (!root_node) {
		// delete element also
		JMenuItem delete_element = new JMenuItem();
		if (element_node) {
		    delete_element.setText(Dictionary.get("GEMS.Popup.DeleteElement"));
		} else {
		    delete_element.setText(Dictionary.get("GEMS.Popup.DeleteSubElement"));
		}
		delete_element.addActionListener(meta_set_tree);
		delete_element.setActionCommand(GEMSConstants.DELETE_ELEMENT);
		add(delete_element);
	    }

            // Show the popup menu on screen
            show(meta_set_tree, event.getX(), event.getY());
        }

    }
}


class MetadataSetCellRenderer extends JLabel implements TreeCellRenderer {
    
    private Color SELECTED_COLOR = Color.lightGray;
    private Color SELECTED_BORDER_COLOR = Color.gray;
      
   
    public Component getTreeCellRendererComponent(JTree tree,
						  Object value,
						  boolean selected,
						  boolean expanded,
						  boolean leaf,
						  int row,
						  boolean hasFocus){
	
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)value;
        Object userObject = node.getUserObject();

	if (userObject instanceof MetadataElementModel){
	    MetadataElementModel model = (MetadataElementModel)userObject;
	    setText(model.getFullName());
	}
	else{
	    if (userObject instanceof MetadataSetInfo){
		MetadataSetInfo info = (MetadataSetInfo)userObject;
                setText(info.getMetadataSetName());	
	    }
	}
   	 
	if (selected){
	    setBackground(SELECTED_COLOR);  
        
	}
	else{
	    setBackground(tree.getBackground());  
	}
	setForeground(tree.getForeground());  
	setOpaque(true);
	return this;
    }

} 
