package challengetask.group02.controllers;
import challengetask.group02.controllers.exceptions.*;
import challengetask.group02.fsstructure.Directory;
import challengetask.group02.fsstructure.Entry;
import challengetask.group02.fsstructure.File;
import challengetask.group02.helpers.FSModifyHelper;
import challengetask.group02.helpers.PathResolver;
import net.fusejna.StructStat;
import net.fusejna.types.TypeMode;
import net.tomp2p.dht.PeerDHT;
import net.tomp2p.peers.Number160;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.UUID;
import static challengetask.group02.fsstructure.Entry.TYPE.DIRECTORY;
import static challengetask.group02.fsstructure.Entry.TYPE.FILE;
public class TreeController implements ITreeController {
private final FSModifyHelper helper;
private PeerDHT peer;
public TreeController(PeerDHT peer) {
this.peer = peer;
helper = new FSModifyHelper(peer);
}
//methods to view and traverse the tree
@Override
public Entry resolvePath(String path) throws IOException, ClassNotFoundException, FsException {
return PathResolver.resolvePath(path, helper);
}
@Override
public File getFile(String path) throws ClassNotFoundException, FsException, IOException {
Entry entry = resolvePath(path);
if (entry.getType() == FILE) {
return (File) entry;
} else {
throw new NotAFileException(path);
}
}
@Override
public Directory getDirectory(String path) throws ClassNotFoundException, FsException, IOException {
Entry entry = resolvePath(path);
if (entry.getType() == DIRECTORY) {
return (Directory) entry;
} else {
throw new NotADirectoryException(path);
}
}
@Override
public ArrayList<String> readDir(String path) throws IOException, ClassNotFoundException, FsException {
Directory dir = getDirectory(path);
return new ArrayList<>(dir.getChildren().keySet());
//Hashtable<String, Number160> children = dir.getChildren(FILE);
//children.putAll(dir.getChildren(DIRECTORY));
//return new ArrayList<>(children.keySet());
}
//methods to modify the tree
@Override
public void createDir(String path) throws ClassNotFoundException, FsException, IOException {
Path subPaths = Paths.get(path);
int pathLength = subPaths.getNameCount();
if (pathLength == 0) {
throw new NotADirectoryException("Don't create a root node like that! Path: "+path);
}
Directory parentEntry = getDirectory(subPaths.getParent().toString());
if (parentEntry.getChild(subPaths.getFileName().toString()) != null) {
throw new FileExistsException(path);
}
Number160 newKey = Number160.createHash(UUID.randomUUID().hashCode());
Directory newDir = new Directory(newKey, subPaths.getFileName().toString());
helper.addNewEntry(parentEntry, newDir);
}
@Override
public void createFile(String path) throws ClassNotFoundException, FsException, IOException {
Path subPaths = Paths.get(path);
int pathLength = subPaths.getNameCount();
if (pathLength == 0) {
throw new NoSuchFileOrDirectoryException("Can not create such file");
}
Directory parentEntry = getDirectory(subPaths.getParent().toString());
if (parentEntry.getChild(subPaths.getFileName().toString()) != null) {
throw new FileExistsException(path);
}
Number160 newKey = Number160.createHash(UUID.randomUUID().hashCode());
File newFile = new File (newKey, subPaths.getFileName().toString());
//this is new locking logic, due to fuse constraints we have to associate a file creation with the respective owner
newFile.setDirtyBit(true);
newFile.setModifierPeer(peer.peerID());
helper.addNewEntry(parentEntry, newFile);
}
@Override
public void renameEntry(String from, String to) throws ClassNotFoundException, FsException, IOException {
Path oldPath = Paths.get(from);
Path newPath = Paths.get(to);
Entry entry = resolvePath(from);
String newName = newPath.getFileName().toString();
//if the path is the same, just rename the entry
//if the path is different
//add new link to new parent
//remove link from old parent
if (oldPath.getParent().compareTo(newPath.getParent()) == 0) {
Directory parent = getDirectory(oldPath.getParent().toString());
helper.updateEntryName(parent, entry, newName);
} else {
Directory oldParent = getDirectory(oldPath.getParent().toString());
Directory newParent = getDirectory(newPath.getParent().toString());
helper.moveEntry(newParent, oldParent, entry, newName);
}
}
@Override
public void removeDirectory(String path) throws IOException, ClassNotFoundException, FsException {
Path dirPath = Paths.get(path);
Directory dirEntry = getDirectory(path);
Directory parentEntry = getDirectory(dirPath.getParent().toString());
if (dirEntry.getChildren().isEmpty()) {
helper.removeEntry(parentEntry, dirEntry);
} else {
throw new DirectoryNotEmptyException(path);
}
}
@Override
public void deleteFile(String path) throws ClassNotFoundException, FsException, IOException {
Path dirPath = Paths.get(path);
File file = getFile(path);
Directory parent = getDirectory(dirPath.getParent().toString());
helper.clearAndDeleteFile(file);
helper.removeEntry(parent, file);
}
//used for the locking logic
@Override
public void whenFileClosed(String path) throws ClassNotFoundException, FsException, IOException {
File file = getFile(path);
helper.flushFile(file);
}
@Override
public void updateFileMetaData(String path, final StructStat.StatWrapper stat) throws ClassNotFoundException, FsException, IOException {
Entry entry = resolvePath(path);
if (entry.getType() == Entry.TYPE.DIRECTORY) {
stat.setMode(TypeMode.NodeType.DIRECTORY);
}
if (entry.getType() == Entry.TYPE.FILE) {
stat.setMode(TypeMode.NodeType.FILE);
File file = (File) entry;
stat.setMode(TypeMode.NodeType.FILE).size(entry.getSize());
stat.atime(file.getAtime());
stat.ctime(file.getCtime());
}
}
}