package alice.tuprolog.lib;

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

import alice.tuprolog.Library;
import alice.tuprolog.PrologError;
import alice.tuprolog.Struct;
import alice.tuprolog.Term;
import alice.tuprolog.Var;

/**
 * 
 * @author Andrea Boccacci
 *
 */
@SuppressWarnings("serial")
public class TokenizerLibrary extends Library{

	private static final boolean debug = false;
	public TokenizerLibrary(){
	}

	public String getTheory(){
		return ""
		+getOperatorsForTheory()+"\n"
		+getTokenizerTheory()+"\n"
		;
	}

	private String getOperatorsForTheory(){
		return ""
		+":-op(200,fx,'skiplist').\n"
		+":-op(200,fx,'tokenlist').\n"
		+":-op(400,fy,'token').\n"
		+":-op(200,xfx,'becomes').\n"
		+":-op(400,fy,'type').\n"
		+":-op(250,xfx,':').\n"
		+":-op(210,xfx,'as').\n"
		+":-op(50,xfx,'where').\n"
		;
	}
	private String getTokenizerTheory(){
		return ""

		+"file_tokenize(File,Tokens):-text_from_file(File,Text),tokenize(Text,Tokens).\n"
		+"tokenize(Text,Tokens):-tokenizer(Text,Tokens,1,1),!.\n"

		+"tokenizer('',[],_,_).\n"
		+"tokenizer(Text,OUT,ROW,COL):-skip_token(Text,OUT,ROW,COL).\n"
		+"tokenizer(Text,OUT,ROW,COL):-get_token(Text,OUT,ROW,COL).\n"

		+"skip_token(Text,OUT,ROW,COL):-(skiplist Sk),\n"
		+"	member(S,Sk),token_match(Text,S,TOK,RADD,CADD,Rest),\n"
		+"	((RADD>0,NROW is ROW+RADD, NCOL is 1,!);(NROW is ROW , NCOL is COL+CADD)),\n"
		+"	tokenizer(Rest,OUT,NROW,NCOL).\n"

		+"get_token(Text,[TOKEN|OUT],ROW,COL):-(type Type : R),\n"
		+"  get_regex(R,Regex),\n"
		+"  token_match(Text,Regex,T,_,CADD,Rest),\n"
		+"  ((tokenlist Ts),member(T,Ts),unify_with_occurs_check(T,TOK);\n"
		+"  (token T),unify_with_occurs_check(T,TOK);\n"
		+"  (token T becomes TOK);\n"
		+"  get_type_token(Type,R,T,TOK)),\n"
		+"  TOKEN=..[token,TOK,ROW,COL],\n"
		+"  NCOL is COL+CADD,\n"
		+"  tokenizer(Rest,OUT,ROW,NCOL).\n"

		+"get_token(Text,[TOKEN|OUT],ROW,COL):-(tokenlist Ts),\n"
		+"	member(TOK,Ts),\n"
		+"	string_append(TOK,Rest,Text),\n"
		+"	TOKEN=..[token,TOK,ROW,COL],\n"
		+"	atom_length(TOK,ADD),\n"
		+"	NCOL is COL+ADD,\n"
		+"	tokenizer(Rest,OUT,ROW,NCOL).\n"

		+"get_token(Text,[TOKEN|OUT],ROW,COL):-(token T),\n"
		+"	token_transform(T,TMATCH,TOK),\n"
		+"	string_append(TMATCH,Rest,Text),\n"
		+"	TOKEN=..[token,TOK,ROW,COL],\n"
		+"	atom_length(TMATCH,ADD),NCOL is COL+ADD,\n"
		+"	tokenizer(Rest,OUT,ROW,NCOL).\n"

		+"get_type_token(_,(_ as T becomes TOK where {CALL}),T,TOK):-!,call(CALL).\n"
		+"get_type_token(_,(_ as T becomes TOK),T,TOK):-!.\n"
		+"get_type_token(Type,_,T,TOK):-TOK=..[Type,T].\n"

		+"get_regex(as(R,_),R):-!.\n"
		+"get_regex(R,R).\n"

		+"token_transform((TMATCH becomes TOK),TMATCH,TOK):-!.\n"
		+"token_transform(T,T,T).\n"

		+"token_match(Text,'\\n',Token,1,0,Rest):-!,string_match(Text,'\\n',Token),string_append(Token,Rest,Text),!.\n"
		+"token_match(Text,Regex,Token,0,CADD,Rest):-string_match(Text,Regex,Token),string_append(Token,Rest,Text),!,atom_length(Token,CADD).\n"
		;
	}

	/**
	 * @author Andrea Boccacci
	 * Predicate that works like atom_concat/3. It is faster with long strings.
	 * Template : string_append(?start_str,?end_str,?concat_str)
	 * @return true if successful
	 * @throws PrologError
	 */
	public boolean string_append_3(Term arg0, Term arg1,Term arg2) throws PrologError {
		arg0 = arg0.getTerm();
		arg1 = arg1.getTerm();
		arg2 = arg2.getTerm();
		if(debug)
			System.out.println("mi attivo : "+arg0.toString()+" "+arg1.toString()+" "+arg2.toString());
		if (arg1 instanceof Var){
			if(arg0 instanceof Var)
				throw PrologError.instantiation_error(engine.getEngineManager(), 1);
			String st = alice.util.Tools.removeApices(arg2.toString());
			String strarg0 = alice.util.Tools.removeApices(arg0.toString());
			if(debug)
				System.out.println("st ="+st+" strarg0 = "+strarg0);
			if (!st.startsWith( strarg0 )){
				return false;
			}
			st = st.substring(strarg0.length());
			if(debug)
				System.out.println("risultato = "+st);
			Struct res = new Struct(st);
			return unify(arg1, res);
		}
		if (arg0 instanceof Var){
			if(arg1 instanceof Var)
				throw PrologError.instantiation_error(engine.getEngineManager(), 2);

			String st = alice.util.Tools.removeApices(arg2.toString());
			String strarg1 = alice.util.Tools.removeApices(arg1.toString());
			if(debug)
				System.out.println("st ="+st+" strarg1 = "+strarg1);
			if (!st.endsWith( strarg1 )){
				return false;
			}
			st = st.substring(0, st.length()-strarg1.length());
			if(debug)
				System.out.println("risultato = "+st);
			Term res = engine.toTerm("'"+st+"'");
			return unify(arg0, res);
		}
		String s0 = alice.util.Tools.removeApices(arg0.toString());
		String s1 = alice.util.Tools.removeApices(arg1.toString());
		if(debug)
			System.out.println("arg0 ="+s0+" arg1 = "+s1);
		return unify(arg2, engine.toTerm("'"+s0+s1+"'"));
	}

	/**
	 * @author Andrea Boccacci
	 * Predicate that takes a source string ad a regex string and it tries to find a substring that matches the regex.
	 * Template : string_match(@string,@regex_string,?integer,?integer,?string)
	 * @return true if successful
	 * @throws PrologError
	 */
	public boolean string_match_4(Term srct, Term regext, Term indext, Term matcht) throws PrologError{
		srct = srct.getTerm();
		regext = regext.getTerm();
		if (srct instanceof Var)
			throw PrologError.instantiation_error(engine.getEngineManager(), 1);
		if (regext instanceof Var)
			throw PrologError.instantiation_error(engine.getEngineManager(), 2);

		String regex = alice.util.Tools.removeApices(regext.toString());
		String src = alice.util.Tools.removeApices(srct.toString());
		if(debug)
			System.out.println("regex : "+regex+"\tsrc : "+src);
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(src);

		// Check first occurrence              
		if (matcher.find()){                   
			String found = matcher.group();    
			String idx = ""+matcher.start();   
			if(debug)
				System.out.println("index : "+idx+"\tfound : "+found);
			Struct match = new Struct(found);
			Term index = engine.toTerm(idx);
			if(debug)
				System.out.println("all ok!");
			return (unify(matcht, match) && unify(indext, index));
		}
		else {
			if(debug)
				System.out.println("non trovato!");
			return false;
		}
	}

	public boolean string_match_3(Term srct, Term regext, Term matcht) throws PrologError{
		//This predicate take 
		srct = srct.getTerm();
		regext = regext.getTerm();
		if (srct instanceof Var)
			throw PrologError.instantiation_error(engine.getEngineManager(), 1);
		if (regext instanceof Var)
			throw PrologError.instantiation_error(engine.getEngineManager(), 2);

		String regex = alice.util.Tools.removeApices(regext.toString());
		String src = alice.util.Tools.removeApices(srct.toString());
		if(debug)
			System.out.println("regex : "+regex+"\tsrc : "+src);
		Pattern pattern = Pattern.compile(regex);
		Matcher matcher = pattern.matcher(src);

		// Check first occurrence              
		if (matcher.find() && matcher.start()==0){                   
			String found = matcher.group();      
			if(debug)
				System.out.println("tfound : "+found);
			Struct match = new Struct(found);
			if(debug){
				System.out.println("all ok!");
			}
			return unify(matcht, match);
		}
		else {
			if(debug)
				System.out.println("non trovato!");
			return false;
		}
	}	

}
