package org.netbeans.lib.cvsclient.command;

import java.io.*;
import java.util.*;

import org.netbeans.lib.cvsclient.*;
import org.netbeans.lib.cvsclient.admin.*;
import org.netbeans.lib.cvsclient.file.*;
import org.netbeans.lib.cvsclient.request.*;
import org.netbeans.lib.cvsclient.util.*;

/**
 * @author Thomas Singer
 */
public class AbstractRequestFactory {

	// Accessing ==============================================================

	protected final boolean addFileRequests(ICvsFiles cvsFiles, IIgnoreFileFilter ignoreFileFilter, IClientEnvironment clientEnvironment, IRequestSender requestSender) throws IOException, CommandAbortedException {
		final FileRequestsVisitor fileRequestsVisitor = new FileRequestsVisitor(requestSender, ignoreFileFilter, clientEnvironment);
		cvsFiles.visitFilesAndDirectories(fileRequestsVisitor);
		return fileRequestsVisitor.isSwitchBackToLocalPathRequired();
	}

	protected final void addFileAndLocalPathRequests(ICvsFiles cvsFiles, IIgnoreFileFilter ignoreFileFilter, IClientEnvironment clientEnvironment, IRequestSender requestSender) throws IOException, CommandAbortedException {
		if (addFileRequests(cvsFiles, ignoreFileFilter, clientEnvironment, requestSender)) {
			Requests.addLocalPathDirectoryRequest(clientEnvironment.getAdminReader(), clientEnvironment.getCvsFileSystem(), requestSender);
		}
	}

	protected void addRequestForFile(FsFilePath fsFile, Entry entry, boolean fileExists, IIgnoreFileFilter ignoreFileFilter, IClientEnvironment clientEnvironment, IRequestSender requestSender) throws CommandAbortedException, IOException {
		BugLog.assertNotNull(fsFile);
		BugLog.assertNotNull(requestSender);
		BugLog.assertNotNull(clientEnvironment);

		if (entry == null) {
			if (!ignoreFileFilter.shouldBeIgnored(fsFile)) {
				Requests.addQuestionableRequest(fsFile, requestSender);
			}
			return;
		}

		// for deleted added files, don't send anything..
		if (entry.isAdded() && !fileExists) {
			return;
		}

		final Date entryLastModified = entry.getLastModified();
		final boolean hadConflicts = entry.isConflict();
		if (!hadConflicts) {
			// we null out the conflict field if there is no conflict
			// because we use that field to store the timestamp of the
			// file (just like command-line CVS). There is no point
			// in sending this information to the CVS server, even
			// though it should be ignored by the server.
			entry.parseConflictString(null);
		}
		Requests.addEntryRequest(entry, requestSender);

		if (!fileExists || entry.isRemoved()) {
			return;
		}

		if (!hadConflicts && entryLastModified != null) {
			if (!isModified(fsFile, entryLastModified, clientEnvironment)) {
				Requests.addUnchangedRequest(fsFile, requestSender);
				return;
			}
		}

		addModifiedRequest(fsFile, entry, clientEnvironment, requestSender);
	}

	protected boolean isModified(FsFilePath fsFile, Date entryLastModified, IClientEnvironment clientEnvironment) {
		return clientEnvironment.getLocalFileReader().isModified(fsFile, entryLastModified);
	}

	protected void addModifiedRequest(FsFilePath fsFile, Entry entry, IClientEnvironment clientEnvironment, IRequestSender requestSender) throws CommandAbortedException, IOException {
		final FilePermissions permissions = clientEnvironment.getLocalFileReader().getFilePermissions(fsFile);
		Requests.addModifiedRequest(fsFile, entry.isBinary(), permissions, requestSender);
	}

	// Inner Classes ==========================================================

	private class FileRequestsVisitor implements ICvsFilesDirectoriesVisitor {
		private final IRequestSender requestSender;
		private final IIgnoreFileFilter ignoreFileFilter;
		private final IClientEnvironment clientEnvironment;
		private final IAdminReader adminReader;
		private final ICvsFileSystem cvsFileSystem;
		private boolean nonRootPath;
		private boolean anyPath;

		public FileRequestsVisitor(IRequestSender requestSender, IIgnoreFileFilter ignoreFileFilter, IClientEnvironment clientEnvironment) {
			this.requestSender = requestSender;
			this.ignoreFileFilter = ignoreFileFilter;
			this.clientEnvironment = clientEnvironment;
			adminReader = clientEnvironment.getAdminReader();
			cvsFileSystem = clientEnvironment.getCvsFileSystem();
		}

		public void handleFile(FsFilePath fsFile, Entry entry, boolean exists) throws CommandAbortedException, IOException {
			addRequestForFile(fsFile, entry, exists, ignoreFileFilter, clientEnvironment, requestSender);
		}

		public void handleDirectory(FsDirectoryPath fsDirectory) throws IOException, CommandAbortedException {
			Requests.addDirectoryStickyRequests(fsDirectory, adminReader, cvsFileSystem, requestSender);
			nonRootPath |= !FsPathUtils.isRoot(fsDirectory);
			anyPath = true;
		}

		public boolean isSwitchBackToLocalPathRequired() {
			return !anyPath || nonRootPath;
		}
	}
}
