/*
 * Copyright 2011 gitblit.com.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitblit.utils;

import java.io.IOException;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.revwalk.RevCommit;

import com.gitblit.Constants;

/**
 * A diff formatter that outputs standard patch content.
 *
 * @author James Moger
 *
 */
public class PatchFormatter extends DiffFormatter {

	private final OutputStream os;

	private Map<String, PatchTouple> changes = new HashMap<String, PatchTouple>();

	private PatchTouple currentTouple;

	public PatchFormatter(OutputStream os) {
		super(os);
		this.os = os;
	}

	@Override
	public void format(DiffEntry entry) throws IOException {
		currentTouple = new PatchTouple();
		changes.put(entry.getNewPath(), currentTouple);
		super.format(entry);
	}

	@Override
	protected void writeLine(final char prefix, final RawText text, final int cur)
			throws IOException {
		switch (prefix) {
		case '+':
			currentTouple.insertions++;
			break;
		case '-':
			currentTouple.deletions++;
			break;
		}
		super.writeLine(prefix, text, cur);
	}

	public String getPatch(RevCommit commit) {
		StringBuilder patch = new StringBuilder();
		// hard-code the mon sep 17 2001 date string.
		// I have no idea why that is there. it seems to be a constant.
		patch.append("From " + commit.getName() + " Mon Sep 17 00:00:00 2001" + "\n");
		patch.append("From: " + JGitUtils.getDisplayName(commit.getAuthorIdent()) + "\n");
		patch.append("Date: "
				+ (new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z").format(new Date(commit
						.getCommitTime() * 1000L))) + "\n");
		patch.append("Subject: [PATCH] " + commit.getShortMessage() + "\n");
		patch.append('\n');
		patch.append("---");
		int maxPathLen = 0;
		int files = 0;
		int insertions = 0;
		int deletions = 0;
		for (String path : changes.keySet()) {
			if (path.length() > maxPathLen) {
				maxPathLen = path.length();
			}
			PatchTouple touple = changes.get(path);
			files++;
			insertions += touple.insertions;
			deletions += touple.deletions;
		}
		int columns = 60;
		int total = insertions + deletions;
		int unit = total / columns + (total % columns > 0 ? 1 : 0);
		if (unit == 0) {
			unit = 1;
		}
		for (String path : changes.keySet()) {
			PatchTouple touple = changes.get(path);
			patch.append("\n " + StringUtils.rightPad(path, maxPathLen, ' ') + " | "
					+ StringUtils.leftPad("" + touple.total(), 4, ' ') + " "
					+ touple.relativeScale(unit));
		}
		patch.append(MessageFormat.format(
				"\n {0} files changed, {1} insertions(+), {2} deletions(-)\n\n", files, insertions,
				deletions));
		patch.append(os.toString());
		patch.append("\n--\n");
		patch.append(Constants.getGitBlitVersion());
		return patch.toString();
	}

	/**
	 * Class that represents the number of insertions and deletions from a
	 * chunk.
	 */
	private static class PatchTouple {
		int insertions;
		int deletions;

		int total() {
			return insertions + deletions;
		}

		String relativeScale(int unit) {
			int plus = insertions / unit;
			int minus = deletions / unit;
			StringBuilder sb = new StringBuilder();
			for (int i = 0; i < plus; i++) {
				sb.append('+');
			}
			for (int i = 0; i < minus; i++) {
				sb.append('-');
			}
			return sb.toString();
		}
	}
}
