/*
 * Created on Jan 28, 2005
 *
 */
package net.sf.thingamablog.generator;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;


public class TemplateProcessor
{	
	private static final String LT = "<";
	private static final String GT = ">";
	private static final String END_LT = "</";
	private static final String TAG_LT = LT + '$';
	private static final String TAG_GT = '$' + GT;
	
    public String processTemplate(String tmpl, TemplateContainer root)
	{
        List temps = parseContainers(tmpl, root);
        for(Iterator it = temps.iterator(); it.hasNext();)
		{			
            String curTmp = it.next().toString();
			StringWriter sw = new StringWriter(curTmp.length() * 2);
			
			try
			{
			    writeContainer(curTmp, root, sw);
			}
			catch(IOException ioe){}
			
			StringBuffer buf = sw.getBuffer();
			tmpl = replace(curTmp, buf.substring(0, buf.length()), tmpl);			
		}		
		
        return tmpl;
	}
    
    public void writeContainer(String tmpl, TemplateContainer cont, Writer writer)
    throws IOException
    {		
		cont.initialize(parseAttributes(tmpl, cont));		
		if(cont.isVisible())
		{
		    do
		    {	        
		        String result = processVariables(stripOffTags(tmpl, cont), cont);		       
		        if(cont.prefix() != null)
		            writer.write(cont.prefix());		        
		        writer.write(processContainers(result, cont));		        
		        if(cont.postfix() != null)
		            writer.write(cont.postfix());
		    }
		    while(cont.processAgain());
		}
    }
    
    public List parseContainers(String tmpl, TemplateContainer tc)
    {
		int _pos = 0;
        Vector temps = new Vector();
		String containerStart = LT + tc.getName();
		String closeTag = END_LT + tc.getName() + GT;
		
		while(tmpl.indexOf(containerStart, _pos) > -1)
		{
			int s = tmpl.indexOf(containerStart, _pos);//open tag begin
			if(s < 0)//not found
				continue;
			
			_pos = s + tc.getName().length() + 1; //update pos;
			
			char ch = tmpl.charAt(_pos);
			if(ch != '>' && !Character.isWhitespace(ch))
				continue;   //this isn't the right tag, 
							//it only starts like the one we're looking for
			//System.out.println("After tag = " + t.charAt(pos));
			
			//now we have to find the end ">" of the open tag
			int otEnd = tmpl.indexOf(GT, _pos);
			if(otEnd < 0)//incomplete tag
				continue;		
			
			_pos = otEnd;
			
			int e = tmpl.indexOf(closeTag, _pos);
			if(e < 0)//incomple tag
				continue;
				
			e += closeTag.length();
			_pos = e;
			
			temps.add(tmpl.substring(s, e));
		}
		
		return temps;
    }
	
	private String stripOffTags(String t, TemplateContainer tc)
	{
		int s = t.indexOf(GT); //end of open tag
		int e = t.indexOf(END_LT + tc.getName() + GT);//end of close tag
		if(s < 0 || e < 0 || (s + 1) >= t.length())
			return t;
		return t.substring(s + 1, e);
	}    

	private String processVariables(String tmpl, TemplateContainer tc)
	{
	    List list = tc.getTags();
	    if(list != null)
	    {
	        Iterator it = list.iterator();
	        while(it.hasNext())
	        {
	            TemplateTag tag = (TemplateTag)it.next();
	            tmpl = parseVariable(tmpl, tag, tc.getValueForTag(tag));
	        }
	    }	    
	    return tmpl;
	}
	
	private String processContainers(String tmpl, TemplateContainer tc)
	{
        List list = tc.getContainers();        
        if(list != null)
        {
            Iterator it = list.iterator();
            while(it.hasNext())
            {
                tmpl = processTemplate(tmpl, (TemplateContainer)it.next());	
            }
        }        
        return tmpl;
	}
    
	private String parseVariable(String tmpl, TemplateTag tag, Object obj)
	{		
		int _pos = 0;
		String attribsStart = TAG_LT;
		String attribsEnd = TAG_GT;
		String tagStart = attribsStart + tag.getName();
		
		while((_pos = tmpl.indexOf(tagStart, _pos)) > -1)
		{
			int end = tmpl.indexOf(attribsEnd, _pos) + attribsEnd.length();
			if(end < _pos)//no more
				return tmpl;
			
			String var = tmpl.substring(_pos, end);
			int nameLen = tag.getName().length();
			int normalLen = 
				attribsStart.length() + nameLen + attribsEnd.length();
						
			if(var.length() > normalLen)
			{
				//this tag should have whitespace between the tag name and
				//attribute list because it's longer than
				//the normal, no-attribute, length.
				char c = var.charAt(attribsStart.length() + nameLen);
				if(!Character.isWhitespace(c))
				{				
					//System.out.println(c + " - " + tag.getName() + " - " + var);
					//No whitespace found so
					//this isn't the right tag, it only starts
					//like the one we're looking for, so pass it over
					_pos = end;
					continue;					
				}			
			}
						
			Hashtable ht = parseAttributes(var, tag);
			String val = tag.process(obj, ht);
			tmpl = replace(var, val, tmpl);			
		}
		
		return tmpl;		
	}
    
    private Hashtable parseAttributes(String t, TemplateElement te)
    {
        Hashtable defaults = te.getDefaultAttributes();
        if(defaults == null)//don't allow null attributes
            defaults = new Hashtable();//empty attributes
        Hashtable ht = new Hashtable(defaults);//copy defaults
        
        String attribsStart = getAttribsStart(te) + te.getName();
        String attribsEnd = getAttribsEnd(te);
        int s = t.indexOf(attribsStart, 0);//open tag begin
        if(s == -1)//not found
            return ht;
        
        int e = t.indexOf(attribsEnd, s + attribsStart.length());
        if(e == -1 || e == (s + attribsStart.length()))//no attributes given, use defaults
            return ht;
        
        String attribs = t.substring(s + attribsStart.length(), e).trim();
        //System.out.println(attribs);
        attribs = " " + attribs;//ensure the first attrib has a prefixed space
        char ws[] = {' ', '\n', '\t'};
        
        for(Enumeration eEnum = ht.keys(); eEnum.hasMoreElements();) 
        {
            String key = eEnum.nextElement().toString();
            int p = 0;
            for(int i = 0; i < ws.length; i++)
            {
                p = attribs.indexOf(ws[i] + key);
                if(p != -1)
                    break;
            }           
            if(p == -1)//attribute wasn't found, default will be used
                continue;            
            //skip over the space and attribute name        
            p += key.length() + 1;
            
            try
            {
                //iterate thru whitespace till we hit equals
                while(Character.isWhitespace(attribs.charAt(p++)));             
                if(attribs.charAt(--p) != '=')
                    break;
                //iterate thru whitespace till we hit opening quote
                while(Character.isWhitespace(attribs.charAt(++p)));             
                if(attribs.charAt(p) != '\"')
                    break;
                
                int end = attribs.indexOf("\"", p + 1);//end quote
                if(end == -1)
                    break;//no closing quote
                
                
                //FIXED with trim() this screws up attribs like glue=", "
                //String val = attribs.substring(p, end).trim();
                String val = attribs.substring(p, end);
                //remove quotes 
                if(val.startsWith("\"") /*&& val.length() > 1*/)
                    val = val.substring(1, val.length());
                
                if(val.endsWith("\"") /*&& val.length() > 1*/)
                    val = val.substring(0, val.length() - 1);               
                
                ht.put(key, val);
                //System.out.println(key + " = " +val); 
                
            }
            catch(IndexOutOfBoundsException ex)
            {
                break;
            }
        }
        
        return ht;
    }
	
	private String replace(String var, String val, String tmpl)
	{		
		if(var.equals("") || var == null)
			 return tmpl;
		StringBuffer sb = new StringBuffer(val.length() + tmpl.length());
		sb.append(tmpl);
		while(sb.indexOf(var) != -1 && !var.equals(val))
		{			
			int s = sb.indexOf(var);            
			int e = s + var.length();
			sb.delete(s, e);
			sb.insert(s, val);
		}                
		return sb.substring(0, sb.length());
	}
	
	private String getAttribsStart(TemplateElement te)
	{
	    if(te instanceof TemplateTag)
	        return TAG_LT;
	    return LT;
	}
	
	private String getAttribsEnd(TemplateElement te)
	{
	    if(te instanceof TemplateTag)
	        return TAG_GT;
	    return GT;
	}
}
