/*
 * blanco Framework
 * Copyright (C) 2004-2006 IGA Tosiki
 * 
 * This library 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.1 of the License, or (at your option) any later version.
 */
package blanco.plugin.filemanager.views;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.FileType;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PlatformUI;

import blanco.plugin.filemanager.views.item.BlancoFileManagerProtocolRootItem;

/**
 * ファイルマネージャのファイル操作に関するプロバイダ。
 * 
 * @author IGA Tosiki
 */
public class BlancoFileManagerProvider implements IStructuredContentProvider,
        ITreeContentProvider {
    /**
     * ファイルも処理対象とするかどうか。
     */
    private boolean fIsProcessFile = true;

    public void setIsProcessFile(final boolean argIsProcessFile) {
        fIsProcessFile = argIsProcessFile;
    }

    public boolean getIsProcessFile() {
        return fIsProcessFile;
    }

    public Object[] getElements(final Object inputElement) {
        final Object[][] resultElements = new Object[1][];
        resultElements[0] = new Object[0];

        try {
            PlatformUI.getWorkbench().getProgressService().busyCursorWhile(
                    new IRunnableWithProgress() {
                        public void run(final IProgressMonitor monitor)
                                throws InvocationTargetException,
                                InterruptedException {
                            monitor.beginTask("Expand directory", 100);
                            monitor.subTask("expand directory: "
                                    + inputElement.toString());
                            monitor.worked(1);

                            if (inputElement instanceof IViewSite) {
                                // ここでルートを作成します。
                                resultElements[0] = new BlancoFileManagerProtocolRootItem()
                                        .getElements(inputElement);
                            } else if (inputElement instanceof IStructuredContentProvider) {
                                resultElements[0] = ((IStructuredContentProvider) inputElement)
                                        .getElements(inputElement);
                            } else if (inputElement instanceof FileObject) {
                                // VFSの場合の処理。
                                final FileObject fileLook = (FileObject) inputElement;
                                try {
                                    if (fileLook.getType() == FileType.FOLDER) {
                                        // フォルダの場合にのみディレクトリ展開します。
                                        final FileObject[] fileChild = fileLook
                                                .getChildren();
                                        if (fileChild != null) {
                                            final ArrayList listDirectory = new ArrayList();
                                            for (int index = 0; index < fileChild.length; index++) {
                                                // TODO monitor.isCanceled() は現状
                                                // チェックしていません。

                                                if (fileChild[index].getType() == FileType.FOLDER) {
                                                    listDirectory
                                                            .add(fileChild[index]);

                                                    monitor
                                                            .subTask("expand directory: "
                                                                    + fileChild[index]
                                                                            .toString());
                                                    monitor.worked(1);
                                                } else if (fIsProcessFile
                                                        && fileChild[index]
                                                                .getType() == FileType.FILE) {
                                                    listDirectory
                                                            .add(fileChild[index]);

                                                    monitor
                                                            .subTask("expand file: "
                                                                    + fileChild[index]
                                                                            .toString());
                                                    monitor.worked(1);
                                                }
                                            }

                                            resultElements[0] = listDirectory
                                                    .toArray(new FileObject[listDirectory
                                                            .size()]);
                                        }
                                    }
                                } catch (FileSystemException e) {
                                    e.printStackTrace();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                } catch (Error e) {
                                    e.printStackTrace();
                                }
                            } else {
                                System.out
                                        .println("トレース: 処理されていない getElements(): "
                                                + inputElement.toString()
                                                + ": "
                                                + inputElement.getClass()
                                                        .toString());
                            }

                            monitor.done();
                        }
                    });
        } catch (InvocationTargetException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        } catch (InterruptedException e) {
            // TODO 自動生成された catch ブロック
            e.printStackTrace();
        }
        return resultElements[0];
    }

    public void dispose() {
    }

    public void inputChanged(final Viewer viewer, final Object oldInput,
            final Object newInput) {
    }

    // ITreeContentProvider

    public Object[] getChildren(final Object parentElement) {
        return getElements(parentElement);
    }

    public Object getParent(final Object element) {
        if (element instanceof FileObject) {
            final FileObject fileLook = (FileObject) element;
            try {
                return fileLook.getParent();
            } catch (FileSystemException e) {
                e.printStackTrace();
            }
        }

        return null;
    }

    public boolean hasChildren(final Object element) {
        if (element instanceof FileObject) {
            final FileObject fileObject = (FileObject) element;
            // VFSの場合、ディレクトリかどうかの判断を持って子ノードの有無を判断します。
            // これは速度アップのために必要なことです。正確に実施するにはディレクトリの場合に子ノードを展開して
            // その数をもって子ノード有無判定を示せばよいのですが、これだと ftpなどのデバイスに対して
            // 子ノードを展開して内容を把握するような処理をおこなうと非常に低速に動作してしまいます。これを避けるため、ディレクトリかファイルかだけの判断を利用しています。
            try {
                if (fileObject.getType() == FileType.FOLDER) {
                    return true;
                } else if (fileObject.getType() == FileType.FILE) {
                    return false;
                }
            } catch (FileSystemException e) {
                e.printStackTrace();
            }

            return false;
        }

        if (element instanceof IStructuredContentProvider) {
            // IStructuredContentProvider の場合、子ノードは存在するものと仮定します。
            return true;
        }

        return false;
    }
}
