package railo.runtime.regex;

import java.util.Map;

import org.apache.oro.text.regex.MalformedPatternException;
import org.apache.oro.text.regex.MatchResult;
import org.apache.oro.text.regex.Pattern;
import org.apache.oro.text.regex.PatternMatcherInput;
import org.apache.oro.text.regex.Perl5Compiler;
import org.apache.oro.text.regex.Perl5Matcher;
import org.apache.oro.text.regex.Perl5Substitution;
import org.apache.oro.text.regex.Util;

import railo.commons.collection.MapFactory;
import railo.runtime.op.Constants;
import railo.runtime.type.Array;
import railo.runtime.type.ArrayImpl;
import railo.runtime.type.Struct;
import railo.runtime.type.StructImpl;

/**
 * 
 */
public final class Perl5Util {
    
    private static Map<String,Pattern> patterns=MapFactory.<String,Pattern>getConcurrentMap();
    
	/**
	 * return index of the first occurence of the pattern in input text
	 * @param strPattern pattern to search
	 * @param strInput text to search pattern
	 * @param offset 
	 * @param caseSensitive
	 * @return position of the first occurence
	 * @throws MalformedPatternException
	*/
	public static int indexOf(String strPattern, String strInput, int offset, boolean caseSensitive) throws MalformedPatternException {
        //Perl5Compiler compiler = new Perl5Compiler();
        PatternMatcherInput input = new PatternMatcherInput(strInput);
        Perl5Matcher matcher = new Perl5Matcher();
        
        int compileOptions=caseSensitive ? 0 : Perl5Compiler.CASE_INSENSITIVE_MASK;
        compileOptions+=Perl5Compiler.SINGLELINE_MASK;
        if(offset < 1) offset = 1;
        
        Pattern pattern = getPattern(strPattern,compileOptions);
        //Pattern pattern = compiler.compile(strPattern,compileOptions);
        

        if(offset <= strInput.length()) input.setCurrentOffset(offset - 1);
        
        if(offset <= strInput.length() && matcher.contains(input, pattern)) {
            return matcher.getMatch().beginOffset(0) + 1; 
        }
        return 0;
    }
    
    
	/**
	 * find occurence of a pattern in a string (same like indexOf), but dont return first ocurence , it return
	 * struct with all information
	 * @param strPattern
	 * @param strInput
	 * @param offset
	 * @param caseSensitive
	 * @return
	 * @throws MalformedPatternException
	 */
	public static Struct find(String strPattern, String strInput, int offset, boolean caseSensitive) throws MalformedPatternException {
        
        Perl5Matcher matcher = new Perl5Matcher();
        PatternMatcherInput input = new PatternMatcherInput(strInput);
        
        
        int compileOptions=caseSensitive ? 0 : Perl5Compiler.CASE_INSENSITIVE_MASK;
        compileOptions+=Perl5Compiler.SINGLELINE_MASK;
        if(offset < 1) offset = 1;
        
        
        Pattern pattern = getPattern(strPattern,compileOptions);
        
  
        if(offset <= strInput.length()) input.setCurrentOffset(offset - 1);
        
        if(offset <= strInput.length() && matcher.contains(input, pattern)) {
            MatchResult result = matcher.getMatch();
            
            int groupCount = result.groups();
            Array posArray = new ArrayImpl();
            Array lenArray = new ArrayImpl();
            for(int i = 0; i < groupCount; i++) {
                int off = result.beginOffset(i);
                posArray.appendEL(Integer.valueOf(off + 1));
                lenArray.appendEL(Integer.valueOf(result.endOffset(i) - off));
            }
            Struct struct = new StructImpl();
            struct.setEL("pos", posArray);
            struct.setEL("len", lenArray);
            return struct;
            
        }
        Array posArray = new ArrayImpl();
        Array lenArray = new ArrayImpl();
        posArray.appendEL(Constants.INTEGER_0);
        lenArray.appendEL(Constants.INTEGER_0);
        
        Struct struct = new StructImpl();
        struct.setEL("pos", posArray);
        struct.setEL("len", lenArray);
        return struct;
    }
	

	public static Array match(String strPattern, String strInput, int offset, boolean caseSensitive) throws MalformedPatternException {
        
        Perl5Matcher matcher = new Perl5Matcher();
        PatternMatcherInput input = new PatternMatcherInput(strInput);
        
        
        int compileOptions=caseSensitive ? 0 : Perl5Compiler.CASE_INSENSITIVE_MASK;
        compileOptions+=Perl5Compiler.MULTILINE_MASK;
        if(offset < 1) offset = 1;
        
        
        Pattern pattern = getPattern(strPattern,compileOptions);
        
        
        Array rtn = new ArrayImpl();
        MatchResult result;
        while(matcher.contains(input, pattern)) {
          result = matcher.getMatch();  
          rtn.appendEL(result.toString());
          /*
          System.out.println("Match: " + result.toString());
          System.out.println("Length: " + result.length());
          groups = result.groups();
          System.out.println("Groups: " + groups);
          System.out.println("Begin offset: " + result.beginOffset(0));
          System.out.println("End offset: " + result.endOffset(0));
          System.out.println("Saved Groups: ");

          // Start at 1 because we just printed out group 0
          for(int group = 1; group < groups; group++) {
        	  System.out.println(group + ": " + result.group(group));
        	  System.out.println("Begin: " + result.begin(group));
        	  System.out.println("End: " + result.end(group));
          }*/
        }
        return rtn;
    }
	
	
	private static Pattern getPattern(String strPattern, int type) throws MalformedPatternException {
	    Object o=patterns.get(strPattern+type);
	    if(o==null) {
	        Pattern pattern = new Perl5Compiler().compile(strPattern, type);
	        patterns.put(strPattern+type,pattern);
	        return pattern;
	    }
	    return (Pattern) o;
	    
	}
	
	/**
	 * replace the first/all occurence of given pattern
	 * @param strInput text to search pattern
	 * @param strPattern pattern to search
	 * @param replacement text to replace with pattern
	 * @param caseSensitive
	 * @param replaceAll do replace all or only one
	 * @return transformed text
	 * @throws MalformedPatternException
	 */
	public static String replace(String strInput, String strPattern, String replacement, boolean caseSensitive, boolean replaceAll) throws MalformedPatternException {
	    return _replace(strInput,strPattern,escape(replacement),caseSensitive,replaceAll);
	}
	private static String _replace(String strInput, String strPattern, String replacement, boolean caseSensitive, boolean replaceAll) throws MalformedPatternException {
		Pattern pattern = getPattern(strPattern, caseSensitive?16:17);
		return Util.substitute(new Perl5Matcher(), pattern, new Perl5Substitution(replacement), strInput, replaceAll ? -1 : 1);
	}
	

	private static String escape(String replacement) throws MalformedPatternException {
	    replacement = _replace(replacement, "\\\\", "\\\\\\\\", false, true);
	    replacement=_escape(replacement);
	    replacement = _replace(replacement, "\\\\\\\\(\\d)", "\\$$1", false, true);
		return replacement;
	}
    private static String _escape(String str) {
        StringBuffer sb=new StringBuffer();
        int len=str.length();
        char c;
        
        for(int i=0;i<len;i++) {
            c=str.charAt(i);
            
            if('+'==c)			sb.append("\\+");
            else if('?'==c)		sb.append("\\?");
            else if('$'==c)		sb.append("\\$");
            else if('^'==c)		sb.append("\\^");
            else if('\\'==c)	{
                if(i+1<len) {
                    char n=str.charAt(i+1);
                    if('\\'==n) {
                        if(i+2<len) {
                            char nn=str.charAt(i+2);
	                        char x=0;
	                        if('U'==nn) 	 x='U';
	                        else if('L'==nn) x='L';
	                        else if('u'==nn) x='u';
	                        else if('l'==nn) x='l';
	                        else if('E'==nn) x='E';
	                        //else if('d'==nn) x='d';
	                        if(x!=0) {
	                            sb.append("\\"+x);
	                            i+=2;
	                            continue;
	                        }
                        }
                    }
                }
                sb.append(c);
            }
            else sb.append(c);
        }
        return sb.toString();
    }
    
    public static void main(String[] args) throws MalformedPatternException {
        find(
                "<function[^>]*>.*?</function>",
                "<function name=\"susi2\" some=\"1\">\n<argument name=\"susi\">\naaa</function>",
                1,false);
    }
}