/**
 *  RELOAD TOOLS
 *
 *  Copyright (c) 2003 Oleg Liber, Bill Olivier, Phillip Beauvoir
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  Project Management Contact:
 *
 *  Oleg Liber
 *  Bolton Institute of Higher Education
 *  Deane Road
 *  Bolton BL3 5AB
 *  UK
 *
 *  e-mail:   o.liber@bolton.ac.uk
 *
 *
 *  Technical Contact:
 *
 *  Phillip Beauvoir
 *  e-mail:   p.beauvoir@bolton.ac.uk
 *
 *  Web:      http://www.reload.ac.uk
 *
 */

package uk.ac.reload.editor.contentpackaging.editor.resourceview.metadata;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.List;
import java.util.Vector;
import java.util.Stack;
import javax.swing.*;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.filter.ElementFilter;
import org.jdom.JDOMException;

import uk.ac.reload.dweezil.gui.DweezilFileChooser;
import uk.ac.reload.dweezil.gui.DweezilFileFilter;
import uk.ac.reload.dweezil.gui.DweezilToolBar;
import uk.ac.reload.dweezil.gui.ErrorDialogBox;
import uk.ac.reload.dweezil.util.DweezilUIManager;
import uk.ac.reload.editor.EditorFrame;
import uk.ac.reload.editor.EditorHandler;
import uk.ac.reload.editor.Messages;
import uk.ac.reload.editor.contentpackaging.xml.CP_SchemaController;
import uk.ac.reload.editor.contentpackaging.xml.ContentPackage;
import uk.ac.reload.editor.menu.Menu_Edit;
import uk.ac.reload.editor.metadata.xml.MD_SchemaController;
import uk.ac.reload.editor.metadata.xml.MD121_SchemaController;
import uk.ac.reload.editor.metadata.xml.Metadata;
import uk.ac.reload.editor.prefs.EditorPrefs;
import uk.ac.reload.moonunit.schema.SchemaException;
import uk.ac.reload.editor.IIcons;
import uk.ac.reload.editor.gui.widgets.FileTextFieldWidget;
import uk.ac.reload.dweezil.gui.layout.SGLayout;

/**
 * The Metadata Editor Dialog used in the CP Editor
 * It edits MD from a node of a CP
 *
 * @author Phillip Beauvoir
 * @version $Id: MD_EditorDialog.java,v 1.2 2004/11/06 18:03:11 phillipus Exp $
 */
public class CP_ResourcesByMetadata
extends JDialog implements IIcons
{

	public static final int PARAMETERS = 0;
	public static final int KEYWORD = 1;

	private int searchMethod = PARAMETERS;
	
	/**
     * The Metadata Editor Panel
     */
    private MD_EditorPanel _mdEditorPanel;

 
    /**
     * The parent CP "metadata" element containing the lom
     */
    private Element patronMD, fileMD;

    private Vector filesAllTemp = new Vector(0,1);
    private Vector searchCriterion = new Vector(0,1);

    JList targetFilesList;
    JLabel numberOfFounds;
    
    private File poolFolder;
    private File[] filesAll, filesTarget, filesToOpen;
    private FileTextFieldWidget labelFolder;
    private boolean approved = false;
    
	// This filter returns any files that ends with `_LOMv1.xml'.
   	private FilenameFilter filter = new FilenameFilter() {
	    public boolean accept(File dir, String name) {
	      	return name.endsWith("_LOMv1.xml");
	    }
	};
	
  	private FileFilter onlyDirectories = new FileFilter() {
	    public boolean accept(File dir) {
	      	return dir.isDirectory();
	    }
	};
	

	
    
    /**
     * Constructor
     * @param contentPackage The Content Package that is the parent Document
     * @param mdElement The "metadata" Element in the Content Package
     * @throws JDOMException
     * @throws IOException
     * @throws SchemaException
     */
    public CP_ResourcesByMetadata(File _folder) throws JDOMException, IOException, SchemaException {
        super(EditorFrame.getInstance(),
                Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.0") ,true); 


        patronMD = new Element("metadata");
        poolFolder = _folder;
        filesAll = null;
        filesTarget = null;
        filesToOpen = null;

        
        // Trap window closing event for our Window listener
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

        // Add a listener to Close our window down on exit
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                dispose();
            }
        });
        

        
        JSplitPane pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);

        // Add MD Editor
        _mdEditorPanel = new MD_EditorPanel();
        //getContentPane().add(_mdEditorPanel, BorderLayout.CENTER);
        //pane.add(_mdEditorPanel);
        pane.setTopComponent(_mdEditorPanel);
        
        // The south Panel which includes buttonsUpPanel, filesPanel & buttonsDownPanel
        JPanel southPanel = new JPanel(new BorderLayout());
   
        // The buttonsUpPanel
        // LeftDown Panel
        JPanel leftPanel = new JPanel();
        leftPanel.setLayout(new FlowLayout(FlowLayout.LEFT));
        
        JLabel labelSCOFolder = new JLabel(Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.1") + ": ");
        labelFolder = new FileTextFieldWidget(FileTextFieldWidget.FOLDER_TYPE, poolFolder.toString(), DweezilUIManager.getIcon(ICON_OPEN));
        labelFolder.setTextValue(poolFolder.toString());
        labelFolder.setPreferredSize(new Dimension(400,22));
        leftPanel.add(labelSCOFolder);
        leftPanel.add(labelFolder);
        // RightDown Panel
        JPanel rightPanel = new JPanel();
        rightPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        
        // Label which shows the number of results
        numberOfFounds = new JLabel("");
        
        // Search Button
        JButton btnSEARCH = new JButton(Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.2")); //$NON-NLS-1$
        btnSEARCH.addActionListener(new SEARCHClick());
        getRootPane().setDefaultButton(btnSEARCH);
        rightPanel.add(btnSEARCH);

        JPanel buttonsUpPanel = new JPanel(new BorderLayout());
        buttonsUpPanel.add(leftPanel, BorderLayout.WEST);
        buttonsUpPanel.add(numberOfFounds, BorderLayout.CENTER);
        buttonsUpPanel.add(rightPanel, BorderLayout.EAST);

        southPanel.add(buttonsUpPanel, BorderLayout.NORTH);
        
        
        // The filesPanel

        JPanel filesPanel = new JPanel(new SGLayout(1,1,SGLayout.FILL,SGLayout.FILL,0,0));
        targetFilesList = new JList(new File[] {});
        JScrollPane scrollPane = new JScrollPane(targetFilesList);
        JTabbedPane tabbedPane = new JTabbedPane();
        tabbedPane.add(Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.6"),scrollPane);
        filesPanel.add(tabbedPane);

        
        southPanel.add(filesPanel, BorderLayout.CENTER);

        // The buttonsDown Panel
        JPanel buttonsDownPanel = new JPanel();
        buttonsDownPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
        // OK Button
        JButton btnOK = new JButton(Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.3")); //$NON-NLS-1$
        btnOK.addActionListener(new OKClick());
        buttonsDownPanel.add(btnOK);
        // Cancel Buttton
        JButton btnCancel = new JButton(Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.4")); //$NON-NLS-1$
        btnCancel.addActionListener(new CancelClick());
        buttonsDownPanel.add(btnCancel);
 
        southPanel.add(buttonsDownPanel, BorderLayout.SOUTH);
        
        
        pane.setBottomComponent(southPanel);
        pane.setResizeWeight(1);

        getContentPane().add(pane, BorderLayout.CENTER);

        // Dialog Size
        setSize(EditorFrame.getInstance().getWidth() * 3/4, EditorFrame.getInstance().getHeight() * 3/4);
        setLocationRelativeTo(EditorFrame.getInstance());

        // Create the MD JDOM Document
        createMetadataDocument();
    }

    /**
     * Create a Metadata Document for Editing
     * @throws JDOMException
     * @throws IOException
     * @throws SchemaException
     */
    protected void createMetadataDocument() throws JDOMException, IOException, SchemaException {
  

        // No we didn't find a "lom" element, so create a new blank MD document based on the
        // Parent CP's MD Namespace (if there is one)       
       	// No root Namespace, so use default Controller

//    	MD_SchemaController mdController = ((CP_SchemaController)cp.getSchemaController()).getDefaultMD_SchemaController();
    	MD_SchemaController mdController = new MD121_SchemaController();
   
         // Edit a new document
         Metadata md = new Metadata(false, mdController);
         _mdEditorPanel.setDocument(md);
        
    }
    
 

    /**
     * Show the Dialog
     */
    public void show() {
         super.show();
    }

    protected void search() {
    	
    	
    	// Preparamos para búsqueda por coincidencia de campos
    	if(_mdEditorPanel.getSelectedTab() == 0) {    	
    		searchMethod = PARAMETERS;
    	
    		// Get the Metadata from the Editor
    		Metadata md = _mdEditorPanel.getMetadata();

    		// If user edited the MD
    		if(md.isDirty()) {
    			// If we had an old node, remove it (in order to new searchs)
    			if (patronMD != null) {
    				if (patronMD.getContent() != null) {
    					List borrar = patronMD.getContent();
    					for (int bIndex = 0; bIndex < borrar.size(); bIndex++)
    						patronMD.removeContent((Element) borrar.get(bIndex));
    				}
    			}
    			// Created embedded version (add prefixes)
    			Metadata md2 = md.createEmbeddedMetadata();
            
    			// Get root element from MD Document
    			Element newRoot = md2.getRootElement();
                      
    			newRoot.detach();

    			// Attach it
    			patronMD.addContent(newRoot);

    			// Capturamos los criterios de búsqueda
    	    	searchCriterion.clear();
    	    	fillCriterionFromElement(patronMD);
    		
    		} 
    	// Preparamos para búsqueda por keyword
    	} else {
    		searchMethod = KEYWORD;
    		searchCriterion.clear();
    		if (!_mdEditorPanel.getKeyword().equals(""))
    			fillCriterionFromElement(_mdEditorPanel.getKeyword());
    		//System.out.println("Keyword: " + _mdEditorPanel.getKeyword());
    		
    		
    	}
    	
    	//*************** Aquí tenemos en el Element patronMD los datos por los que buscar
    	
    	//leer todos los ficheros de poolFolder    	
    	poolFolder = new File(labelFolder.getTextValue());
        filesAllTemp.clear();
        addFiles(poolFolder, EditorPrefs.getInstance().getBooleanValue(EditorPrefs.POOL_EXTENSIVE_SEARCH));
    	// The list of files .xml metadata
        
      	filesAll = new File[filesAllTemp.size()];
    	for(int i= 0; i< filesAllTemp.size(); i++) {
    		filesAll[i] = new File( filesAllTemp.get(i).toString());
    	}
    	
    	
    	//printCriterionSearch();
    	
    	//Para cada metadata comprobamos si se ajusta al perfil
    	boolean fitsToCriterion = false;

    	Vector founded = new Vector(0,1);
    	//System.out.println("Ficheros totales en la búsqueda: " + filesAll.length);


    	for (int indexFile=0; indexFile<filesAll.length; indexFile++) {
    		fitsToCriterion = false;
    		
    		
    		//****** ZONA PARA COMPARAR LOS METADATOS
    		try{
    			Metadata m = new Metadata(filesAll[indexFile]);
        		fileMD = m.getRootElement();
    		} catch (Exception e) {
    			e.printStackTrace();
    			return;
    		}
    		
    		if (searchMethod == PARAMETERS)
    			fitsToCriterion = fitsTo(fileMD);    		    	
    		else
    			fitsToCriterion = fitsByKeywordTo(fileMD);
    		
    		//******
    		if (fitsToCriterion) {
    			String scoName = filesAll[indexFile].toString();
    			scoName = scoName.substring( 0 , scoName.lastIndexOf("_LOMv1.xml"));
    			scoName += ".jar";
    			founded.add(new File(scoName));
    		}
    	}    	

    	filesTarget = new File[founded.size()];
    	for(int i= 0; i< founded.size(); i++) {
    		filesTarget[i] = new File( founded.get(i).toString());
    	}
    	        
    	if (searchCriterion.size() > 0){
    		if (searchMethod == PARAMETERS) {
    			numberOfFounds.setText("      " + 
    					Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.8") + ": " + searchCriterion.size() +
    					"      "+
    					Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.6") + ": " + filesTarget.length );
    		} else {
    			numberOfFounds.setText("      " +
    					Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.13") +
    					"      "+
    					Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.6") + ": " + filesTarget.length );
    		}
    	} else
    		numberOfFounds.setText("      " + Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.9"));
    	
    	targetFilesList.setListData((File[])filesTarget);    	    	
    }

    private void addFiles(File root, boolean descend) {
    	File[] rightXMLs = root.listFiles(filter);
    	for (int i = 0; i < rightXMLs.length; i++) {
    		filesAllTemp.add((File) rightXMLs[i]);
    	}
    	if (descend) {
    		File[] children = root.listFiles(onlyDirectories);
    		if( children != null) {
    			for (int i = 0; i < children.length; i++) {
    				addFiles(children[i], true);
    			}
    		}
    	}
    }
    

    protected class Criterion {
        private String value;
        private Vector path;
    	public Criterion(String v,Vector p) {
            value = v;
            path = p;
        }
    	public String getValue(){
    		return value;
    	}
    	public Vector getPath(){
    		return path;
    	}
    }  
    
    private void printCriterionSearch() {
		System.out.println("Criterios de búsqueda encontrados...");
		Criterion criterion = null;
		for (int i= 0; i < searchCriterion.size(); i++){
			criterion = (Criterion) searchCriterion.get(i);
			List path = criterion.getPath();
			System.out.print("Criterio #" + i + ": ");
			for (int j=0; j < path.size() ; j++) {
				System.out.print("\\" + path.get(j).toString());
			}
			System.out.println(" = " + criterion.getValue());
		}		
    }
    
    // El path se crea desde el padre más profundo en el árbol hasta el elemento raíz del documento
    private Vector getPath(Element _in){
    	Vector pathOut = new Vector(0,1);
    	Element in = _in;
    	while(in.getParent() != null){
    		
    		// Debido a que algunos elementos difieren en el nombre...
    		String parsedName = in.getName();
    		if (parsedName.equals("langstring")) parsedName = "string";
    		pathOut.add(parsedName);
    		in = in.getParent();
    	}    	    	
    	return pathOut;
    }
    
    
    // Check if the metadata referenced by mnE fits to the serchCriterion
    private boolean fitsTo(Element mdE) {
    	for(int i = 0 ; i< searchCriterion.size(); i++) {
    		
    		Element descendant = mdE;
    		Criterion criterion = (Criterion) searchCriterion.get(i);
    		List path = criterion.getPath();
    		for (int j = path.size()-2 ; j >= 0 ; j--) {
    			
    			//System.out.println("descendant: " + descendant.getName() + " ; expected: " + path.get(j).toString());
    			
    			//Si hay el elemento del criterio
				List descendants = descendant.getContent( new ElementFilter(path.get(j).toString()));
       			if (descendants != null && descendants.size() > 0 ) {
   					// Sólo hay uno de cada tipo
    				descendant = (Element) descendants.get(0);
   					//Sólo con el elemento más profundo, o sea, el que supuestamente tendrá el metadato a contrastar
   					if (j==0) {
   						//Si tiene texto asociado que comparar
   						if (descendant.getTextTrim() != null && !descendant.getTextTrim().equals("")) {
							//Si cumple con nuestro caso con método de búsqueda PARAMETERS
							if (!descendant.getTextTrim().equals(criterion.getValue())) return false;
   						} else return false;    					    			
    				}
    			// Si no lo hay pues ya no lo cumple	
    			} else return false; 
    		}    		
    	}
    	// So, fits to criterions
    	return true;
    }
    
    // Check if the metadata referenced by mnE fits to the serchCriterion by Keyword
    private boolean fitsByKeywordTo(Element mdE) {
    	for(int i = 0 ; i< searchCriterion.size(); i++) {
    		
    		Element descendant = mdE;
    		Criterion criterion = (Criterion) searchCriterion.get(i);
    		List path = criterion.getPath();
    		for (int j = path.size()-2 ; j >= 0 ; j--) {
    			
    			//System.out.println("descendant: " + descendant.getName() + " ; expected: " + path.get(j).toString());
    			
    			//Si hay el elemento del criterio
				List descendants = descendant.getContent( new ElementFilter(path.get(j).toString()));
       			if (descendants != null && descendants.size() > 0 ) {
   					// Sólo hay uno de cada tipo
    				descendant = (Element) descendants.get(0);
   					//Sólo con el elemento más profundo, o sea, el que supuestamente tendrá el metadato a contrastar
   					if (j==0) {
   						//Si tiene texto asociado que comparar
   						if (descendant.getTextTrim() != null && !descendant.getTextTrim().equals("")) {
							//Si encuentra la Keyword en alguno de los campos ya devolvemos true en el método de búsqueda KEYWORD
							if ( (descendant.getTextTrim().toLowerCase()).indexOf(criterion.getValue().toLowerCase()) >= 0) return true;
   						}    					    			
    				}
    			}  
    		}    		
    	}
    	return false;
    }
    
    private void fillCriterionFromElement(Element in) {
    	
            // Process node
        if (!in.getTextTrim().equals("")){
        	searchCriterion.add(new Criterion( in.getTextTrim(), getPath(in) ));
        }
    	
            // If there are any children, visit each one
            List childrenElements = in.getContent(new ElementFilter());

            for (int i=0; i<childrenElements.size(); i++) {
                // Get child node
                Element childElement = (Element) childrenElements.get(i);
        
                // Visit child node
                fillCriterionFromElement(childElement);
            }
    }
    
    private void fillCriterionFromElement(String keyW) {
    	
    	Vector path1 = new Vector(0,1); 
    	Vector path2 = new Vector(0,1); 
    	Vector path3 = new Vector(0,1); 
    	Vector path4 = new Vector(0,1); 
    	Vector path5 = new Vector(0,1); 

    	path1.add("string");
    	path1.add("title");
    	path1.add("general");
    	path1.add("lom");
    	searchCriterion.add(new Criterion(keyW.trim(), path1 ));
    	path2.add("string");
    	path2.add("description");
    	path2.add("general");
    	path2.add("lom");
    	searchCriterion.add(new Criterion(keyW.trim(), path2 ));    	
    	path3.add("string");
    	path3.add("keyword");
    	path3.add("general");
    	path3.add("lom");
    	searchCriterion.add(new Criterion(keyW.trim(), path3 ));    	
       	path4.add("string");
    	path4.add("description");
    	path4.add("classification");
    	path4.add("lom");
    	searchCriterion.add(new Criterion(keyW.trim(), path4 ));    	
    	path5.add("string");
    	path5.add("keyword");
    	path5.add("classification");
    	path5.add("lom");
    	searchCriterion.add(new Criterion(keyW.trim(), path5 ));    	
}

    
    
    /**
     * User pressed OK
     */
    protected void finish() {
  
	    Object[] aux = targetFilesList.getSelectedValues();
	    //There are files selected to import
	    if (aux.length > 0) {
	    	
	    	// If metadata files need to be imported
	    	if (EditorPrefs.getInstance().getBooleanValue(EditorPrefs.POOL_INCLUDE_METADATA)) {
	    		filesToOpen = new File[(aux.length)*2];
	    		for(int i=0; i<aux.length; i++){
	    			String mdName = aux[i].toString();
	    			mdName = mdName.substring( 0 , mdName.lastIndexOf(".jar"));
	    			mdName += "_LOMv1.xml";	    			
	    			filesToOpen[i+aux.length] = new File(mdName);
	    		}
	    	}
	    	else filesToOpen = new File[aux.length];	
	    	
	    	for (int i=0; i<aux.length; i++) { 
	    		filesToOpen[i] = new File(aux[i].toString());
	    	}
	    	// Finished
	    	approved = true;
	    	dispose();
	    //No files selected	
	    } else {
		    ErrorDialogBox.showWarning("\n"+Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.7"), 
		            Messages.getString("uk.ac.reload.editor.contentpackaging.resourceview.metadata.CP_ResourcesByMetadata.3"), 
		            null);
	    }	    
    }

    
    public File[] getFilesToOpen() {
    	return filesToOpen;
    }

    /**
     * Dispose and Clean up
     */
    public void dispose() {
        cleanup();
        super.dispose();
    }

    /**
     * Clean up
     */
    public void cleanup() {
        _mdEditorPanel.cleanup();
    }

    public boolean isApproved() {
    	return approved;
    }


    /**
     * Search handler
     */
    private class SEARCHClick extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            search();
        }
    }

    /**
     * OK handler
     */
    private class OKClick extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            finish();
        }
    }

    /**
     * Cancel handler
     */
    private class CancelClick extends AbstractAction {
        public void actionPerformed(ActionEvent e) {
            dispose();
        }
    }

}