package migl.lisp.operator;

import migl.lisp.LispElement;
import migl.lisp.LispError;
import migl.lisp.LispEval;
import migl.lisp.LispList;
import migl.util.Cons;
import migl.util.ConsList;

public class ConsOperator implements LispOperator {

	@Override
	public LispElement apply(LispEval eval, String operator, ConsList<Object> lisp) throws LispError {
		switch(operator) {
			case "cons":
				return this.cons(eval, lisp);
			case "car":
				return this.car(eval, lisp);
			case "cdr":
				return this.cdr(eval, lisp);
			default:
				throw new LispError(operator + LispError.ERR_UNKNOW);
		}
	}
	
	public LispElement cons(LispEval eval, ConsList<Object> lisp) throws LispError {
		if(lisp.size() != 2) {
			throw new LispError(LispError.ERR_NUM_ARG);
		}
		if(lisp.cdr().car() instanceof ConsList) {
			//Recup + evaluation de la liste
			String listStr = eval.getElement(lisp.cdr().car()).toString();
			LispList list = LispList.valueOf(listStr);
			list.prepend(eval.getElement(lisp.car()).getValue());
			return LispElement.generate(list);
		} else {
			LispElement elt = eval.getElement(lisp.car());
			Cons<LispElement, Object> c = new Cons<>(elt, lisp.cdr().car());
			return LispElement.generate(c);
		}
	}
	
	public LispElement car(LispEval eval, ConsList<Object> lisp) throws LispError {
		if(lisp.size() != 1) {
			throw new LispError(LispError.ERR_NUM_ARG);
		}
		LispElement elt = eval.getElement(lisp.car());
		if(elt.isList()) {
			LispList list = elt.toList();
			if(list.size() < 1) {
				return  LispElement.generate(LispList.nil());
			}
			return LispElement.generate(list.get(0));
		} else if(elt.isCons()) {
			return LispElement.generate(elt.toCons().left());
		} else {
			throw new LispError(elt + LispError.ERR_INVALID);
		}
	}
	
	public LispElement cdr(LispEval eval, ConsList<Object> lisp) throws LispError {
		if(lisp.size() != 1) {
			throw new LispError(LispError.ERR_NUM_ARG);
		}
		LispElement elt = eval.getElement(lisp.car());
		if(elt.isList()) {
			LispList list = elt.toList();
			if(list.size() < 2) {
				return  LispElement.generate(LispList.nil());
			}
			return LispElement.generate(list.getSubList(1));
		} else if(elt.isCons()) {
			return LispElement.generate(elt.toCons().right());
		} else {
			throw new LispError(elt + LispError.ERR_INVALID);
		}
	}

}
