package net.java.webdav.samples;

import static javax.ws.rs.core.HttpHeaders.CONTENT_LENGTH;
import static javax.ws.rs.core.Response.Status.OK;
import static net.java.webdav.Headers.DAV;
import static net.java.webdav.Headers.DEPTH;
import static net.java.webdav.Headers.DESTINATION;
import static net.java.webdav.Headers.OVERWRITE;
import static net.java.webdav.elements.ResourceType.COLLECTION;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Response.ResponseBuilder;
import javax.ws.rs.ext.Providers;

import net.java.webdav.COPY;
import net.java.webdav.MKCOL;
import net.java.webdav.OPTIONS;
import net.java.webdav.PROPFIND;
import net.java.webdav.PROPPATCH;
import net.java.webdav.elements.Comment;
import net.java.webdav.elements.CreationDate;
import net.java.webdav.elements.GetContentLength;
import net.java.webdav.elements.GetContentType;
import net.java.webdav.elements.GetLastModified;
import net.java.webdav.elements.HRef;
import net.java.webdav.elements.MultiStatus;
import net.java.webdav.elements.Prop;
import net.java.webdav.elements.PropStat;
import net.java.webdav.elements.Response;
import net.java.webdav.elements.Status;
import net.java.webdav.samples.elements.SampleProperty;

@Path("/")
public class FileSystem {
	private File davFolder = new File("C:/temp");

	@OPTIONS
	public javax.ws.rs.core.Response options() {
		ResponseBuilder builder = javax.ws.rs.core.Response.noContent();
		builder.header(DAV, "1, 2");
		/*
		 * builder.header("Allow","");
		 * OPTIONS, GET, HEAD, DELETE, PROPPATCH, COPY, MOVE, LOCK, UNLOCK, PROPFIND, PUT
		 */

		builder.header("MS-Author-Via", "DAV");
		
		return builder.build();
	}
	
	@COPY
	@Path("/filesystem")
	public javax.ws.rs.core.Response copy(@Context final UriInfo uriInfo, @HeaderParam(DESTINATION) final String destination,
			@HeaderParam(OVERWRITE) final String overwriteDav) throws URISyntaxException {
		boolean overwrite = overwriteDav.equalsIgnoreCase("T");
		
		
		return javax.ws.rs.core.Response.status(403).build();
	}
	
	@MKCOL
	@Path("/filesystem/{folder}")
	public javax.ws.rs.core.Response mkcol(@PathParam("folder") final String folder) throws URISyntaxException {
		System.out.println("mkcol(..folder..) - "+folder);
		
		String path = davFolder.getPath()+File.separator+folder;
		File requestedFolder = new File(path);
		
		if(requestedFolder.exists()){
			return javax.ws.rs.core.Response.status(405).build();
		}else{
			boolean created = requestedFolder.mkdirs();
			if(created){
				return javax.ws.rs.core.Response.status(201).build();	
			}else{
				return javax.ws.rs.core.Response.status(403).build();
			}
		}
	}
	
	@DELETE
	@Path("/filesystem/{folder}")
	public javax.ws.rs.core.Response delete(@PathParam("folder") final String folder) throws URISyntaxException {
		System.out.println("delete(..folder..) - "+folder);
		
		String path = davFolder.getPath()+File.separator+folder;
		File requestedFolder = new File(path);
		
		if(requestedFolder.exists()){
			boolean deleted = requestedFolder.delete();
			if(deleted){
				return javax.ws.rs.core.Response.status(205).build();	
			}else
				return javax.ws.rs.core.Response.status(405).build();
		}else{
			return javax.ws.rs.core.Response.status(404).build();	
		}
	}
	
	@PROPFIND
	@Path("/filesystem")
	public javax.ws.rs.core.Response propfind(@Context final UriInfo uriInfo,
			@HeaderParam(DEPTH) final int depth) throws URISyntaxException {
		URI uri = uriInfo.getRequestUri();
		System.out.println("propfind(..) " + uri + " depth - "+depth);
		if (depth == 0) {
			final Response folder = new Response(new HRef(uri), null, null,
					null, new PropStat(new Prop(new CreationDate(new Date()),
							new GetLastModified(new Date(davFolder.lastModified())), new Comment("I'm commented"), new SampleProperty("buuh"), COLLECTION),
							new Status(OK)));
			
//			return new MultiStatus(folder);

			return javax.ws.rs.core.Response.ok(new MultiStatus(folder)).build();
		}

		File[] files = davFolder.listFiles();
		List<Response> responses = new ArrayList<Response>();

		for (File file : files) {
			Response davFile;

			if (file.isDirectory()) {
				davFile = new Response(new HRef(URLUTF8Encoder.encode(file
						.getName())), null, null, null, new PropStat(new Prop(
						new CreationDate(new Date()), new GetLastModified(
//								new Date(file.lastModified())), new SupportedLock(new LockEntry(LockScope.SHARED, LockType.WRITE)), new Comment("I'm commented"), new SampleProperty("buuh"), COLLECTION), new Status(OK)));
								new Date(file.lastModified())), new Comment("I'm commented"), new SampleProperty("buuh"), COLLECTION), new Status(OK)));

			} else {
				davFile = new Response(new HRef(URLUTF8Encoder.encode(file
						.getName())), null, null, null, new PropStat(new Prop(
						new CreationDate(new Date()), new GetLastModified(
								new Date(file.lastModified())), new Comment("I'm commented"), new GetContentType("application/octet-stream"), new GetContentLength(file.length()), new SampleProperty("buuh")), new Status(OK)));

			}

			responses.add(davFile);
		}

		MultiStatus st = new MultiStatus(responses
				.toArray(new Response[responses.size()]));
		
		return javax.ws.rs.core.Response.ok(st).build();
	}

	@PROPFIND
	@Path("/filesystem/{folder:.+}")
//	@Path("/filesystem/{folder}")
	public javax.ws.rs.core.Response propfind(@Context final UriInfo uriInfo,
			@HeaderParam(DEPTH) final int depth,
			@PathParam("folder") final String folder)
			throws URISyntaxException {
		URI uri = uriInfo.getRequestUri();
		System.out.println("propfind(..folder..) " + uri + " depth - "+depth);
		System.out.println("propfind(..folder..) - "+folder);
		
		String path = davFolder.getPath()+File.separator+folder;
		File requestedFolder = new File(path);
		
		if(!requestedFolder.exists()){
			return javax.ws.rs.core.Response.status(404).build();
		}
		
		if(requestedFolder != null && (depth == 0 || requestedFolder.isFile())){
			Response davResource;	
			if (requestedFolder.isDirectory()) {
				davResource = new Response(new HRef(URLUTF8Encoder.encode(requestedFolder
						.getName())), null, null, null, new PropStat(new Prop(
						new CreationDate(new Date()), new GetLastModified(
								new Date(requestedFolder.lastModified())), new Comment("I'm commented"), new SampleProperty("buuh"), COLLECTION), new Status(OK)));

			} else {
				davResource = new Response(new HRef(URLUTF8Encoder.encode(requestedFolder
						.getName())), null, null, null, new PropStat(new Prop(
						new CreationDate(new Date()), new GetLastModified(
//								new Date(requestedFolder.lastModified())), new SupportedLock(new LockEntry(LockScope.SHARED, LockType.WRITE)), new Comment("I'm commented"), new GetContentType("application/octet-stream"), new GetContentLength(requestedFolder.length()), new SampleProperty("buuh")), new Status(OK)));
								new Date(requestedFolder.lastModified())), new Comment("I'm commented"), new GetContentType("application/octet-stream"), new GetContentLength(requestedFolder.length()), new SampleProperty("buuh")), new Status(OK)));
			}
			
			MultiStatus st = new MultiStatus(davResource);
			
			return javax.ws.rs.core.Response.ok(st).build();
		}
		
		if(requestedFolder != null && requestedFolder.isDirectory()){
			File[] files = requestedFolder.listFiles();
			List<Response> responses = new ArrayList<Response>();

			for (File file : files) {
				Response davFile;

				if (file.isDirectory()) {
					davFile = new Response(new HRef(URLUTF8Encoder.encode(file
							.getName())), null, null, null, new PropStat(new Prop(
							new CreationDate(new Date()), new GetLastModified(
									new Date(file.lastModified())), new Comment("I'm commented"), new SampleProperty("buuh"), COLLECTION), new Status(OK)));

				} else {
					davFile = new Response(new HRef(URLUTF8Encoder.encode(file
							.getName())), null, null, null, new PropStat(new Prop(
							new CreationDate(new Date()), new GetLastModified(
									new Date(file.lastModified())), new Comment("I'm commented"), new GetContentType("application/octet-stream"), new GetContentLength(file.length()), new SampleProperty("buuh")), new Status(OK)));

				}

				responses.add(davFile);
			}

			MultiStatus st = new MultiStatus(responses
					.toArray(new Response[responses.size()]));
			
			return javax.ws.rs.core.Response.ok(st).build();
		}	
		
		return javax.ws.rs.core.Response.noContent().build();
	}

	@PROPPATCH
	@Path("/filesystem/{filename}")
	public void proppatch() {
		/*
		 * TODO Patch properties in database.
		 */

		System.out.println("PATCHED");
	}

	@GET
	@Produces("application/octet-stream")
	@Path("/filesystem/{filename}")
	public InputStream get(@PathParam("filename") final String filename) {
		System.out.println("GET: " + filename);
		File[] files = davFolder.listFiles();
		File found = null;
		for (File file : files) {
			if (file.getName().equalsIgnoreCase(filename)) {
				found = file;
				break;
			}
		}
		if (found != null) {
			try {
				return new FileInputStream(found);
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}

		return null;
	}
	
//	@GET
//	@Produces("application/octet-stream")
//	@Path("/filesystem/{folder}/{filename}")
//	public InputStream get(@PathParam("folder") final String folder, @PathParam("filename") final String filename) {
//		System.out.println("GET: " + filename);
//		File[] files = davFolder.listFiles();
//		File found = null;
//		for (File file : files) {
//			if (file.getName().equalsIgnoreCase(folder)) {
//				found = file;
//				break;
//			}
//		}
//		
//		files = found.listFiles();
//		found = null;
//		for (File file : files) {
//			if (file.getName().equalsIgnoreCase(filename)) {
//				found = file;
//				break;
//			}
//		}
//		if (found != null) {
//			try {
//				return new FileInputStream(found);
//			} catch (FileNotFoundException e) {
//				e.printStackTrace();
//			}
//		}
//
//		return null;
//	}

	@PUT
	@Consumes("application/octet-stream")
	@Path("/filesystem/{filename}")
	public void put(final InputStream entityStream,
			@HeaderParam(CONTENT_LENGTH) final long contentLength,
			@Context final Providers providers,
			@Context final HttpHeaders httpHeaders, @PathParam("filename") String filename) throws IOException {
		/*
		 * Workaround for Jersey issue #154 (see
		 * https://jersey.dev.java.net/issues/show_bug.cgi?id=154): Jersey will
		 * throw an exception and abstain from calling a method if the method
		 * expects a JAXB element body while the actual Content-Length is zero.
		 */

		if (contentLength == 0)
			return;

		BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(davFolder.getPath()+File.separator+filename));
		
		int b = -1;
		while((b = entityStream.read()) != -1){
			out.write(b);
		}
		out.flush();
		out.close();

		/*
		 * End of #154 workaround
		 */

		/*
		 * TODO Store to database here, using @PathParam as primary key.
		 */

		System.out.println(String.format("STORED: %s", filename));
	}

}