package ws.prova;

/*
 * Copyright (C) 2002-2007 <a href="mailto:a.kozlenkov@city.ac.uk">Alex Kozlenkov</a>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import org.mandarax.kernel.ComplexTerm;
import org.mandarax.kernel.ConstantTerm;
import org.mandarax.kernel.Term;
import org.mandarax.kernel.VariableTerm;
import ws.prova.kernel.ProvaTerm;
import ws.prova.util.DefaultLogicFactorySupport;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Prova RList utilities
 * <p>Title: Prova RList utilities</p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (C) 2002-2007</p>
 * <p>Company: City University, London, United Kingdom</p>
 *
 * @author unascribed
 * @version 2.0 <25 March 2007>
 */
public final class RListUtils {
	// Matching the whole list (greedy search for last ']')
	final static Pattern brPattern = Pattern.compile("(\\[.*\\])");

	// Matching a separator followed by a sub-list: uses non-capturing group to allow any symbols in quotes
	final static Pattern subPattern = Pattern.compile("([\\[,])(\\[(?:\"[^\"]*\"|.)*?\\])(\\]*)|([\\[,\\|])(\"[^\"]*\"|[^\\[,\\|\\]]+)(\\]*)");

	// Matching a separator followed by an individual list element (not a sub-list)
//	final static Pattern elPattern = Pattern.compile("([\\[,\\|])(\"[^\"]*\"|[^\\[,\\|\\]]*)");
	final static Pattern elPattern = Pattern.compile("([\\[,\\|])(\"[^\"]*\"|[^\\[,\\|\\]]+)|(\\|\\[)([^\\[,\\|\\]]+)(\\])");

	// Matching two list starting symbols in a row
	final static Pattern embPattern = Pattern.compile("\\[(\"[^\"]*\"|[^\\]\\|])*\\[");

	final static RList emptyRList = new RList();

	public final static Term emptyRListTerm = DefaultLogicFactorySupport.cons(emptyRList, RList.class);

	static Random r = new Random();

	/**
	 * A helper method used for creating complex terms from lists
	 *
	 * @param cl      Class class of individual variables
	 * @param matcher Matcher
	 * @param objs    Object[]
	 * @return Term
	 */
	static private Term _get_list(final Map vars, final Class cl, final Matcher matcher, final Object[] objs) throws ClassNotFoundException {
		if (matcher.find()) {
			String sep = matcher.group(1);
			String var = matcher.group(2);
			if( var==null ) {
				sep = matcher.group(3);
				var = matcher.group(4);
//				Term t = rlist(vars, 1, cl, var, objs);
//				int i = 0;
			}
			if (sep.equals("[") && var.equals("")) {
				// An empty list
				return emptyRListTerm; // DefaultLogicFactorySupport.cons(emptyRList, RList.class);
			}
			if (sep.equals("|")) {
                if( var.startsWith("%") ) {
                    // Rest seems to have an object placeholder
                    // We only allow that if the corresponding object is a ComplexTerm,
                    //    otherwise a variable with the name equal to the "list" content is created
                    int n = Integer.parseInt(var.substring(1));
                    if( objs[n] instanceof ComplexTerm )
                        return (Term) objs[n];
                }
				return generateVar(vars, var, RList.class);
			}
			char first = var.charAt(0);
			if (first == '%') {
				// Java object is a list element number 'n'
				// TO-DO exceptions
				int n = Integer.parseInt(var.substring(1));
				Term t = DefaultLogicFactorySupport.cplx(reagent.flist, _get_list(vars, cl, matcher, objs), objs[n]);
				return t;
			}
			int dotAt = var.lastIndexOf('.');
			// Anything in double quotes is a constant
			// Anything starting with a small letter and not containing '.' is also a constant
			if (first != '_' && first != '@' &&
					(first == '\"' ||
					(dotAt < 0 && Character.toLowerCase(first) == first))) {
				// It is a string constant
				if (first == '\"') {
					var = var.substring(1, var.length() - 1);
				}
				return DefaultLogicFactorySupport.cplx(reagent.flist, _get_list(vars, cl, matcher, objs), var);
			}
			if (first == '_') {
				var += r.nextInt();
			}
			String varFull = var;
			Class cl2 = cl;
			if (dotAt >= 0) {
				// The class of a list element is explicitly specified (e.g., Investment.X)
//				try {
				try {
					cl2 = ws.prova.Calc.forName("ws.prova." + var.substring(0, dotAt));
				} catch (Throwable exc) {
					try {
						cl2 = ws.prova.Calc.forName("java.lang." + var.substring(0, dotAt));
					} catch (Throwable exc2) {
						try {
							cl2 = ws.prova.Calc.forName(var.substring(0, dotAt));
						} catch (Throwable exc3) {
							// Fully qualified class name
							cl2 = ws.prova.Calc.forName(var);
							return DefaultLogicFactorySupport.cplx(reagent.flist, _get_list(vars, cl, matcher, objs),
									DefaultLogicFactorySupport.cons(cl2, cl2.getClass()));
						}
					}
				}
//				} catch( Throwable ex ) {
//					cl2 = cl;
//				}
				var = var.substring(dotAt + 1);
				try {
					// Retrieve the static field value if there is one
					Object o = cl2.getField(var).get(null);
					return DefaultLogicFactorySupport.cplx(reagent.flist, _get_list(vars, cl, matcher, objs),
							DefaultLogicFactorySupport.cons(o, o.getClass()));
				} catch (Exception ex) {
					// This exception is ignored intentionally:
					//     any unidentified field in a class becomes a variable 'var' OF a class
				}
			}
			return DefaultLogicFactorySupport.cplx(reagent.flist, _get_list(vars, cl, matcher, objs),
					generateVar(vars, varFull, cl2)); //DefaultLogicFactorySupport.variable(var, cl2));
		}
		return emptyRListTerm; // DefaultLogicFactorySupport.cons(emptyRList, RList.class);
	}

	/**
	 * Generate a variable term making sure each term with the same name points to the same instance.
	 *
	 * @param var Full specified variable name including possible type prefix
	 * @return a constructed term
	 */
	private static Term generateVar(final Map vars, String var, final Class cl) {
		if( var.startsWith("RList.") ) {
			var = var.substring(6);
		}
		if( var.startsWith(cl.getName()) ) {
			// The full class prefix is not copied
			var = var.substring(cl.getName().length()+1);
		}
		var = var.replaceAll("\\.","::");
		Term term = (Term) vars.get(var);
		if (term != null) {
			if( !term.isVariable() )
				// Subsequent tests are only applicable to variables mapped to VariableTerm's
				return term;
			Class tp = term.getType(); 
			if (tp != cl) {
				if (cl==RList.class && tp==Object.class) {
					// RList previously declared as Object
					VariableTerm v = (VariableTerm)term;
					v.setType(cl);
					return term;
				} else {
					// TODO: Conflicting type prefixes found in the term
				}
			} else
				return term;
		}
		String varAsGenerated = var;
//		int dotAt = var.lastIndexOf('.');
//		if( dotAt>=0 )
//			varAsGenerated = var.substring(dotAt+1);
		term = DefaultLogicFactorySupport.variable(varAsGenerated, cl);
		vars.put(varAsGenerated, term);
		return term;
	}

	// RA 2005-10-06: created for use in both in decode/4 and decode_rename/2
	static private boolean isList(Term t) {
		return
			t instanceof ComplexTerm
			||
			(t instanceof ConstantTerm &&
					((ConstantTerm) t).getObject() instanceof RList);
	}

	/**
	 * A helper method used for creating complex terms from lists
	 *
	 * @param cl   Class class of individual variables
	 * @param var  String
	 * @param objs Object[]
	 * @return Term
	 */
	static private Term _get_list(final Map vars, final Class cl, String var, final Object[] objs) throws ClassNotFoundException {
		char first = var.charAt(0);
		if (first == '%') {
			// Java object is a list element number 'n'
			// TO-DO exceptions
			int n = Integer.parseInt(var.substring(1));
            if( objs[n] instanceof VariableTerm || objs[n] instanceof ComplexTerm )
                return (Term) objs[n];
            return DefaultLogicFactorySupport.cons(objs[n], objs[n].getClass());
		}
		int dotAt = var.lastIndexOf('.');
		// Anything in double quotes is a constant
		// Anything starting with a small letter and not containing '.' is also a constant
		if (first != '_' && first != '@' &&
				(first == '\"' || (dotAt < 0 && Character.toLowerCase(first) == first))) {
			// It is a string constant
			if (first == '\"') {
				var = var.substring(1, var.length() - 1);
			}
			return DefaultLogicFactorySupport.cons(var, String.class);
		}
		if (first == '_') {
			var += r.nextInt();
		}
		String varFull = var;
		Class cl2 = cl;
		if (dotAt >= 0) {
			// The class of a list element is explicitly specified (e.g., Investment.X)
//			try {
			try {
				cl2 = ws.prova.Calc.forName("ws.prova." + var.substring(0, dotAt));
			} catch (Throwable exc) {
				try {
					cl2 = ws.prova.Calc.forName("java.lang." + var.substring(0, dotAt));
				} catch (Throwable exc2) {
					try {
						cl2 = ws.prova.Calc.forName(var.substring(0, dotAt));
					} catch (Throwable exc3) {
						// Fully qualified class name
						cl2 = ws.prova.Calc.forName(var);
						return DefaultLogicFactorySupport.cons(cl2, cl2.getClass());
					}
				}
			}
//			} catch( Throwable ex ) {
//				cl2 = cl;
//			}
			var = var.substring(dotAt + 1);
			try {
				// Retrieve the static field value if there is one
				Object o = cl2.getField(var).get(null);
				return DefaultLogicFactorySupport.cons(o, o.getClass());
			} catch (Exception ex) {
				// This exception is ignored intentionally:
				//     any unidentified field in a class becomes a variable 'var' OF a class
			}
		}
		return generateVar(vars, varFull, cl2);
	}

	/**
	 * Count occurrences of <code>find</code> in the supplied String <code>s</code>
	 *
	 * @param s    String the string to be searched
	 * @param find char the character to be found
	 * @return int the number of occurrences
	 */
	static private int count(final String s, final char find) {
		int count = 0;
		for (int i = s.length(); i-- > 0;) {
			if (s.charAt(i) == find) {
				count++;
			}
		}
		return count;
	}

	/**
	 * A helper method used during recursion to sub-lists
	 * when creating complex terms from lists
	 *
	 * @param cl      Class class of individual variables
	 * @param matcher Matcher
	 * @param objs    Object[]
	 * @return Term
	 */
	static private Term _sub_list(final Map vars, final Class cl, final Matcher matcher, final Object[] objs) throws ClassNotFoundException {
		if (matcher.find()) {
			String brc = matcher.group(3);
			String sep = matcher.group(1);
			String list = matcher.group(2);
			if (sep == null) {
				sep = matcher.group(4);
				list = matcher.group(5);
				brc = matcher.group(6);

			} else {
				int count_open = count(list, '[');
				int count_close = count(list, ']');
				if (brc.length() > 0) {
					count_close += brc.length();
				}
				while ((count_open != count_close)) {
					if (brc.length() > 0) {
						list += brc;
					}
					boolean b = matcher.find();
					if (!b) {
						if (sep.equals("|")) {
                            if( list.startsWith("%") ) {
                                // Rest seems to have an object placeholder
                                // We only allow that if the corresponding object is a ComplexTerm,
                                //    otherwise a variable with the name equal to the "list" content is created
                                int n = Integer.parseInt(list.substring(1));
                                if( objs[n] instanceof ComplexTerm )
                                    return (Term) objs[n];
                            }
							return generateVar(vars, list, RList.class);
						}
						return DefaultLogicFactorySupport.cplx(reagent.flist, 
								emptyRListTerm, // DefaultLogicFactorySupport.cons(emptyRList, RList.class),
								rlist(vars, 1, cl, list, objs));
					}
					String sep1 = matcher.group(1);
					String list1 = matcher.group(2);
					brc = matcher.group(3);
					if (sep1 == null) {
						brc = matcher.group(6);
						sep1 = matcher.group(4);
						list1 = matcher.group(5);
					}
					list += sep1 + list1;
					count_open += count(list1, '[');
					count_close += count(list1, ']');
					if (brc.length() > 0) {
						count_close += brc.length();
					}
				}
				if (brc.length() > 0) {
					list += brc;
				}
			}
			if (sep.equals("|")) {
                if( list.startsWith("%") ) {
                    // Rest seems to have an object placeholder
                    // We only allow that if the corresponding object is a ComplexTerm,
                    //    otherwise a variable with the name equal to the "list" content is created
                    int n = Integer.parseInt(list.substring(1));
                    if( objs[n] instanceof ComplexTerm )
                        return (Term) objs[n];
                }
                return generateVar(vars, list, RList.class);
			}
			Term sub = _sub_list(vars, cl, matcher, objs);
			return DefaultLogicFactorySupport.cplx(reagent.flist, sub, rlist(vars, 1, cl, list, objs));
		}
		return emptyRListTerm;
	}

	/**
	 * The top-level method for creating complex terms from lists
	 *
	 * @param i    int
	 * @param cl   Class
	 * @param s    String
	 * @param objs Object[]
	 * @return Term
	 */
	static protected Term rlist(final Map vars, final int i, final Class cl, final String s, final Object[] objs) throws ClassNotFoundException {
		Matcher matcherA = brPattern.matcher(s);
		if (matcherA.find()) {
			String sB = matcherA.group(1);
			Matcher matcherB = embPattern.matcher(sB);
			if (!matcherB.find()) {
				// An innermost list
				Matcher matcher = elPattern.matcher(sB);
				return _get_list(vars, cl, matcher, objs);
			}
			// Sub-lists in sB
			Matcher matcherC = subPattern.matcher(sB);
			return _sub_list(vars, cl, matcherC, objs);
		}
		// Currently, return an empty list for an empty no-match
		if (s.equals("")) {
			return emptyRListTerm; // DefaultLogicFactorySupport.cons(emptyRList, RList.class);
		}
		if (i == 0) {
			// A non-bracketed outer element
			return _get_list(vars, RList.class, s, objs);
		}
		// An innermost element
		return _get_list(vars, cl, s, objs);
	}

	/**
	 * List translation with individual list variables assumed to be of class String
	 * and no objects list
	 *
	 * @param vars Maps variable names to variable terms
	 * @param s    String the string with a list in Prolog-like format
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist_with_exceptions(final Map vars, final String s) throws ClassNotFoundException {
		return rlist(vars, 0, Object.class, s, null);
	}

	/**
	 * List translation with individual list variables assumed to be of class String
	 * and an array with embedded objects
	 *
	 * @param s    String the string with a list in Prolog-like format
	 * @param objs Object[] array with embedded objects
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist_with_exceptions(final Map vars, final String s, final Object[] objs) throws ClassNotFoundException {
		return rlist(vars, 0, Object.class, s, objs);
	}

	/**
	 * List translation with individual list variables assumed to be of class String
	 * and no objects list
	 *
	 * @param s String the string with a list in Prolog-like format
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist(final String s) {
		Map vars = new HashMap();
		Term t = null;
		try {
			t = rlist(vars, 0, Object.class, s, null);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return t;
	}

	/**
	 * List translation with individual list variables assumed to be of class String
	 * and an array with embedded objects
	 *
	 * @param s    String the string with a list in Prolog-like format
	 * @param objs Object[] array with embedded objects
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist(final String s, final Object[] objs) {
		Map vars = new HashMap();
		Term t = null;
		try {
			t = rlist(vars, 0, Object.class, s, objs);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return t;
	}

	/**
	 * List translation with individual list variables of the specified class
	 *
	 * @param cl Class class of individual variables
	 * @param s  String the string with a list in Prolog-like format
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist(final Class cl, final String s) {
		Map vars = new HashMap();
		Term t = null;
		try {
			t = rlist(vars, 0, cl, s, null);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return t;
	}

	/**
	 * List translation with individual list variables of the specified class
	 * and an array with embedded objects
	 *
	 * @param cl   Class class of individual variables
	 * @param s    String the string with a list in Prolog-like format
	 * @param objs Object[] array with embedded objects
	 * @return Term the complex term resulting from the translation
	 */
	static public Term rlist(final Class cl, final String s, final Object[] objs) {
		Map vars = new HashMap();
		Term t = null;
		try {
			t = rlist(vars, 0, cl, s, objs);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return t;
	}

	/**
	 * An inner method used by decode(Term,List) to build a string representing
	 * the currect sub-rlist and update the map and list of embedded objects
	 *
	 * @param t    - the term to be decoded
+	 * @param ren  - rename temporal variables if true
	 * @param vars - the map of embedded objects to their respective %NN placeholders
	 * @param objs - the ordered list of embedded objects
	 * @return - the string representing the term t
	 */
	// RA 2005-10-06: added parameter ren and merged with decode_rename/3
	static private String decode(Term t, boolean ren, final HashMap vars, final List objs) {
//	static private String decode(Term t, final HashMap vars, final List objs) {
		StringBuffer buf = new StringBuffer();

		if (t instanceof ProvaTerm) {
			t = ((ProvaTerm) t).getTerm();
		}
		if (t instanceof ComplexTerm) {
			ComplexTerm ct = (ComplexTerm) t;
			//buf.append( ct.getFunction().getName());
			Term t0 = ct.getTerms()[0];
			String s0 = decode(t0, ren, vars, objs);
			boolean list_tail_is_empty = s0.equals("");
			Term t2 = ct.getTerms()[1];
			boolean list_start_is_list = isList(t2);
			if (list_start_is_list) {
				buf.append('[');
			}
			buf.append(decode(t2, ren, vars, objs));
			if (list_start_is_list) {
				buf.append(']');
			}
			if (!list_tail_is_empty) {
				if (t0 instanceof VariableTerm && t0.getType() == RList.class) {
					buf.append('|');
				} else {
					buf.append(',');
				}
				buf.append(s0);
			}
		} else if (t instanceof ConstantTerm) {
			ConstantTerm kt = (ConstantTerm) t;
			Class cl = kt.getType();
			Object obj = kt.getObject();
			if (!ren && (cl == String.class || obj instanceof String) ) {
				String s = obj.toString();
				buf.append('"');
				s = s.replaceAll("\"", "\"\"");
				buf.append(s);
				buf.append('"');
			} else if (cl == RList.class) {
				buf.append("");
			} else {
				Object o = vars.get(obj);
				if (o != null) {
					buf.append(o);
				} else {
					objs.add(obj);
					String s = "%" + vars.size();
					vars.put(obj, s);
					buf.append(s);
				}
			}
		} else if (t instanceof VariableTerm) {
			VariableTerm vt = (VariableTerm) t;
			Class cl = vt.getType();
			String name = vt.getName();
			int dotAt = name.lastIndexOf('.');
			if( dotAt>=0 )
				name = name.substring(dotAt+1);
			if (ren && name.charAt(0) == '@') {
				name = 'N' + name;
			}
			if (cl == Object.class || cl == RList.class) {
//				String name = vt.getName();
//				if( name.charAt(0)=='@' ) {
//					name = '_' + name;
//				}
				buf.append(name);
			} else {
				buf.append(vt.getType().getName());
				buf.append('.');
				buf.append(name);
			}
		}

		return new String(buf);
	}

	/**
	 * The same as decode but with temporal variables renamed
	 *
	 * @param t    Term
	 * @param vars HashMap
	 * @param objs List
	 * @return String
	 */
	//	 RA 2005-10-06: merged with decode/4 (with new parameter boolean ren)
/*
	static private String decode_rename(final Term t, final HashMap vars, final List objs) {
		StringBuffer buf = new StringBuffer();

		if (t instanceof ComplexTerm) {
			ComplexTerm ct = (ComplexTerm) t;
			//buf.append( ct.getFunction().getName());
			Term t0 = ct.getTerms()[0];
			String s0 = decode_rename(t0, vars, objs);
			boolean list_tail_is_empty = s0.equals("");
			boolean list_start_is_list = ct.getTerms()[1] instanceof ComplexTerm;
			if (list_start_is_list) {
				buf.append('[');
			}
			buf.append(decode_rename(ct.getTerms()[1], vars, objs));
			if (list_start_is_list) {
				buf.append(']');
			}
			if (!list_tail_is_empty) {
				if (t0 instanceof VariableTerm && t0.getType() == RList.class) {
					buf.append('|');
				} else {
					buf.append(',');
				}
				buf.append(s0);
			}
		} else if (t instanceof ConstantTerm) {
			ConstantTerm kt = (ConstantTerm) t;
			Class cl = kt.getType();
			Object obj = kt.getObject();
			if (cl == RList.class) {
				buf.append("");
			} else {
				Object o = vars.get(obj);
				if (o != null) {
					buf.append(o);
				} else {
					objs.add(obj);
					String s = "%" + vars.size();
					vars.put(obj, s);
					buf.append(s);
				}
			}
		} else if (t instanceof VariableTerm) {
			VariableTerm vt = (VariableTerm) t;
			Class cl = vt.getType();
			String name = vt.getName();
			if (name.charAt(0) == '@') {
				name = 'N' + name;
			}
			if (cl == Object.class || cl == RList.class) {
				buf.append(name);
			} else {
				buf.append(vt.getType().getName());
				buf.append('.');
				buf.append(name);
			}
		}

		return new String(buf);
	}
*/
	/**
	 * Build a string representing an rlist in ComplexTerm format
	 * and update the map and the list of embedded objects
	 * Limitation: no checks are made that the ComplexTerm term t
	 * is indeed an rlist
	 *
	 * @param t    ComplexTerm the term to be decoded
	 * @param objs List the ordered list of embedded objects
	 * @return String the string representing the term <code>t</code>
	 */
	//	 RA 2005-10-06: use decode/4 (with new parameter boolean ren)
	static public String decode(final ComplexTerm t, final List objs) {
		HashMap vars = new HashMap();
		String s = decode(t, false, vars, objs);
		return "[" + s + "]";
	}

	/**
	 * The same as decode but with temporal variables renamed.
	 * !!! IMPORTANT: It is assumed that if the term t is not ComplexTerm it is an empty list
	 *
	 * @param t    Term
	 * @param objs List
	 * @return String
	 */
	//	 RA 2005-10-06: use decode/4 (with new parameter boolean ren)
	static String decode_rename(final Term t, final List objs) {
		HashMap vars = new HashMap();
		if( isList(t) ) {
			String s = decode(t, true, vars, objs);
			return "[" + s + "]";
		}
		return "[]";
	}

    /**
     * Simplified way of converting a linear list into a Java list
     * @param ct ComplexTerm a linear (non-nested) list
     * @return Term the list rest, an empty list if no rest available, null if there is a problem
     */
    static public Term rlist2List(final ComplexTerm ct) {
        return rlist2List(ct, new ArrayList(), 0);
    }

    /**
     * Build an <code>RList</code> given a <code>Collection</code>
     *
     * @param coll <code>Collection</code> to be stored into the returned <code>RList</code>.
     * @return <code>RList</code> with the collection.
     */
    public static Object list2RList( final Collection coll ) {
        StringBuffer rlistTxt = new StringBuffer();
        rlistTxt.append('[');

        int n = coll.size();
        for (int index = 0; index < n; index++) {
            rlistTxt.append('%').append(index);
            if (index + 1 < n)
                rlistTxt.append(',');
        }
        rlistTxt.append(']');
        return RListUtils.rlist(rlistTxt.toString(), coll.toArray());
    }

    /**
	 * Convert a linear list into a Java list
	 *
	 * @param ct   ComplexTerm a linear (non-nested) list
	 * @param l    Collection the list with objects for ConstantTerms(s) and VariableTerm(s) left intact
	 * @param skip int how many initial elements to skip before starting copying
	 * @return Term the list rest, an empty list if no rest available, null if there is a problem 
	 */
	static public Term rlist2List(final ComplexTerm ct, final Collection l, int skip) {
		Term[] tt = ct.getTerms();
		for (int i = 1; i >= 0; i--) {
			Term t = tt[i];
			if (i == 0) {
				if (t instanceof ComplexTerm) {
					return rlist2List((ComplexTerm) t, l, skip);
				} else if (t instanceof VariableTerm) {
					return t;
				}
				return emptyRListTerm;
			}
			if (skip > 0) {
				skip--;
				continue;
			}
//			System.out.println(t);
			if (t instanceof ConstantTerm) {
				final Object cn = ((ConstantTerm) t).getObject();
				if (cn == null)
					l.add(t);
				else
					l.add(cn);
			} else {
				// Either VariableTerm or ComplexTerm
				l.add(t);
			}
		}
		return null;
	}

	static public String term_as_string(final Object o) {
		if (o instanceof String) {
			return o.toString();
		}
		if (!(o instanceof ComplexTerm)) {
			return "";
		}
		ComplexTerm ct = (ComplexTerm) o;
		List objs = new ArrayList();
		String s = decode(ct, objs);
		if (s.equals("")) {
			s = "[]";
		}
		for (int k = objs.size(); k-- > 0;) {
			RE placeholderRE = null;
			try {
				placeholderRE = new RE("%" + k);
			} catch (RESyntaxException ex) {
			}
			s = placeholderRE.subst(s, objs.get(k).toString());
		}

		return s;
	}
}
