// CloudCoder - a web-based pedagogical programming environment
// Copyright (C) 2011-2012, Jaime Spacco <jspacco@knox.edu>
// Copyright (C) 2011-2012, David H. Hovemeyer <david.hovemeyer@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package org.cloudcoder.builder2.cfunction;

import java.util.Properties;

import org.cloudcoder.app.shared.model.Problem;
import org.cloudcoder.app.shared.model.ProblemType;
import org.cloudcoder.app.shared.model.TestCase;
import org.cloudcoder.builder2.model.BuilderSubmission;
import org.cloudcoder.builder2.model.IBuildStep;
import org.cloudcoder.builder2.model.InternalBuilderException;
import org.cloudcoder.builder2.model.ProgramSource;
import org.cloudcoder.builder2.util.StringUtil;

/**
 * Add test scaffolding to C program source consisting of a single
 * function to add a main function that calls the function
 * for each test case, checks it against the expected
 * return value, and returns an exit value indicating success
 * or failure.  This is used for {@link ProblemType#C_FUNCTION}
 * submissions.  It works by replacing the {@link ProgramSource}
 * submission artifact with the scaffolded version.
 * 
 * @author David Hovemeyer
 * @author Jaime Spacco
 */
public class AddCFunctionScaffoldingBuildStep implements IBuildStep {

	@Override
	public void execute(BuilderSubmission submission, Properties config) {

		ProgramSource[] programSourceList = submission.requireArtifact(this.getClass(), ProgramSource[].class);
		
		if (programSourceList.length != 1) {
			throw new InternalBuilderException(this.getClass(), "C_FUNCTION problems only support a single source file");
		}

		TestCase[] testCaseList = submission.requireArtifact(this.getClass(), TestCase[].class);

		Problem problem = submission.requireArtifact(this.getClass(), Problem.class);

		String programText = programSourceList[0].getProgramText();
		if (!programText.endsWith("\n")) {
			programText = programText + "\n";
		}

		int prologueLength = 3;
		int programTextLength = StringUtil.countLines(programText);

		StringBuilder test = new StringBuilder();
		test.append("#include <string.h>\n");  // 3 lines of prologue
		test.append("#include <stdlib.h>\n");
		test.append("#include <stdio.h>\n");

		// The program text is the user's function
		test.append(programText);
		test.append("\n");

		// The eq macro will test the function's return value against
		// the expected return value.
		test.append("#undef eq\n");
		test.append("#define eq(a,b) ((a) == (b))\n");

		// Generate a main() function which can run all of the test cases.
		// argv[1] specifies the test case to execute by name.
		// argv[2] and argv[3] specify the exit values to use to indicate
		// whether or not the tested function's return value matched the
		// expected value.
		test.append("int main(int argc, char ** argv) {\n");
		test.append("  int rcIfEqual = atoi(argv[2]);\n");
		test.append("  int rcIfNotEqual = atoi(argv[3]);\n");
		// Make it a bit harder to steal the exit codes
		test.append("  argv[2] = 0;\n");
		test.append("  argv[3] = 0;\n");

		// Generate calls to execute test cases.
		for (TestCase t : testCaseList) {
			test.append("  if (strcmp(argv[1], \"" +t.getTestCaseName()+"\")==0) {\n");
			test.append("    return eq("+problem.getTestname()+
					"("+t.getInput()+"), ("+t.getOutput()+")) ? rcIfEqual : rcIfNotEqual;\n");
			test.append("  }\n");
		}

		// We return 99 if an invalid test case was provided: shouldn't
		// happen in practice.
		test.append("  return 99;\n");
		test.append("}\n");
		String result = test.toString();
		System.out.println(result);

		int epilogueLength = StringUtil.countLines(result) - programTextLength - prologueLength;

		// Create new ProgramSource artifact with scaffolded source
		ProgramSource scaffoldedProgramSource = new ProgramSource(result, prologueLength, epilogueLength);
		submission.addArtifact(new ProgramSource[]{scaffoldedProgramSource});
	}

}

@@ -2944,24 +2944,428 @@
 end(%22%5Cn%22);%0A%0A
+%09%09// Add equality predicate.%0A%09%09// If the problem specifies one, use it.%0A%09%09// Otherwise, generate the default equality predicate.%0A%09%09String equalityPredicate = problem.getEqualityPredicate();%0A%09%09if (equalityPredicate == null %7C%7C equalityPredicate.trim().equals(%22%22)) %7B%0A%09%09%09// Generate the default equality predicate (which just%0A%09%09%09// applies the built-in == operator to the expected%0A%09%09%09// and actual values.)%0A%09
 %09%09// The eq 
@@ -3418,16 +3418,17 @@
 ainst%0A%09%09
+%09
 // the e
@@ -3441,32 +3441,33 @@
 d return value.%0A
+%09
 %09%09test.append(%22#
@@ -3474,32 +3474,33 @@
 undef eq%5Cn%22);%0A%09%09
+%09
 test.append(%22#de
@@ -3525,24 +3525,163 @@
 == (b))%5Cn%22);
+%0A%09%09%7D else %7B%0A%09%09%09// The problem defines its own equality predicate.%0A%09%09%09test.append(problem.getEqualityPredicate());%0A%09%09%09test.append(%22%5Cn%22);%0A%09%09%7D
 %0A%0A%09%09// Gener

add support for equality predicate for C_FUNCTION problems