// Copyright (c) 2002  Hitoshi Guutara Maruyama.
// This is free software;  for terms and warranty disclaimer see ./COPYING.

package jp.sourceforge.glj.lisp;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;

import gnu.expr.Language;
import kawa.standard.Scheme;
import gnu.kawa.lispexpr.LispReader;
import gnu.mapping.InPort;
import gnu.text.SyntaxException;
import gnu.lists.LList;
import gnu.lists.Pair;

public class Lisp {

  public static final Object	nil = (Object)LList.Empty;
  
  public static LList read(Reader reader)
    throws IOException, SyntaxException {
    if (reader == null) {
      return (LList)nil;
    }
    InPort	port = new InPort(reader);
    return read(port);
  }

  public static LList read(InputStream stream)
    throws IOException, SyntaxException {
    if (stream == null) {
      return (LList)nil;
    }
    InPort	port = new InPort(stream);
    return read(port);
  }
  
  static LList read(InPort port) throws IOException, SyntaxException {
    Object	obj = null;
    Language.setDefaultLanguage(new Scheme());
    LispReader	lispReader = new LispReader(port);
    obj = lispReader.readObject();
    return (LList)obj;
  }

  public static boolean	isAtom(Object obj) {
    return !(obj instanceof Pair);
  }
  
  public static boolean isList(Object obj) {
    return (obj instanceof LList);
  }

  public static boolean isNil(Object obj) {
    return (obj instanceof LList && ((LList)obj).isEmpty());
  }
  
  public static boolean isPair(Object obj) {
    return (isList(obj) && !isNil(obj));
  }
  
  public static Object	car(Object list) {
    if (list instanceof Pair) {
      return ((Pair)list).car;
    }
    else {
      return nil;
    }
  }

  public static Object	cdr(Object list) {
    if (list instanceof Pair) {
      return ((Pair)list).cdr;
    }
    else {
      return nil;
    }
  }
  
  public static Pair	cons(Object car, Object cdr) {
    return new Pair(car, cdr);
  }
  
  public static Pair	list(Object car, Object cadr) {
    return new Pair(car, new Pair(cadr, LList.Empty));
  }

  public static LList	remove(Object obj, LList list) {
    if (list.isEmpty()) {
      return list;
    }
    if (obj.equals(((Pair)list).car)) {
      return remove(obj, (LList)cdr(list));
    }
    return new Pair(car(list), remove(obj, (LList)cdr(list)));
  }

  public static LList	merge(LList list1, LList list2) {
    if (list1.isEmpty()) {
      return list2;
    }
    return merge((LList)cdr(list1), insert(car(list1), list2));
  }

  public static LList	insert(Object obj, LList list) {
    if (list.contains(obj)) {
      return list;
    }
    return ((LList)(new Pair(obj, list)));
  }

  public static LList	append(LList list1, LList list2) {
    if (list1.isEmpty()) {
      return list2;
    }
    if (list2.isEmpty()) {
      return list1;
    }
    LList	revList = reverse(list1);
    for (int i = 0; i < list2.size(); i++) {
      revList = new Pair(list2.get(i), revList);
    }
    return reverse(revList);
  }

  public static LList	reverse(LList list) {
    if (list.isEmpty()) {
      return list;
    }
    LList	retList = LList.Empty;
    for (int i = 0, n = list.size(); i < n; i++) {
      retList = new Pair(list.get(i), retList);
    }
    return retList;
  }

  public static LList	assoc(Object obj, LList list) {
    if (list == null || list.isEmpty()) {
      return (LList)nil;
    }
    for (int i = 0; i < ((Pair)list).size(); i++) {
      Object	part = list.get(i);
      if (!(part instanceof Pair)) {
	return (LList)nil;
      }
      if (obj.equals(car(part))) {
	return ((LList)part);
      }
    }
    return (LList)nil;
  }

  public static LList	member(Object obj, LList list) {
    if (list == null || list.isEmpty()) {
      return (LList)nil;
    }
    if (car(list).equals(obj)) {
      return list;
    }
    for (list = (LList)cdr(list);
	 list instanceof Pair;
	 list = (LList)cdr(list)) {
      if (car(list).equals(obj)) {
	return list;
      }
    }
    return (LList)nil;
  }

  public static LList	subst(Object newObj, Object oldObj, LList list) {
    if (list.isEmpty()) {
      return list;
    }
    LList	retList = LList.Empty;
    for (int i = 0; i < list.size(); i++) {
      if (((Pair)list).get(i) instanceof LList) {
	retList = append(retList,
			 new Pair(subst(newObj, oldObj,
					(LList)((Pair)list).get(i)),
				  LList.Empty));
      }
      else if (oldObj.equals(((Pair)list).get(i))) {
	retList = append(retList, new Pair(newObj, LList.Empty));
      }
      else {
	retList = append(retList, new Pair(((Pair)list).get(i), LList.Empty));
      }
    }
    return retList;
  }
  
  public static LList	lastPair(LList list) {
    if (list.isEmpty()) {
      return list;
    }
    if (isPair(cdr(list))) {
      return lastPair((LList)cdr(list));
    }
    return list;
  }
  
  public static Object	nthCdr(int count, Object list) {
    if (!isList(list)) {
      return list;
    }
    for (int i = 0; i < count; i++) {
      if (isNil(list)) {
	return list;
      }
      list = cdr(list);
    }
    return list;
  }

}
