public SyncJob prepare(final SyncSet syncSet) {

		log.info("Checking directories");
		if (!Files.isDirectory(syncSet.getSourcePath())) {
			throw new IllegalStateException("Source directory does not exist: " + syncSet.getSourceDir());
		}
		if (!Files.isDirectory(syncSet.getTargetPath())) {
			throw new IllegalStateException("Target directory does not exist: " + syncSet.getTargetDir());
		}

		log.info("Preparing synchronization of " + syncSet.getSourceDir() + " to " + syncSet.getTargetDir());
		if (syncSet.isDryRun()) {
			log.info("Dry run is active");
		}

		Stats stats = new Stats();
		SyncJob synchJob = new SyncJob(syncSet);

		if (syncSet.hasOption(Option.COPY_TO_TARGET)) {
			Visitor visitor = new Visitor(new SourceRunDelegate(syncSet, synchJob, stats));
			try {
				log.info("Scanning source tree for files to copy");
				Files.walkFileTree(syncSet.getSourcePath(), visitor);
				log.info("Finished scanning source tree");
			} catch (IOException e) {
				log.error("Error while walking source tree", e);
			}
		}

		if (syncSet.hasOption(Option.DELETE_FROM_TARGET)) {
			Visitor visitor = new Visitor(new TargetRunDelegate(syncSet, synchJob, stats));
			try {
				log.info("Scanning target tree for files to delete");
				Files.walkFileTree(syncSet.getTargetPath(), visitor);
				log.info("Finished scanning target tree");
			} catch (IOException e) {
				log.error("Error while walking target tree", e);
			}
		}

		log.info("Finished preparation");
		stats.report();

		return synchJob;
	}