package migl.lisp.operator;

import static org.junit.Assert.assertEquals;

import java.math.BigInteger;

import org.junit.Before;
import org.junit.Test;

import migl.lisp.LispElement;
import migl.lisp.LispError;
import migl.lisp.LispEval;
import migl.lisp.LispFactory;
import migl.lisp.LispImpl;
import migl.lisp.LispList;
import migl.util.ConsList;
import migl.util.ConsListFactory;

public class ConsOperatorOwnTest {
	
	private LispImpl interpreter;

	@Before
	public void init() {
		interpreter = (LispImpl) LispFactory.makeIntepreter();
	}
	
	@Test
	public void testConsOperator() throws LispError {
		assertEquals("(4 . 8)", interpreter.eval("(cons 4 8)").toString());
		assertEquals("(())", interpreter.eval("(cons () ())").toString());
		assertEquals("(8)", interpreter.eval("(cons 8 ())").toString());
		assertEquals("(() . 8)", interpreter.eval("(cons () 8)").toString());
		assertEquals("(8 4)", interpreter.eval("(cons 8 (cons 4 ()))").toString());
	}
	
	@Test(expected = LispError.class)
	public void testIncorrectNumberArgumentCons() throws LispError {
		interpreter.eval("(cons 8)");
	}
	
	@Test
	public void testCar() throws LispError {
		assertEquals(LispElement.generate(8), interpreter.eval("(car (cons 8 4))"));
		assertEquals(LispElement.generate(8), interpreter.eval("(car (cons 8 (cons 4 ())))"));
		assertEquals(LispElement.generate(LispList.nil()), interpreter.eval("(car (list))"));
	}
	
	@Test
	public void testCarWithSingletonList() throws LispError {
		ConsOperator cons = new ConsOperator();
		LispList list = LispList.nil().prepend(8.4);
		ConsList<Object> cl = ConsListFactory.singleton(list);
		assertEquals(list.get(0), cons.apply(interpreter.getEval(), "car", cl).getValue());
	}
	
	@Test(expected = LispError.class)
	public void testIncorrectNumberArgumentCar() throws LispError {
		interpreter.eval("(car 8 4)");
	}
	
	@Test
	public void testCdr() throws LispError {
		assertEquals(LispElement.generate(4), interpreter.eval("(cdr (cons 8 4))"));
		assertEquals(LispElement.generate(LispList.valueOf("(4)")), interpreter.eval("(cdr (cons 8 (cons 4 ())))"));
		assertEquals(LispElement.generate(LispList.nil()), interpreter.eval("(cdr (list))"));
	}
	
	@Test(expected = LispError.class)
	public void testIncorrectNumberArgumentCdr() throws LispError {
		interpreter.eval("(cdr 8 4)");
	}

	@Test(expected = LispError.class)
	public void testMinMaxOperatorException2() throws LispError {
		LispOperator cons = new ConsOperator();
		ConsList<Object> cl = ConsListFactory.nil();
		cl = cl.prepend(new BigInteger("8"));
		cl = cl.prepend(new BigInteger("4"));
		cons.apply(new LispEval(interpreter), "_", cl);
	}
	
	@Test(expected = LispError.class)
	public void testCarException() throws LispError {
		ConsOperator cons = new ConsOperator();
		ConsList<Object> cl = ConsListFactory.singleton(8.4);
		cons.car(interpreter.getEval(), cl);
	}
	
	@Test(expected = LispError.class)
	public void testCdrException() throws LispError {
		ConsOperator cons = new ConsOperator();
		ConsList<Object> cl = ConsListFactory.singleton(8.4);
		cons.cdr(interpreter.getEval(), cl);
	}

}
