package org.drools.examples;

import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.drools.lang.descr.AndDescr;
import org.drools.lang.descr.AttributeDescr;
import org.drools.lang.descr.BoundVariableDescr;
import org.drools.lang.descr.ColumnDescr;
import org.drools.lang.descr.EvalDescr;
import org.drools.lang.descr.ExistsDescr;
import org.drools.lang.descr.FieldBindingDescr;
import org.drools.lang.descr.FunctionDescr;
import org.drools.lang.descr.LiteralDescr;
import org.drools.lang.descr.NotDescr;
import org.drools.lang.descr.OrDescr;
import org.drools.lang.descr.PackageDescr;
import org.drools.lang.descr.PackageDescrDumper;
import org.drools.lang.descr.PredicateDescr;
import org.drools.lang.descr.QueryDescr;
import org.drools.lang.descr.ReturnValueDescr;
import org.drools.lang.descr.RuleDescr;
import org.drools.util.ReflectiveVisitor;


public class xmlDump extends ReflectiveVisitor
 {

    private StringBuffer xmlDumpr;
    private final static String eol = System.getProperty( "line.separator" );

    public synchronized String dump(final PackageDescr packageDescr,final ColumnDescr columnDescr,final FieldBindingDescr fieldbindingDescr,final LiteralDescr []literalDescr) {
        this.xmlDumpr = new StringBuffer();
        visitPackageDescr( packageDescr,columnDescr,fieldbindingDescr,literalDescr );
        return this.xmlDumpr.toString();
    }

    public void visitAndDescr(final AndDescr descr) {
        this.template = new String();
        if ( descr.getDescrs() != Collections.EMPTY_LIST ) {
            this.template = "<and>" + processDescrList( descr.getDescrs() ) + "</and>";
        } else {
            this.template = "<and> </and>";
        }
    }

    public void visitAttributeDescr(final AttributeDescr attributeDescr) {
        this.template = new String();
        this.template = "<rule-attribute name=\"" + attributeDescr.getName() + "\" value=\"" + attributeDescr.getValue() + "\" />" + xmlDump.eol;
    }

    public void visitBoundVariableDescr(final BoundVariableDescr descr) {
        this.template = new String();
        this.template = "<bound-variable field-name=\"" + descr.getFieldName() + "\" evaluator=\"" + getEvaluator( descr.getEvaluator() ) + "\" identifier=\"" + descr.getIdentifier() + "\" />" + xmlDump.eol;
    }

    public String visitColumnDescr(final ColumnDescr descr,final LiteralDescr literalDescr,final FieldBindingDescr fieldbindingDescr) {
        this.template = new String();
        if ( descr.getDescrs() != Collections.EMPTY_LIST ) {
            if ( descr.getIdentifier() != null ) {
                this.template = "<column identifier=\"" + descr.getIdentifier() + "\" object-type=\"" + descr.getObjectType() + "\" >" + processDescrList( descr.getDescrs() )+xmlDump.eol+visitLiteralDescr(literalDescr)+xmlDump.eol+visitFieldBindingDescr(fieldbindingDescr) + xmlDump.eol + "</column>" + xmlDump.eol;
                return template.toString();
            } else {
                this.template = "<column object-type=\"" + descr.getObjectType() + "\" >" + processDescrList( descr.getDescrs() )+xmlDump.eol +visitLiteralDescr(literalDescr)+xmlDump.eol+visitFieldBindingDescr(fieldbindingDescr) + xmlDump.eol+ "</column>" + xmlDump.eol;
                return template.toString();
            }
        } else {
            if ( descr.getIdentifier() != null ) {
                this.template = "<column identifier=\"" + descr.getIdentifier() + "\" object-type=\"" + descr.getObjectType()+"\" >"+xmlDump.eol +visitLiteralDescr(literalDescr)+xmlDump.eol+visitFieldBindingDescr(fieldbindingDescr) + xmlDump.eol+ "</column>" + xmlDump.eol;
                return template.toString();
            } else {
            	this.template = "<column object-type=\"" + descr.getObjectType()+"\" >"+ xmlDump.eol+ visitLiteralDescr(literalDescr)+"\" >"+xmlDump.eol+visitFieldBindingDescr(fieldbindingDescr) + xmlDump.eol+ "</column>" + xmlDump.eol;
            	return template.toString();
            }
        }

    }

    public void visitEvalDescr(final EvalDescr descr) {
        this.template = new String();
        
        this.template = "<eval>" + replaceIllegalChars(descr.getText()) + "</eval>" + xmlDump.eol;
    }

    public void visitExistsDescr(final ExistsDescr descr) {
        this.template = new String();
        if ( descr.getDescrs() != Collections.EMPTY_LIST ) {
            this.template = "<exists>" + processDescrList( descr.getDescrs() ) + "</exists>";
        } else {
            this.template = "<exists> </exists>";
        }
    }

    public String visitFieldBindingDescr(final FieldBindingDescr descr) {
        this.template = new String();
        this.template = "<field-binding field-name=\"" + descr.getFieldName() + "\" identifier=\"" + descr.getIdentifier() + "\" />" + xmlDump.eol;
        return template.toString();
    }

    public void visitFunctionDescr(final FunctionDescr functionDescr) {
        this.template = new String();
        final String parameterTemplate = processParameters( functionDescr.getParameterNames(),
                                                      functionDescr.getParameterTypes() );
        
        this.template = "<function return-type=\"" + functionDescr.getReturnType() + "\" name=\"" + functionDescr.getName() + "\">" + xmlDump.eol + parameterTemplate + "<body>" + xmlDump.eol + replaceIllegalChars(functionDescr.getText()) + xmlDump.eol + "</body>" + xmlDump.eol + "</function>" + xmlDump.eol;
    }

    public String visitLiteralDescr(final LiteralDescr descr) {
        this.template = new String();
        this.template = "<literal field-name=\"" + descr.getFieldName() + "\" evaluator=\"" + getEvaluator( descr.getEvaluator() ) + "\" value=\"" + descr.getText() + "\" />" + xmlDump.eol;
        return template.toString();
    }

    public void visitNotDescr(final NotDescr descr) {
        this.template = new String();
        if ( descr.getDescrs() != Collections.EMPTY_LIST ) {
            this.template = "<not>" + processDescrList( descr.getDescrs() ) + "</not>";
        } else {
            this.template = "<not> </not>";
        }

    }

    public void visitOrDescr(final OrDescr descr) {
        this.template = new String();
        if ( descr.getDescrs() != Collections.EMPTY_LIST ) {
            this.template = "<or>" + processDescrList( descr.getDescrs() ) + "</or>";
        } else {
            this.template = "<or> </or>";
        }
    }

    public void visitPackageDescr(final PackageDescr packageDescr,final ColumnDescr columnDescr,final FieldBindingDescr fieldbindingDescr,final LiteralDescr []literalDescr) {
        final String packageName = packageDescr.getName();
        final String xmlString = "<?xml version=\"1.0\" encoding=\"UTF-8\"?> " + xmlDump.eol + " <package name=\"" + packageName + "\"  " + xmlDump.eol + "\txmlns=\"http://drools.org/drools-3.0\" " + xmlDump.eol + "\txmlns:xs=\"http://www.w3.org/2001/XMLSchema-instance\" " + xmlDump.eol
                           + "\txs:schemaLocation=\"http://drools.org/drools-3.0 drools-3.0.xsd\"> " + xmlDump.eol;
        appendXmlDump( xmlString );
        appendXmlDump( processImportsList( packageDescr.getImports() ) );
        appendXmlDump( processGlobalsMap( packageDescr.getGlobals() ) );
        appendXmlDump( processFunctionsList( packageDescr.getFunctions() ) );
        appendXmlDump( processRules( packageDescr.getRules(),columnDescr,literalDescr,fieldbindingDescr ) );
        appendXmlDump( "</package>" );
    }

    public void visitPredicateDescr(final PredicateDescr descr) {
        this.template = new String();
        this.template = "<predicate field-name=\"" + descr.getFieldName() + "\" identifier=\"" + descr.getDeclaration() + "\" >" + descr.getText() + "</predicate>" + xmlDump.eol;

    }

    public void visitReturnValueDescr(final ReturnValueDescr descr) {
        this.template = new String();
        this.template = "<return-value field-name=\"" + descr.getFieldName() + "\" evaluator=\"" + getEvaluator( descr.getEvaluator() ) + "\" >" + descr.getText() + "</return-value>" + xmlDump.eol;
    }

    public void visitQueryDescr(final QueryDescr descr) {
        this.template = new String();
        this.template = "<query name=\"" + descr.getName() + "\">" + "<lhs>" + processDescrList( descr.getLhs().getDescrs() ) + "</lhs>" + "</query>";
    }

    private String template;

    private String processRules(final List rules,final ColumnDescr columnDescr,final LiteralDescr []literalDescr,final FieldBindingDescr fieldbindingDescr) {
        String ruleList = "";
        int i=0;
        for (final Iterator iterator = rules.iterator(); iterator.hasNext()&& i<literalDescr.length;i++ ) {
            final RuleDescr ruleDescr = (RuleDescr) iterator.next();
            String rule = xmlDump.eol+"<rule name=\"" + ruleDescr.getName() + "\">" + xmlDump.eol;
            final String attribute = processAttribute( ruleDescr.getAttributes() );
            String lhs = "";
            if ( ruleDescr.getLhs().getDescrs() != Collections.EMPTY_LIST ) {
                lhs = "<lhs>"+xmlDump.eol + visitColumnDescr(columnDescr,literalDescr[i],fieldbindingDescr)+xmlDump.eol+"</lhs>"+xmlDump.eol;
            	
            } else {

                lhs = "<lhs> </lhs>";
            }
            
            
            final String rhs = "<rhs>"+xmlDump.eol + replaceIllegalChars(ruleDescr.getConsequence()) + xmlDump.eol + "</rhs>" + xmlDump.eol;
            rule += attribute;
            rule += lhs;
            rule += rhs;
            rule += "</rule>";
            ruleList += rule;
        }

        return ruleList + xmlDump.eol;
    }

    private String processDescrList(final List descr) {
        String descrString = "";
        for ( final Iterator iterator = descr.iterator(); iterator.hasNext(); ) {
            visit( iterator.next() );
            descrString += this.template;
            descrString += xmlDump.eol;
        }
        return descrString + xmlDump.eol;
    }

    private String processFunctionsList(final List functions) {
        String functionList = "";

        for ( final Iterator iterator = functions.iterator(); iterator.hasNext(); ) {
            visit( iterator.next() );
            functionList += this.template;
        }

        return functionList + xmlDump.eol;
    }

    private String processAttribute(final List attributes) {

        String attributeList = "";
        for ( final Iterator iterator = attributes.iterator(); iterator.hasNext(); ) {
            final AttributeDescr attributeDescr = (AttributeDescr) iterator.next();
            visit( attributeDescr );
            attributeList += this.template;
        }
        return attributeList + xmlDump.eol;
    }

    private String processParameters(final List parameterNames,
                                     final List parameterTypes) {
        String paramList = "";
        int i = 0;
        for ( final Iterator iterator = parameterNames.iterator(); iterator.hasNext(); i++ ) {
            final String paramName = (String) iterator.next();
            final String paramType = (String) parameterTypes.get( i );
            final String paramTemplate = "<parameter identifier=\"" + paramName + "\" type=\"" + paramType + "\" />" + xmlDump.eol;
            paramList += paramTemplate;
        }

        return paramList + xmlDump.eol;
    }

    private String processGlobalsMap(final Map globals) {
        String globalList = "";
        for ( final Iterator iterator = globals.keySet().iterator(); iterator.hasNext(); ) {
            final String key = (String) iterator.next();
            final String value = (String) globals.get( key );
            final String globalTemplate = "<global identifier=\"" + key + "\" type=\"" + value + "\" />" + xmlDump.eol;
            globalList += globalTemplate;
        }

        return globalList + xmlDump.eol;
    }

    private String processImportsList(final List imports) {
        String importList = "";

        for ( final Iterator iterator = imports.iterator(); iterator.hasNext(); ) {
            final String importString = (String) iterator.next();
            final String importTemplate = "<import name=\"" + importString + "\" /> " + xmlDump.eol;
            importList += importTemplate;
        }
        return importList + xmlDump.eol;
    }

    private void appendXmlDump(final String temp) {
        this.xmlDumpr.append( temp );
    }

    private String getEvaluator(String eval) {

        eval = eval.replaceAll( "<", "&lt;" );
        eval = eval.replaceAll( ">", "&gt;" );
        return eval;
    }
    
    
    private String replaceIllegalChars(String code) {
    	
    	code = code.replaceAll( "&", "&amp;" );
        code = code.replaceAll( "<", "&lt;" );
        code = code.replaceAll( ">", "&gt;" );  
        return code;
    }
    
}