package at.ac.tuwien.kr.alpha.commons.programs;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import at.ac.tuwien.kr.alpha.api.programs.ASPCore2Program;
import at.ac.tuwien.kr.alpha.api.programs.InlineDirectives;
import at.ac.tuwien.kr.alpha.api.programs.NormalProgram;
import at.ac.tuwien.kr.alpha.api.programs.atoms.Atom;
import at.ac.tuwien.kr.alpha.api.programs.rules.NormalRule;
import at.ac.tuwien.kr.alpha.api.programs.rules.Rule;
import at.ac.tuwien.kr.alpha.api.programs.rules.heads.Head;
import at.ac.tuwien.kr.alpha.api.programs.tests.TestCase;
import at.ac.tuwien.kr.alpha.commons.programs.rules.Rules;

public final class Programs {
	
	private Programs() {
		throw new AssertionError("Cannot instantiate utility class!");
	}

	public static ASPCore2Program emptyProgram() {
		return ASPCore2ProgramImpl.EMPTY;
	}

	public static ASPCore2Program newASPCore2Program(List<Rule<Head>> rules, List<Atom> facts, InlineDirectives inlineDirectives, List<TestCase> testCases) {
		return new ASPCore2ProgramImpl(rules, facts, inlineDirectives, testCases);
	}

	public static ASPCore2Program newASPCore2Program(List<Rule<Head>> rules, List<Atom> facts, InlineDirectives inlineDirectives) {
		return new ASPCore2ProgramImpl(rules, facts, inlineDirectives, Collections.emptyList());
	}

	public static ASPCore2ProgramBuilder builder() {
		return new ASPCore2ProgramBuilder();
	}

	public static ASPCore2ProgramBuilder builder(ASPCore2Program program) {
		return new ASPCore2ProgramBuilder(program);
	}

	public static NormalProgram newNormalProgram(List<NormalRule> rules, List<Atom> facts, InlineDirectives inlineDirectives) {
		return new NormalProgramImpl(rules, facts, inlineDirectives);
	}

	public static NormalProgram toNormalProgram(ASPCore2Program inputProgram) {
		List<NormalRule> normalRules = new ArrayList<>();
		for (Rule<Head> r : inputProgram.getRules()) {
			normalRules.add(Rules.toNormalRule(r));
		}
		return new NormalProgramImpl(normalRules, inputProgram.getFacts(), inputProgram.getInlineDirectives());
	}

	public static InlineDirectives newInlineDirectives() {
		return new InlineDirectivesImpl();
	}

	/**
	 * Builder for more complex program construction scenarios, ensuring that an {@link AspCore2ProgramImpl} is immutable
	 */
	public static class ASPCore2ProgramBuilder {

		private List<Rule<Head>> rules = new ArrayList<>();
		private List<Atom> facts = new ArrayList<>();
		private InlineDirectives inlineDirectives = new InlineDirectivesImpl();

		private List<TestCase> testCases = new ArrayList<>();

		public ASPCore2ProgramBuilder(ASPCore2Program prog) {
			this.addRules(prog.getRules());
			this.addFacts(prog.getFacts());
			this.addInlineDirectives(prog.getInlineDirectives());
			this.addTestCases(prog.getTestCases());
		}

		public ASPCore2ProgramBuilder() {

		}

		public ASPCore2ProgramBuilder addRules(List<Rule<Head>> rules) {
			this.rules.addAll(rules);
			return this;
		}

		public ASPCore2ProgramBuilder addRule(Rule<Head> r) {
			this.rules.add(r);
			return this;
		}

		public ASPCore2ProgramBuilder addFacts(List<Atom> facts) {
			this.facts.addAll(facts);
			return this;
		}

		public ASPCore2ProgramBuilder addFact(Atom fact) {
			this.facts.add(fact);
			return this;
		}

		public ASPCore2ProgramBuilder addInlineDirectives(InlineDirectives inlineDirectives) {
			this.inlineDirectives.accumulate(inlineDirectives);
			return this;
		}

		public ASPCore2ProgramBuilder addTestCase(TestCase testCase) {
			this.testCases.add(testCase);
			return this;
		}

		public ASPCore2ProgramBuilder addTestCases(List<TestCase> testCases) {
			this.testCases.addAll(testCases);
			return this;
		}

		public ASPCore2ProgramBuilder accumulate(ASPCore2Program prog) {
			return this.addRules(prog.getRules()).addFacts(prog.getFacts()).addInlineDirectives(prog.getInlineDirectives()).addTestCases(prog.getTestCases());
		}

		public ASPCore2Program build() {
			return Programs.newASPCore2Program(this.rules, this.facts, this.inlineDirectives, this.testCases);
		}

	}

}
