/* This code is part of Freenet. It is distributed under the GNU General
 * Public License, version 2 (or at your option any later version). See
 * http://www.gnu.org/ for further details of the GPL. */
package freenet.clients.fcp;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;

import freenet.client.DefaultMIMETypes;
import freenet.node.Node;
import freenet.support.LogThresholdCallback;
import freenet.support.Logger;
import freenet.support.Logger.LogLevel;
import freenet.support.SimpleFieldSet;
import freenet.support.api.BucketFactory;
import freenet.support.api.ManifestElement;
import freenet.support.io.FileBucket;

/**
 * Insert a directory from disk as a manifest.
 * 
 * ClientPutDiskDirMessage
 * < generic fields from ClientPutDirMessage >
 * Filename=<filename>
 * AllowUnreadableFiles=<unless true, any unreadable files cause the whole request to fail>
 * End
 * 
 * FIXME this should use the same code as makeDiskDirManifest does for internal
 * directory inserts.
 */
public class ClientPutDiskDirMessage extends ClientPutDirMessage {

	public final static String NAME = "ClientPutDiskDir";
	
	final File dirname;
	final boolean allowUnreadableFiles;
	final boolean includeHiddenFiles;

        private static volatile boolean logMINOR;
	static {
		Logger.registerLogThresholdCallback(new LogThresholdCallback(){
			@Override
			public void shouldUpdate(){
				logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
			}
		});
	}

	public ClientPutDiskDirMessage(SimpleFieldSet fs) throws MessageInvalidException {
		super(fs);
		allowUnreadableFiles = fs.getBoolean("AllowUnreadableFiles", false);
		includeHiddenFiles = fs.getBoolean("includeHiddenFiles", false);
		String fnam = fs.get("Filename");
		if(fnam == null)
			throw new MessageInvalidException(ProtocolErrorMessage.MISSING_FIELD, "Filename missing", identifier, global);
		dirname = new File(fnam);
	}

	@Override
	public String getName() {
		return NAME;
	}

	@Override
	public void run(FCPConnectionHandler handler, Node node)
			throws MessageInvalidException {
		if(!handler.getServer().getCore().allowUploadFrom(dirname))
			throw new MessageInvalidException(ProtocolErrorMessage.ACCESS_DENIED, "Not allowed to upload from "+dirname, identifier, global);
		// Create a directory listing of Buckets of data, mapped to ManifestElement's.
		// Directories are sub-HashMap's.
		HashMap<String, Object> buckets = makeBucketsByName(dirname, "");
		handler.startClientPutDir(this, buckets, true);
	}

    /**
     * Create a map of String -> Bucket for every file in a directory
     * and its subdirs.
     * @throws MessageInvalidException 
     */
    private HashMap<String, Object> makeBucketsByName(File thisdir, String prefix) throws MessageInvalidException {
    	
    	if(logMINOR)
    		Logger.minor(this, "Listing directory: "+thisdir);
    	
    	HashMap<String, Object> ret = new HashMap<String, Object>();
    	
    	File filelist[] = thisdir.listFiles();
    	if(filelist == null)
    		throw new MessageInvalidException(ProtocolErrorMessage.FILE_NOT_FOUND, "No such directory!", identifier, global);
    	for(int i = 0 ; i < filelist.length ; i++) {
    		if(filelist[i].isHidden() && !includeHiddenFiles) continue;
                //   Skip unreadable files and dirs
		//   Skip files nonexistent (dangling symlinks) - check last
	        if (filelist[i].canRead() && filelist[i].exists()) {
	        	if (filelist[i].isFile()) {
	        		File f = filelist[i];
	        		
	        		FileBucket bucket = new FileBucket(f, true, false, false, false);
	        		
	        		ret.put(f.getName(), new ManifestElement(f.getName(), prefix + f.getName(), bucket, DefaultMIMETypes.guessMIMEType(f.getName(), true), f.length()));
	        	} else if(filelist[i].isDirectory()) {
	        		HashMap<String, Object> subdir = makeBucketsByName(new File(thisdir, filelist[i].getName()), prefix
					        + filelist[i].getName() + '/');
	        		ret.put(filelist[i].getName(), subdir);
	        	} else if(!allowUnreadableFiles) {
	        		throw new MessageInvalidException(ProtocolErrorMessage.FILE_NOT_FOUND, "Not directory and not file: "+filelist[i], identifier, global);
	        	}
	        } else {
	        	if(!allowUnreadableFiles)
	        		throw new MessageInvalidException(ProtocolErrorMessage.FILE_NOT_FOUND, "Not readable or doesn't exist: "+filelist[i], identifier, global);
	        }
    	}
    	return ret;
	}

	@Override
	long dataLength() {
		return 0;
	}

	String getIdentifier() {
		return identifier;
	}

	@Override
	public void readFrom(InputStream is, BucketFactory bf, FCPServer server) throws IOException, MessageInvalidException {
		// Do nothing
	}

	@Override
	protected void writeData(OutputStream os) throws IOException {
		// Do nothing
	}

}
