/*
 * Copyright 2006-2019 DLR, Germany
 * 
 * SPDX-License-Identifier: EPL-1.0
 * 
 * http://www.rcenvironment.de/
 */

package de.rcenvironment.components.cpacs.writer.gui.runtime;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.eclipse.core.runtime.Platform;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.part.ViewPart;

import de.rcenvironment.components.cpacs.writer.common.CpacsWriterComponentConstants;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.component.execution.api.ComponentExecutionInformation;
import de.rcenvironment.core.datamanagement.DataManagementService;
import de.rcenvironment.core.gui.workflow.view.ComponentRuntimeView;
import de.rcenvironment.core.notification.DistributedNotificationService;
import de.rcenvironment.core.notification.Notification;
import de.rcenvironment.core.utils.common.TempFileService;
import de.rcenvironment.core.utils.common.TempFileServiceAccess;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
import de.rcenvironment.core.utils.incubator.ServiceRegistry;
import de.rcenvironment.core.utils.incubator.ServiceRegistryAccess;

/**
 * Abstract super class of all runtime views in this bundle.
 * 
 * @author Arne Bachmann
 * @author Markus Kunde
 * @author Sascha Zur
 */
public abstract class AbstractCpacsRuntimeView extends ViewPart implements ComponentRuntimeView {

    protected static final TempFileService TEMP_MANAGER = TempFileServiceAccess.getInstance();

    private static final int MINIMUM_HEIGHT_OF_FILE_CONTENT_TEXT = 300;

    private static File managedTempDir = null;

    /**
     * Component information of this component view.
     */
    protected ComponentExecutionInformation componentInstanceDescriptor = null;

    /**
     * Notification service to get notified on new history objects.
     */
    protected DistributedNotificationService notificationService;

    /**
     * The simplified version for DM reference accesses.
     */
    protected DataManagementService dataManagementService;

    /**
     * The form for the widgets.
     */
    protected ScrolledForm form = null;

    private Text fileContentText;

    private Log log;

    private File cpacsFile = null;

    private Button mappedB;

    @Override
    public void initializeData(ComponentExecutionInformation componentExecutionInformation) {
        componentInstanceDescriptor = componentExecutionInformation;
        if (managedTempDir == null) {
            try {
                managedTempDir = TEMP_MANAGER.createManagedTempDir();
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }
        }
        ServiceRegistryAccess serviceRegistryAccess = ServiceRegistry.createAccessFor(this);
        dataManagementService = serviceRegistryAccess.getService(DataManagementService.class);
        notificationService = serviceRegistryAccess.getService(DistributedNotificationService.class);
        final String tempFileReference = getFileReference();
        if (tempFileReference != null) {
            try {
                cpacsFile = new File(managedTempDir, tempFileReference + ".xml");
                if (!cpacsFile.exists()) {
                    // As the file is disposed when the view is disposed, the file will never exist at that point and must be fetched
                    // anew. As the view won't be re-opened very often and won't be executed an a remote node very often, this behavior
                    // is acceptable for now. Will be changed if temp file clean up on RCE shutdown is improved -seid_do
                    dataManagementService.copyReferenceToLocalFile(
                        tempFileReference, cpacsFile, componentInstanceDescriptor.getStorageNetworkDestination());
                }
            } catch (final IOException | CommunicationException e) {
                log.error("Fetching CPACS file failed", e);
            }
        }
    }

    @Override
    public void initializeView() {
        if (mappedB != null) { // This button only exists on Windows
            mappedB.setEnabled(cpacsFile != null);
        }
        if (cpacsFile != null && cpacsFile.exists()) {
            try {
                fileContentText.setText(FileUtils.readFileToString(cpacsFile, Charset.defaultCharset()));
            } catch (IOException e) {
                log.error(e);
            }
        } else {
            fileContentText.setText("");
        }
    }

    /**
     * To be used by inheriting classes.
     * 
     * @param parent The parent frame
     * @return null or an error
     */
    protected void createPartControl(final Composite parent, final Log logger) {
        final FormToolkit tk = new FormToolkit(parent.getDisplay()); // factory to create fancy form
        this.log = logger;
        // controls and layout
        form = tk.createScrolledForm(parent);
        form.getBody().setLayout(new GridLayout());

        if (Platform.OS_WIN32.equals(Platform.getOS())) {
            final Composite main = tk.createComposite(form.getBody());
            main.setLayout(new FillLayout(SWT.HORIZONTAL));
            mappedB = tk.createButton(main, "Show in TiGL Viewer", SWT.PUSH);
            mappedB.setEnabled(false);
            mappedB.addListener(SWT.Selection, new Listener() {

                @Override
                public void handleEvent(final Event event) {
                    if (cpacsFile != null) {
                        performShowAction(cpacsFile);
                    }
                }
            });
        }
        GridData gridData = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL);
        gridData.heightHint = MINIMUM_HEIGHT_OF_FILE_CONTENT_TEXT;
        fileContentText = tk.createText(form.getBody(), "", SWT.V_SCROLL | SWT.H_SCROLL);
        fileContentText.setEditable(false);
        fileContentText.setLayoutData(gridData);
    }

    /**
     * This needs to be implemented by the specific view/editor.
     * 
     * @param file The file to show
     */
    protected abstract void performShowAction(final File file);

    /**
     * Check if there is a recent history entry notification. This is needed to show the last version of the incoming + mapped CPACS
     * document.
     * 
     * @return The object or null if unavailable
     */
    private String getFileReference() {
        Map<String, List<Notification>> all;
        try {
            all = notificationService.getNotifications(componentInstanceDescriptor.getExecutionIdentifier()
                + CpacsWriterComponentConstants.RUNTIME_CPACS_UUIDS, componentInstanceDescriptor.getNodeId());
            if (all.size() >= 1) {
                final List<Notification> notifications = all.values().iterator().next(); // get list of
                // notifications
                // for first (and
                // only) id in map
                if (notifications.size() < 1) {
                    return null; // no notification in queue
                }
                final Serializable object = notifications.get(0).getBody();
                if (object instanceof String) {
                    return (String) object;
                }
            }
        } catch (RemoteOperationException e) {
            log.error("Getting CPACS file reference failed.");
        }

        return null;
    }

    @Override
    public void dispose() {
        super.dispose();
        if (cpacsFile != null) {
            try {
                TEMP_MANAGER.disposeManagedTempDirOrFile(cpacsFile);
            } catch (IOException e) {
                log.error("Deleting CPACS file in temp directory failed", e);
            }
        }
    }
}
