/*******************************************************************************
 * Copyright (c) 2013, 2016 IBM Corporation 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:
 *     IBM Corporation - initial API and implementation
 *     Chris McGee (IBM) - Bug 380325 - Release filesystem fragment providing Java 7 NIO2 support
 *     Sergey Prigogin (Google) - ongoing development
 *******************************************************************************/
package org.eclipse.core.internal.filesystem.local.nio;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.*;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileInfo;
import org.eclipse.core.filesystem.provider.FileInfo;
import org.eclipse.core.internal.filesystem.local.NativeHandler;

/**
 * NativeHandler for POSIX using only Java 7 API's. It can be used for any
 * file system supporting POSIX family of standards.
 */
public class PosixHandler extends NativeHandler {
	private static final int ATTRIBUTES = EFS.ATTRIBUTE_SYMLINK | EFS.ATTRIBUTE_LINK_TARGET // symbolic link support
			| EFS.ATTRIBUTE_READ_ONLY | EFS.ATTRIBUTE_EXECUTABLE // mapped to owner read and owner execute via FileInfo implementation
			| EFS.ATTRIBUTE_OWNER_READ | EFS.ATTRIBUTE_OWNER_WRITE | EFS.ATTRIBUTE_OWNER_EXECUTE // owner
			| EFS.ATTRIBUTE_GROUP_READ | EFS.ATTRIBUTE_GROUP_WRITE | EFS.ATTRIBUTE_GROUP_EXECUTE // group
			| EFS.ATTRIBUTE_OTHER_READ | EFS.ATTRIBUTE_OTHER_WRITE | EFS.ATTRIBUTE_OTHER_EXECUTE; // other

	@Override
	public FileInfo fetchFileInfo(String fileName) {
		Path path = Paths.get(fileName);
		FileInfo info = new FileInfo();

		// Fill in the name of the file.
		// If the file system is case insensitive, we don't know the real name of the file.
		// Since obtaining the real name in such situation is pretty expensive, we use the name
		// passed as a parameter, which may differ by case from the real name of the file
		// if the file system is case insensitive.
		Path fileNamePath = path.getFileName();
		info.setName(fileNamePath == null ? "" : fileNamePath.toString()); //$NON-NLS-1$

		try {
			PosixFileAttributes attrs = Files.readAttributes(path, PosixFileAttributes.class, LinkOption.NOFOLLOW_LINKS);

			if (attrs.isSymbolicLink()) {
				info.setAttribute(EFS.ATTRIBUTE_SYMLINK, true);
				info.setStringAttribute(EFS.ATTRIBUTE_LINK_TARGET, Files.readSymbolicLink(path).toString());
				attrs = Files.readAttributes(path, PosixFileAttributes.class);
			}

			info.setExists(true);
			info.setLastModified(attrs.lastModifiedTime().toMillis());
			info.setLength(attrs.size());
			info.setDirectory(attrs.isDirectory());

			Set<PosixFilePermission> perms = attrs.permissions();
			info.setAttribute(EFS.ATTRIBUTE_OWNER_READ, perms.contains(PosixFilePermission.OWNER_READ));
			info.setAttribute(EFS.ATTRIBUTE_OWNER_WRITE, perms.contains(PosixFilePermission.OWNER_WRITE));
			info.setAttribute(EFS.ATTRIBUTE_OWNER_EXECUTE, perms.contains(PosixFilePermission.OWNER_EXECUTE));
			info.setAttribute(EFS.ATTRIBUTE_GROUP_READ, perms.contains(PosixFilePermission.GROUP_READ));
			info.setAttribute(EFS.ATTRIBUTE_GROUP_WRITE, perms.contains(PosixFilePermission.GROUP_WRITE));
			info.setAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE, perms.contains(PosixFilePermission.GROUP_EXECUTE));
			info.setAttribute(EFS.ATTRIBUTE_OTHER_READ, perms.contains(PosixFilePermission.OTHERS_READ));
			info.setAttribute(EFS.ATTRIBUTE_OTHER_WRITE, perms.contains(PosixFilePermission.OTHERS_WRITE));
			info.setAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE, perms.contains(PosixFilePermission.OTHERS_EXECUTE));
		} catch (NoSuchFileException e) {
			// A non-existing file is not considered an error.
		} catch (IOException e) {
			// Leave alone and continue.
			info.setError(IFileInfo.IO_ERROR);
		}
		return info;
	}

	@Override
	public int getSupportedAttributes() {
		return ATTRIBUTES;
	}

	@Override
	public boolean putFileInfo(String fileName, IFileInfo info, int options) {
		Path path = Paths.get(fileName);
		Set<PosixFilePermission> perms = new HashSet<>();

		if (info.getAttribute(EFS.ATTRIBUTE_OWNER_READ))
			perms.add(PosixFilePermission.OWNER_READ);
		if (info.getAttribute(EFS.ATTRIBUTE_OWNER_WRITE))
			perms.add(PosixFilePermission.OWNER_WRITE);
		if (info.getAttribute(EFS.ATTRIBUTE_OWNER_EXECUTE))
			perms.add(PosixFilePermission.OWNER_EXECUTE);
		if (info.getAttribute(EFS.ATTRIBUTE_GROUP_READ))
			perms.add(PosixFilePermission.GROUP_READ);
		if (info.getAttribute(EFS.ATTRIBUTE_GROUP_WRITE))
			perms.add(PosixFilePermission.GROUP_WRITE);
		if (info.getAttribute(EFS.ATTRIBUTE_GROUP_EXECUTE))
			perms.add(PosixFilePermission.GROUP_EXECUTE);
		if (info.getAttribute(EFS.ATTRIBUTE_OTHER_READ))
			perms.add(PosixFilePermission.OTHERS_READ);
		if (info.getAttribute(EFS.ATTRIBUTE_OTHER_WRITE))
			perms.add(PosixFilePermission.OTHERS_WRITE);
		if (info.getAttribute(EFS.ATTRIBUTE_OTHER_EXECUTE))
			perms.add(PosixFilePermission.OTHERS_EXECUTE);

		PosixFileAttributeView view = Files.getFileAttributeView(path, PosixFileAttributeView.class);
		try {
			view.setPermissions(perms);
		} catch (IOException e) {
			return false;
		}
		return true;
	}
}
