/*
 * eXist Open Source Native XML Database Copyright (C) 2001-06 Wolfgang M.
 * Meier wolfgang@exist-db.org http://exist.sourceforge.net
 * 
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 * 
 * 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 Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * $Id: DocumentView.java 4163 2006-08-24 14:23:13Z wolfgang_m $
 */
package org.exist.client;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Observable;
import java.util.Observer;
import java.util.Properties;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.border.BevelBorder;
import javax.xml.transform.OutputKeys;

import org.exist.security.User;
import org.exist.storage.ElementIndex;
import org.exist.storage.TextSearchEngine;
import org.exist.util.ProgressIndicator;
import org.exist.xmldb.UserManagementService;
import org.exist.xmldb.XmldbURI;
import org.exist.xquery.util.URIUtils;
import org.xmldb.api.base.Collection;
import org.xmldb.api.base.Resource;
import org.xmldb.api.base.XMLDBException;
import org.xmldb.api.modules.XMLResource;

class DocumentView extends JFrame {
	
	protected InteractiveClient client;
	private	 XmldbURI resourceName;
	protected Resource resource;
	protected Collection collection;
	protected boolean readOnly = false;
	protected ClientTextArea text;
	protected JButton saveButton;
	protected JButton saveAsButton;
	protected JTextField statusMessage;
	protected JProgressBar progress;
	protected JPopupMenu popup;
	protected Properties properties;
	
	public DocumentView(InteractiveClient client, XmldbURI resourceName, Properties properties) throws XMLDBException
	{
		super(Messages.getString("DocumentView.0")); //$NON-NLS-1$
		this.resourceName = resourceName;
		this.resource = client.retrieve(resourceName, properties.getProperty(OutputKeys.INDENT, "yes")); //$NON-NLS-1$
		this.client = client;
		this.collection = client.getCollection();
		this.properties = properties;
		getContentPane().setLayout(new BorderLayout());
		setupComponents();
		addWindowListener(new WindowAdapter() {
			public void windowClosing(WindowEvent ev) {
				close();
			}
		});
		pack();
	}
	
	public void viewDocument()
	{
		try{
			if (resource.getResourceType().equals("XMLResource")) //$NON-NLS-1$
	            setText((String) resource.getContent());
	        else
	            setText(new String((byte[]) resource.getContent()));
	        
	        // lock the resource for editing
	        UserManagementService service = (UserManagementService)
	        client.current.getService("UserManagementService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
	        User user = service.getUser(properties.getProperty("user")); //$NON-NLS-1$
	        String lockOwner = service.hasUserLock(resource);
	        if(lockOwner != null) {
	            if(JOptionPane.showConfirmDialog(this,
	                    Messages.getString("DocumentView.6") + lockOwner + //$NON-NLS-1$
	                    Messages.getString("DocumentView.7"), //$NON-NLS-1$
	                    Messages.getString("DocumentView.8"), //$NON-NLS-1$
	                    JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) {
	                dispose();
	                this.setCursor(Cursor.getDefaultCursor());
	                return;
	            }
	        }
	        
	        try {
	            service.lockResource(resource, user);
	        } catch(XMLDBException ex) {
	            System.out.println(ex.getMessage());
	            JOptionPane.showMessageDialog(this,
	                    Messages.getString("DocumentView.9")); //$NON-NLS-1$
	            setReadOnly();
	        }
	        setVisible(true);
		}
		catch (XMLDBException ex) {
			showErrorMessage(Messages.getString("DocumentView.10") + ex.getMessage(), ex); //$NON-NLS-1$
		}
	}
	
	
	private static void showErrorMessage(String message, Throwable t) {
        JScrollPane scroll = null;
        JTextArea msgArea = new JTextArea(message);
        msgArea.setBorder(BorderFactory.createTitledBorder("Message:")); //$NON-NLS-1$
        msgArea.setEditable(false);
        msgArea.setBackground(null);
        if (t != null) {
            StringWriter out = new StringWriter();
            PrintWriter writer = new PrintWriter(out);
            t.printStackTrace(writer);
            JTextArea stacktrace = new JTextArea(out.toString(), 20, 50);
            stacktrace.setBackground(null);
            stacktrace.setEditable(false);
            scroll = new JScrollPane(stacktrace);
            scroll.setPreferredSize(new Dimension(250, 300));
            scroll.setBorder(BorderFactory
                    .createTitledBorder("Exception Stacktrace:")); //$NON-NLS-1$
        }
        JOptionPane optionPane = new JOptionPane();
        optionPane.setMessage(new Object[]{msgArea, scroll});
        optionPane.setMessageType(JOptionPane.ERROR_MESSAGE);
        JDialog dialog = optionPane.createDialog(null, Messages.getString("DocumentView.13")); //$NON-NLS-1$
        dialog.setResizable(true);
        dialog.pack();
        dialog.setVisible(true);
        return;
    }
	
	public void setReadOnly() {
		text.setEditable(false);
		saveButton.setEnabled(false);
		readOnly = true;
	}
	
	private void close() {
		unlockView();
	}
	
	private void unlockView()
	{
		if (readOnly)
			return;
		try
		{
			UserManagementService service = (UserManagementService) collection
					.getService("UserManagementService", "1.0"); //$NON-NLS-1$ //$NON-NLS-2$
			service.unlockResource(resource);
		}
		catch (XMLDBException e)
		{
			e.printStackTrace();
		}
	}
	
	private void setupComponents() throws XMLDBException {

        /* start of menubar */
        JMenuBar menubar = new JMenuBar();

        JMenu fileMenu = new JMenu(Messages.getString("DocumentView.16")); //$NON-NLS-1$
        fileMenu.setMnemonic(KeyEvent.VK_F);
        menubar.add(fileMenu); 

        JMenuItem item;
        // Save to database
        item = new JMenuItem(Messages.getString("DocumentView.17"), KeyEvent.VK_S); //$NON-NLS-1$
        item.setAccelerator(KeyStroke.getKeyStroke("control S")); //$NON-NLS-1$
        item.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                save();
            }
        });
        fileMenu.add(item);
        /*
        // Refresh
        item = new JMenuItem("Refresh", KeyEvent.VK_R);
        item.setAccelerator(KeyStroke.getKeyStroke("control R"));
        item.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                try {
                    refresh() ;
                } catch (XMLDBException u) {
                    u.printStackTrace();
                }
            }
        });
        fileMenu.add(item);
        */

        setJMenuBar(menubar);
        /* end of menubar */
        
        /* The icon toolbar */

		JToolBar toolbar = new JToolBar();
		
		//Save button
		URL url = getClass().getResource("icons/Save24.gif"); //$NON-NLS-1$
		saveButton = new JButton(new ImageIcon(url));
		saveButton
				.setToolTipText(Messages.getString("DocumentView.20")); //$NON-NLS-1$
		saveButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				save();
			}
		});
		toolbar.add(saveButton);
		
		//Save As button
		url = getClass().getResource("icons/SaveAs24.gif"); //$NON-NLS-1$
		saveAsButton = new JButton(new ImageIcon(url));
		saveAsButton
				.setToolTipText(Messages.getString("DocumentView.22")); //$NON-NLS-1$
		saveAsButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				saveAs();
			}
		});
		toolbar.add(saveAsButton);
		
		//Export button
		url = getClass().getResource("icons/Export24.gif"); //$NON-NLS-1$
		JButton button = new JButton(new ImageIcon(url));
		button.setToolTipText(Messages.getString("DocumentView.24")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e)  {
			try {
				export() ;
			} catch (XMLDBException u) {
				u.printStackTrace();
			}
			}
		});
		toolbar.add(button);
		
		toolbar.addSeparator();
		
		//Copy button
		url = getClass().getResource("icons/Copy24.gif"); //$NON-NLS-1$
		button = new JButton(new ImageIcon(url));
		button.setToolTipText(Messages.getString("DocumentView.26")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				text.copy();
			}
		});
		toolbar.add(button);
		
		//Cut button
		url = getClass().getResource("icons/Cut24.gif"); //$NON-NLS-1$
		button = new JButton(new ImageIcon(url));
		button.setToolTipText(Messages.getString("DocumentView.28")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				text.cut();
			}
		});
		toolbar.add(button);
		
		//Paste button
		url = getClass().getResource("icons/Paste24.gif"); //$NON-NLS-1$
		button = new JButton(new ImageIcon(url));
		button.setToolTipText(Messages.getString("DocumentView.30")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				text.paste();
			}
		});
		toolbar.add(button);
		
		toolbar.addSeparator();
		
		//Refresh button
		url = getClass().getResource("icons/Refresh24.gif"); //$NON-NLS-1$
		button = new JButton(new ImageIcon(url));
		button.setToolTipText(Messages.getString("DocumentView.32")); //$NON-NLS-1$
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e)  {
    			try {
    				refresh() ;
    			} catch (XMLDBException u) {
    				u.printStackTrace();
    			}
			}
		});
		toolbar.add(button);
		
		getContentPane().add(toolbar, BorderLayout.NORTH);
		text = new ClientTextArea(true, Messages.getString("DocumentView.33")); //$NON-NLS-1$
		getContentPane().add(text, BorderLayout.CENTER);
		Box statusbar = Box.createHorizontalBox();
		statusbar.setBorder(BorderFactory
				.createBevelBorder(BevelBorder.LOWERED));
		statusMessage = new JTextField(20);
		statusMessage.setEditable(false);
		statusMessage.setFocusable(false);
		statusMessage.setText(Messages.getString("DocumentView.34") + URIUtils.urlDecodeUtf8(resource.getId()) + Messages.getString("DocumentView.35")); //$NON-NLS-1$ //$NON-NLS-2$
		statusbar.add(statusMessage);
		progress = new JProgressBar();
		progress.setPreferredSize(new Dimension(200, 30));
		progress.setVisible(false);
		statusbar.add(progress);
		getContentPane().add(statusbar, BorderLayout.SOUTH);
	}
	
	private void save() {
		new Thread() {
			public void run() {
				try {
					statusMessage.setText(Messages.getString("DocumentView.36") + URIUtils.urlDecodeUtf8(resource.getId())); //$NON-NLS-1$
					if (collection instanceof Observable)
						((Observable) collection)
								.addObserver(new ProgressObserver());
					progress.setIndeterminate(true);
					progress.setVisible(true);
					resource.setContent(text.getText());
					collection.storeResource(resource);
					if (collection instanceof Observable)
						((Observable) collection).deleteObservers();
				} catch (XMLDBException e) {
					ClientFrame.showErrorMessage(Messages.getString("DocumentView.37") //$NON-NLS-1$
							+ e.getMessage(), e);
				} finally {
					progress.setVisible(false);
				}
			}
		}.start();
	}
	
	private void saveAs()
	{
		new Thread()
		{
			public void run()
			{

				//Get the name to save the resource as
				String nameres = JOptionPane.showInputDialog(null, Messages.getString("DocumentView.38")); //$NON-NLS-1$
				if (nameres != null)
				{
					try
					{
						//Change status message and display a progress dialog
						statusMessage.setText(Messages.getString("DocumentView.39") + nameres); //$NON-NLS-1$
						if (collection instanceof Observable)
							((Observable) collection).addObserver(new ProgressObserver());
						progress.setIndeterminate(true);
						progress.setVisible(true);
					
						//Create a new resource as named, set the content, store the resource
						XMLResource result = null;
						result = (XMLResource) collection.createResource(URIUtils.encodeXmldbUriFor(nameres).toString(), XMLResource.RESOURCE_TYPE);
						result.setContent(text.getText());
						collection.storeResource(result);
						client.reloadCollection();	//reload the client collection
						if (collection instanceof Observable)
							((Observable) collection).deleteObservers();
					}
					catch (XMLDBException e)
					{
						ClientFrame.showErrorMessage(Messages.getString("DocumentView.40") + e.getMessage(), e); //$NON-NLS-1$
					} catch (URISyntaxException e) {
						ClientFrame.showErrorMessage(Messages.getString("DocumentView.41") + e.getMessage(), e); //$NON-NLS-1$
					}
					finally
					{
						//hide the progress dialog
						progress.setVisible(false);
					}
				}
			}
		}.start();
	}
	
	private void export() throws XMLDBException {
		String workDir = properties.getProperty("working-dir", System //$NON-NLS-1$
				.getProperty("user.dir")); //$NON-NLS-1$
		JFileChooser chooser = new JFileChooser(workDir);
		chooser.setMultiSelectionEnabled(false);
		chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
		chooser.setSelectedFile(new File(resource.getId())); 
		if (chooser.showDialog(this, Messages.getString("DocumentView.44")) == JFileChooser.APPROVE_OPTION) { //$NON-NLS-1$
			File file = chooser.getSelectedFile();
			if (file.exists()
					&& JOptionPane.showConfirmDialog(this,
							Messages.getString("DocumentView.45"), Messages.getString("DocumentView.46"), //$NON-NLS-1$ //$NON-NLS-2$
							JOptionPane.YES_NO_OPTION) == JOptionPane.NO_OPTION)
				return;
			try {
				OutputStreamWriter writer = new OutputStreamWriter(
						new FileOutputStream(file), Charset.forName(properties
								.getProperty("encoding"))); //$NON-NLS-1$
				writer.write(text.getText());
				writer.close();
			} catch (IOException e) {
				ClientFrame.showErrorMessage(Messages.getString("DocumentView.48") //$NON-NLS-1$
						+ e.getMessage(), e);
			}
			File selectedDir = chooser.getCurrentDirectory();
			properties
					.setProperty("working-dir", selectedDir.getAbsolutePath()); //$NON-NLS-1$
		}
	}
	
	private void refresh() throws XMLDBException
	{	
		//First unlock the resource
		unlockView();
		
		//Reload the resource
		this.resource = client.retrieve(resourceName, properties.getProperty(OutputKeys.INDENT, "yes")); //$NON-NLS-1$
		
		//View and lock the resource
		viewDocument();
	}
	
	public void setText(String content) throws XMLDBException	{
		text.setText(""); //$NON-NLS-1$
		text.setText(content);
		text.setCaretPosition(0);
		text.scrollToCaret();
		statusMessage.setText(Messages.getString("DocumentView.52") + XmldbURI.create(client.getCollection().getName()).append(resourceName) +Messages.getString("DocumentView.53")+properties.getProperty("uri")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
	}
	
	class ProgressObserver implements Observer {
		int mode = 0;
		public void update(Observable o, Object arg) {
			progress.setIndeterminate(false);
			ProgressIndicator ind = (ProgressIndicator) arg;
			progress.setValue(ind.getPercentage());
			if (o instanceof TextSearchEngine)
				progress.setString("Storing words"); //$NON-NLS-1$
			else if (o instanceof ElementIndex)
				progress.setString("Storing elements"); //$NON-NLS-1$
		}
	}
}
