/****************************************************************************
 * ubion.ORS - The Open Report Suite                                        *
 *                                                                          *
 * ------------------------------------------------------------------------ *
 *                                                                          *
 * Subproject: Office Editor UI                                             *
 *                                                                          *
 *                                                                          *
 * The Contents of this file are made available subject to                  *
 * the terms of GNU Lesser General Public License Version 2.1.              *
 *                                                                          * 
 * GNU Lesser General Public License Version 2.1                            *
 * ======================================================================== *
 * Copyright 2003-2005 by IOn AG                                            *
 *                                                                          *
 * This library is free software; you can redistribute it and/or            *
 * modify it under the terms of the GNU Lesser General Public               *
 * License version 2.1, as published by the Free Software Foundation.       *
 *                                                                          *
 * This library 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 library; if not, write to the Free Software      *
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,                    *
 * MA  02111-1307  USA                                                      *
 *                                                                          *
 * Contact us:                                                              *
 *  http://www.ion.ag                                                       *
 *  info@ion.ag                                                             *
 *                                                                          *
 ****************************************************************************/
 
/*
 * Last changes made by $Author: andreas $, $Date: 2006/08/29 10:17:22 $
 */
package ag.ion.bion.workbench.office.editor.ui.editors;

import ag.ion.bion.workbench.office.editor.core.EditorCorePlugin;

import ag.ion.bion.workbench.office.editor.ui.EditorUIPlugin;

import ag.ion.bion.officelayer.application.IOfficeApplication;

import ag.ion.bion.officelayer.desktop.IFrame;

import ag.ion.bion.officelayer.document.IDocument;

import ag.ion.bion.officelayer.event.DocumentAdapter;
import ag.ion.bion.officelayer.event.IDocumentEvent;
import ag.ion.bion.officelayer.event.IDocumentListener;
import ag.ion.bion.officelayer.filter.IFilter;

import ag.ion.noa4e.editor.ui.dialogs.OfficeSaveAsDialog;
import ag.ion.noa4e.ui.NOAUIPlugin;

import org.eclipse.jface.operation.IRunnableWithProgress;

import org.eclipse.jface.window.Window;

import org.eclipse.swt.widgets.Shell;

import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPathEditorInput;

import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.part.EditorPart;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;

import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Abstract advisor for an office editor.
 * 
 * @author Andreas Bröker
 * @author Markus Krüger
 * @version $Revision: 1.17 $
 */
public abstract class AbstractOfficeEditorAdvisor implements IOfficeEditorAdvisor {

  protected IFrame        frame       = null;
  protected IEditorPart   editorPart  = null;
  protected IEditorInput  editorInput = null;
  protected IDocument     document    = null;
  
  protected IEditorAdvisorStateDelegate editorAdvisorStateDelegate  = null;
  protected IOfficeApplication          officeApplication           = null;
  protected IDocumentListener           documentListener            = null;
  
  //----------------------------------------------------------------------------
  /**
   * Listener for loaded OpenOffice.org document.
   * 
   * @author Andreas Bröker
   */
  private class DocumentListener extends DocumentAdapter {
  
    //----------------------------------------------------------------------------
    /**
     * Is called whenever a OnModifyChanged document event occurs.
     * 
     * @param documentEvent document event
     * 
     * @author Andreas Bröker
     */
    public void onModifyChanged(IDocumentEvent documentEvent) {
      if(editorAdvisorStateDelegate != null)
        editorAdvisorStateDelegate.setDirtyState(document.isModified());
    }
    //----------------------------------------------------------------------------
    
  }
  //----------------------------------------------------------------------------

  //----------------------------------------------------------------------------    
  /**
   * The constructor.
   * 
   * @param editorPart editor part used with this advisor.
   * 
   * @author Markus Krüger
   */
  public AbstractOfficeEditorAdvisor(EditorPart editorPart) {
    assert editorPart != null;
    this.editorPart = editorPart;
  }
  //----------------------------------------------------------------------------  
  /**
   * Sets editor advisor state delegate.
   * 
   * @param editorAdvisorStateDelegate editor advisor state delegate to be used
   * 
   * @author Andreas Bröker 
   */
  public void setStateDelegate(IEditorAdvisorStateDelegate editorAdvisorStateDelegate) {
    this.editorAdvisorStateDelegate = editorAdvisorStateDelegate;
  }
  //----------------------------------------------------------------------------  
  /**
   * Saves the document.
   * 
   * @param progressMonitor progress monitor to be used
   * @param isSubTask information whether this operation is only a sub task
   * 
   * @throws CoreException if the document can not be stored
   * 
   * @author Andreas Bröker
   * @author Markus Krüger
   */
  public void doSave(IProgressMonitor progressMonitor, boolean isSubTask) throws CoreException {
    FileOutputStream fileOutputStream = null;
    IEditorInput editorInput = editorPart.getEditorInput();
    try {
      if(isSubTask)
        progressMonitor.subTask(Messages.AbstractOfficeEditorAdvisor_monitor_storing_document);
      else
        progressMonitor.beginTask(Messages.AbstractOfficeEditorAdvisor_monitor_storing_document, IProgressMonitor.UNKNOWN);
      URL documentURL = getURLFromEditorInput(editorInput);
      String fileExtension = null;
      if(!isBaseDocumentURL(documentURL) && editorInput instanceof IFileEditorInput) {
        IPath filePath = ((IFileEditorInput)editorInput).getFile().getLocation();
        fileExtension = filePath.getFileExtension();
        fileOutputStream = new FileOutputStream(filePath.toOSString());
      }
      if(fileOutputStream != null) {
        IFilter usedFilter = null;
        if(fileExtension != null && fileExtension.length() > 0) {
          IFilter[] filter = document.getFilterProvider().getFilters();
          for(int i = 0; i < filter.length; i++) {
            if(filter[i].getFileExtension(document).equalsIgnoreCase(fileExtension)) {
              usedFilter = filter[i];
              break;
            }
          }
        }
        if(usedFilter != null)
          document.getPersistenceService().export(fileOutputStream,usedFilter);
        else
          document.getPersistenceService().store(fileOutputStream);
      }
      else {
        document.getPersistenceService().store();
      }
      
      document.setModified(false);
      
      if(!isSubTask)
        progressMonitor.done();
    }
    catch(Exception exception) {
      progressMonitor.setCanceled(true);
      progressMonitor.done();
      throw new CoreException(new Status(IStatus.ERROR, EditorUIPlugin.PLUGIN_ID, IStatus.OK, exception.getMessage(), exception));
    }
    finally {
      if(fileOutputStream != null) {
        try {
          fileOutputStream.close();
        }
        catch(IOException ioException) {
          //do nothing
        }
      }
    }
    if(editorInput instanceof IFileEditorInput)
      ((IFileEditorInput)editorInput).getFile().refreshLocal(0,null);
  }
  //----------------------------------------------------------------------------  
  /**
   * Saves the document as other document.
   *  
   * @param shell shell to be used
   * 
   * @throws CoreException if the document can not be stored
   * 
   * @author Andreas Bröker
   */
  public void doSaveAs(Shell shell) throws CoreException {    
  	OfficeSaveAsDialog dialog = new OfficeSaveAsDialog(shell, document);
  	IPath path = null;
  	if(editorPart.getEditorInput() instanceof IPathEditorInput)
  		path = ((IPathEditorInput)editorPart.getEditorInput()).getPath();
    if (path != null)
      dialog.setOriginalFile(path);
      
    dialog.create();    
      
    if (dialog.open() == Window.CANCEL) {
      return;
    }
      
    final IPath filePath = dialog.getResult();    
    final IFilter filter = dialog.getFilter();
    final IContainer targetContainer = dialog.getTargetContainer();
    if (filePath == null) 
      return;      
    
    try {
      EditorUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().run(true, false, new IRunnableWithProgress() {
        public void run(IProgressMonitor progressMonitor) throws InvocationTargetException, InterruptedException {
	        try {
	          progressMonitor.beginTask(Messages.AbstractOfficeEditorAdvisor_monitor_storing_document, 100);
	          progressMonitor.worked(20);          
	          document.getPersistenceService().export("file:///" + filePath.toOSString(), filter); //$NON-NLS-1$
	          targetContainer.refreshLocal(1, progressMonitor);
	          progressMonitor.worked(60);
	          progressMonitor.done();
	          
	          editorPart.getEditorSite().getShell().getDisplay().asyncExec(new Runnable() {
	          	public void run() {
	          		IFile file = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(filePath);
	          		try {
	          			IDE.openEditor(EditorUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage(), file);
	          		}
	          		catch(CoreException coreException) {
	          			Platform.getLog(EditorUIPlugin.getDefault().getBundle()).log(coreException.getStatus());
	          		}
	  	          EditorUIPlugin.getDefault().getWorkbench().getActiveWorkbenchWindow().getActivePage().closeEditor(editorPart, false);
	          	}
	          });	          
	        } 
	        catch(Throwable throwable) {     
	          progressMonitor.done();
	          throw new InvocationTargetException(throwable);
	        }
        }
      });
    }
    catch(InterruptedException interruptedException) {
      //do not consume
    }
    catch(InvocationTargetException invocationTargetException) {
      throw new CoreException(new Status(IStatus.ERROR, EditorUIPlugin.PLUGIN_ID, 
          IStatus.ERROR, invocationTargetException.getCause().getMessage(), invocationTargetException.getCause()));
    }
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns information whether the document can saved to
   * another document.
   * 
   * @return information whether the document can saved to
   * another document
   * 
   * @author Andreas Bröker
   * @date 13.07.2006
   */
  public boolean isSaveAsAllowed() {
  	return true;
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns information whether the document is dirty.
   * 
   * @return information whether the document is dirty
   * 
   * @author Andreas Bröker
   */
  public boolean isDocumentDirty() {
    if(document != null)
      return document.isModified();
    return false;
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns document. Returns null if a document is not 
   * available.
   * 
   * @return document or null if a document is not 
   * available
   * 
   * @author Andreas Bröker
   */
  public IDocument getLoadedDocument() {
    return document;
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns the editor part that the advisor uses.
   * 
   * @return the editor part that the advisor uses
   * 
   * @author Markus Krüger
   */
  public IEditorPart getEditorPart() {
    return editorPart;
  }
  //----------------------------------------------------------------------------  
  /**
   * Disposes the advisor.
   * 
   * @author Andreas Bröker
   * @author Markus Krüger
   */
  public void dispose() {
    try {
      if(document != null) {            
        document.close();
      }
    }
    catch(Exception exception) {
      //it seems that the connection to OpenOffice.org is no longer available
    }    
  }
  //----------------------------------------------------------------------------     
  /**
   * Returns the frame, or null, if none was set.
   * 
   * @return the frame, or null, if none was set
   * 
   * @author Markus Krüger
   */
  public IFrame getFrame() {
    return frame;
  }
  //----------------------------------------------------------------------------     
  /**
   * Returns the url of the editor input if possible. Otherwise it returns null.
   * 
   * @param editorInput editor input to be used
   * 
   * @return the url of the editor input, or null
   * 
   * @throws MalformedURLException if url can not be constructed
   * 
   * @author Markus Krüger
   * @date 28.08.2006
   */
  protected URL getURLFromEditorInput(IEditorInput editorInput) throws MalformedURLException{
    URL documentURL = null;
    if(editorInput != null && editorInput instanceof IPathEditorInput) {
      IPathEditorInput pathEditorInput = (IPathEditorInput)editorInput;        
      String url = pathEditorInput.getPath().toString();
      if(System.getProperty("os.name").toLowerCase().indexOf("windows") != -1) { //$NON-NLS-1$ //$NON-NLS-2$
        documentURL = new URL("file://///" + url); //$NON-NLS-1$
      }
      else {
        documentURL = new URL("file:////" + url); //$NON-NLS-1$
      }        
    }  
    return documentURL;
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns if the given URL is for a base document.
   * 
   * @return if the given URL is for a base document
   * 
   * @author Markus Krüger
   * @date 28.08.2006
   */
  protected boolean isBaseDocumentURL(URL documentURL) {
    if(documentURL == null)
      return false;
    String urlString = documentURL.toString();
    return urlString.toUpperCase().endsWith(".odb".toUpperCase());
  }
  //----------------------------------------------------------------------------  
  /**
   * Returns office application. If the office connection is not active it will
   * be activated. Before using the office application the client should check
   * the activation state.
   * 
   * @param shell shell to be used for dialogs
   * 
   * @return active office application
   * 
   * @throws CoreException if the office connection can not be activated
   * 
   * @author Andreas Bröker
   * @author Markus Krüger
   */
  protected IOfficeApplication getOfficeApplication(Shell shell) throws CoreException {
    officeApplication = EditorCorePlugin.getDefault().getManagedLocalOfficeApplication();
    if(!officeApplication.isActive()) {       
      IStatus status = NOAUIPlugin.startLocalOfficeApplication(shell, officeApplication);
      if(status.getSeverity() == IStatus.ERROR)
        throw new CoreException(new Status(IStatus.ERROR, EditorUIPlugin.PLUGIN_ID, IStatus.ERROR, Messages.AbstractOfficeEditorAdvisor_error_message_application_not_available, null));
    }
    return officeApplication;
  }  
  //----------------------------------------------------------------------------  
  /**
   * Sets document.
   * 
   * @param document document to be used
   * 
   * @author Andreas Bröker
   */
  protected void setDocument(IDocument document) {
    this.document = document;
    if(document != null) {
      documentListener = new DocumentListener();
      document.addDocumentListener(documentListener);
    }
  }
  //----------------------------------------------------------------------------  
  /**
   * Sets OpenOffice.org frame.
   * 
   * @param frame OpenOffice.org frame to be used
   * 
   * @author Andreas Bröker
   */
  protected void setFrame(IFrame frame) {
    this.frame = frame;
  }
  //----------------------------------------------------------------------------  
  
}
