/*******************************************************************************
 * Copyright (c) 2008 Wind River Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
//#ifdef exercises
package org.eclipse.cdt.examples.dsf.dataviewer;
//#else
//#package org.eclipse.cdt.examples.dsf.dataviewer.answers;
//#endif

import java.util.Set;

import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;

/**
 * Data viewer based on a table, which reads data using synchronous methods.
 * <p>
 * This viewer implements the {@link IStructuredContentProvider} interface 
 * which is used by the JFace TableViewer class to populate a Table.  This 
 * interface contains one principal methods for reading data {@link #getElements(Object)}, 
 * which synchronously returns an array of elements.  In order to implement this 
 * method using the asynchronous data generator, this provider uses the 
 * {@link Query} object. 
 * </p>
 */
public class SyncDataViewer 
    implements IStructuredContentProvider, IDataGenerator.Listener 
{
    // The viewer and generator that this content provider using.
    final private TableViewer fViewer;
    final private IDataGenerator fDataGenerator;
    
    public SyncDataViewer(TableViewer viewer, IDataGenerator generator) {
        fViewer = viewer;
        fDataGenerator = generator;
        fDataGenerator.addListener(this);
    }
    
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
        // Not used
    }

    
    public Object[] getElements(Object inputElement) {
        
        // Create the query object for reading data count. 
        Query<Integer> countQuery = new Query<Integer>() {
            @Override
            protected void execute(DataRequestMonitor<Integer> rm) {
                fDataGenerator.getCount(rm);
            }
        };
        
        // Submit the query to be executed.  A query implements a runnable
        // interface and it has to be executed in order to do its work.
        ImmediateExecutor.getInstance().execute(countQuery);
        int count = 0;
        
        // Block until the query completes, which will happen when the request
        // monitor of the execute() method is marked done.
        try {
            count = countQuery.get();
        } catch (Exception e) { 
            // InterruptedException and ExecutionException can be thrown here.
            // ExecutionException containing a CoreException will be thrown 
            // if an error status is set to the Query's request monitor.
            return new Object[0]; 
        } 

        // Create the array that will be filled with elements.
        // For each index in the array execute a query to get the element at
        // that index.
        final Object[] elements = new Object[count];

        for (int i = 0; i < count; i++) {
            final int index = i;
            Query<String> valueQuery = new Query<String>() {
                @Override
                protected void execute(DataRequestMonitor<String> rm) {
                    fDataGenerator.getValue(index, rm);
                }
            };
            ImmediateExecutor.getInstance().execute(valueQuery);
            try {
                elements[i] = valueQuery.get();
            } catch (Exception e) { 
                elements[i] = "error";
            } 
        }
        return elements;
    }

    public void dispose() {
        fDataGenerator.removeListener(this);
    }

    public void countChanged() {
        // For any event from the generator, refresh the whole viewer.
        refreshViewer();
    }
    
    public void valuesChanged(Set<Integer> indexes) {
        // For any event from the generator, refresh the whole viewer.
        refreshViewer();
    }
    
    private void refreshViewer() {
        //#ifdef exercises
        // TODO Exercise 5 - Add a call to getElements() to force a deadlock.
        //#else
//#        getElements(null);
        //#endif
        
        // This method may be called on any thread, switch to the display 
        // thread before calling the viewer.
        Display display = fViewer.getControl().getDisplay(); 
        display.asyncExec( new Runnable() {
            public void run() {
                if (!fViewer.getControl().isDisposed()) {
                    fViewer.refresh();
                }
            }
        });
    }
    
    public static void main(String[] args) {
        // Create the shell to hold the viewer.
        Display display = new Display();
        Shell shell = new Shell(display, SWT.SHELL_TRIM);
        shell.setLayout(new GridLayout());
        GridData data = new GridData(GridData.FILL_BOTH);
        shell.setLayoutData(data);
        Font font = new Font(display, "Courier", 10, SWT.NORMAL);

        // Create the table viewer.
        TableViewer tableViewer = new TableViewer(shell, SWT.BORDER);
        tableViewer.getControl().setLayoutData(data);

        // Create the data generator.
        //#ifdef exercises
        // TODO Exercise 5 - Use the DataGeneratorWithExecutor() instead.
        final IDataGenerator generator = new DataGeneratorWithThread();
        //#else
//#        final IDataGenerator generator = new DataGeneratorWithExecutor();     
        //#endif
        
        // Create the content provider which will populate the viewer.
        SyncDataViewer contentProvider = new SyncDataViewer(tableViewer, generator);
        tableViewer.setContentProvider(contentProvider);
        tableViewer.setInput(new Object());

        // Open the shell and service the display dispatch loop until user
        // closes the shell.
        shell.open();
        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }
        
        // The IDataGenerator.shutdown() method is asynchronous, this requires
        // using a query again in order to wait for its completion.
        Query<Object> shutdownQuery = new Query<Object>() {
            @Override
            protected void execute(DataRequestMonitor<Object> rm) {
                generator.shutdown(rm);
            }
        };
        ImmediateExecutor.getInstance().execute(shutdownQuery);
        try {
            shutdownQuery.get();
        } catch (Exception e) {}
        
        // Shut down the display.
        font.dispose();    
        display.dispose();
    }
    
}
