/*
 * This file is part of muCommander, http://www.mucommander.com
 *
 * muCommander is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * muCommander 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.mucommander.desktop.windows;

import com.mucommander.commons.file.AbstractFile;
import com.mucommander.commons.file.protocol.local.LocalFile;
import com.mucommander.commons.file.protocol.local.SpecialWindowsLocation;
import com.mucommander.commons.file.util.Shell32;
import com.mucommander.commons.file.util.Shell32API;
import com.mucommander.desktop.DesktopManager;
import com.mucommander.desktop.QueuedTrash;

import java.io.IOException;
import java.util.List;

/**
 * WindowsTrash is an <code>AbstractTrash</code> implementation for the <i>Microsoft Windows' Recycle Bin</i>.
 *
 * <p>Native methods in the Shell32 Windows API are used to access the Recycle Bin. There is an overhead associated with
 * invoking those methods (via JNA), so for performance reasons, this trash is implemented as a {@link com.mucommander.desktop.QueuedTrash}
 * in order to group calls to {@link #moveToTrash(com.mucommander.commons.file.AbstractFile)}.</p>
 *
 * @see WindowsTrashProvider
 * @author Maxence Bernard
 */
public class WindowsTrash extends QueuedTrash {

    //////////////////////////////////
    // AbstractTrash implementation //
    //////////////////////////////////

    /**
     * Implementation notes: returns <code>true</code> only for local files that are not archive entries.
     */
    @Override
    public boolean canMoveToTrash(AbstractFile file) {
        return file.getTopAncestor() instanceof LocalFile;
    }

    /**
     * Implementation notes: always returns <code>true</code>: {@link #empty()} is implemented.
     */
    @Override
    public boolean canEmpty() {
        return true;
    }

    @Override
    public boolean empty() {
        return Shell32.isAvailable() && Shell32.getInstance().SHEmptyRecycleBin(null, null, Shell32API.SHERB_NOCONFIRMATION) == 0;
    }

    /**
     * Implementation notes: always returns <code>false</code>.
     */
    @Override
    public boolean isTrashFile(AbstractFile file) {
        // Quote from http://en.wikipedia.org/wiki/Recycle_Bin_(Windows):
        // "The actual location of the Recycle Bin varies depending on the operating system and filesystem. On the older
        // FAT filesystems (typically Windows 98 and prior), it is located in Drive:\RECYCLED. In the NTFS filesystem
        // (Windows 2000, XP, NT) it can be found in Drive:\RECYCLER, with the exception of Windows Vista which stores
        // it in the Drive:\$Recycle.Bin folder."

        // => for the test to be accurate, we'd have to go thru the trouble of testing the kind of filesystem
        // (FAT or NTFS) and the Windows version. It's a lot of work for little added value.

        return false;
    }

    /**
     * Implementation notes: returns the number of items for all Recycle Bins on all drives. This information is not
     * available on certain versions of Windows such as <i>Windows 2000</i>.
     */
    @Override
    public int getItemCount() {
        if(!Shell32.isAvailable())
            return -1;

        Shell32API.SHQUERYRBINFO queryRbInfo = new Shell32API.SHQUERYRBINFO();

        // pszRootPath is null to retrieve the information for all Recycle Bins on all drives. Microsoft's documentation
        // states that this fails on certain versions of Windows such as Windows 2000. If it does, we simply return -1.

        int ret = Shell32.getInstance().SHQueryRecycleBin(
            null,
            queryRbInfo
        );

        return ret==0?(int)queryRbInfo.i64NumItems:-1;
    }

    /**
     * Implementation notes: always returns <code>true</code>: {@link #open()} is implemented.
     */
    @Override
    public boolean canOpen() {
        return true;
    }

    @Override
    public void open() {
        try {DesktopManager.openInFileManager(SpecialWindowsLocation.RECYCLE_BIN);}
        catch(IOException e) {/* TODO: report error. */}
    }


    ////////////////////////////////
    // QueuedTrash implementation //
    ////////////////////////////////

    @Override
    protected boolean moveToTrash(List<AbstractFile> queuedFiles) {
        if(!Shell32.isAvailable())
            return false;

        Shell32API.SHFILEOPSTRUCT fileop = new Shell32API.SHFILEOPSTRUCT();

        fileop.wFunc = Shell32API.FO_DELETE;
        fileop.fFlags = Shell32API.FOF_ALLOWUNDO| Shell32API.FOF_NOCONFIRMATION| Shell32API.FOF_SILENT;

        int nbFiles = queuedFiles.size();

        String[] paths = new String[nbFiles];
        for(int i=0; i<nbFiles; i++) {
            // Directories (and regular files) must not end with a trailing slash or the operation will fail.
            paths[i] = queuedFiles.get(i).getAbsolutePath(false);
        }

        // The encodePaths method takes care of encoding the paths in a special way.
        fileop.pFrom = fileop.encodePaths(paths);

        return Shell32.getInstance().SHFileOperation(fileop) == 0;
    }
}
