package com.planet_ink.siplet.support;
import java.util.*;

public class MXPElement implements Cloneable
{
    public static final int BIT_OPEN=1;
    public static final int BIT_COMMAND=2;
    public static final int BIT_NEEDTEXT=4;
    public static final int BIT_SPECIAL=8;
    public static final int BIT_HTML=16;
    public static final int BIT_NOTSUPPORTED=32;
    public static final int BIT_EATTEXT=64;
    public static final int BIT_DISABLED=128;
    
    private String name="";
    private String definition="";
    private String attributes="";
    private String flag="";
    private String unsupportedParms="";
    private int bitmap=0;
    private Vector parsedAttributes=null;
    private Hashtable attributeValues=null;
    private Hashtable alternativeAttributes=null;
    private Vector userParms=new Vector();
    private boolean basicElement=true;
    
    private int bufInsert=-1;
    
    public MXPElement(String newName,
                      String theDefinition,
                      String theAttributes,
                      String theFlag,
                      int theBitmap)
    {
        super();
        name=newName;
        definition=theDefinition;
        attributes=theAttributes;
        flag=theFlag;
        bitmap=theBitmap;
        if((!isCommand())
        &&(theDefinition.toUpperCase().indexOf("&TEXT;")>=0))
              bitmap=bitmap|BIT_NEEDTEXT;
    }
    
    public MXPElement(String newName,
                      String theDefinition,
                      String theAttributes,
                      String theFlag,
                      int theBitmap,
                      String unsupported)
    {
        super();
        name=newName;
        definition=theDefinition;
        attributes=theAttributes;
        flag=theFlag;
        bitmap=theBitmap;
        if((!isCommand())
        &&(theDefinition.toUpperCase().indexOf("&TEXT;")>=0))
            bitmap=bitmap|BIT_NEEDTEXT;
        unsupportedParms=unsupported;
    }
    public MXPElement copyOf()
    {
        try
        {
            MXPElement E=(MXPElement)this.clone();
            if(E.parsedAttributes!=null) E.parsedAttributes=(Vector)E.parsedAttributes.clone();
            if(E.attributeValues!=null) E.attributeValues=(Hashtable)E.attributeValues.clone();
            if(E.alternativeAttributes!=null) E.alternativeAttributes=(Hashtable)E.alternativeAttributes.clone();
            if(E.userParms!=null) E.userParms=(Vector)E.userParms.clone();
            return E;
        }
        catch(Exception e){};
        return this;
    }

    public String name(){return name;}
    public void setName(String newName){name=newName;}
    public boolean isCommand(){return Util.bset(bitmap,BIT_COMMAND);}
    public boolean isOpen(){return Util.bset(bitmap,BIT_OPEN);}
    public boolean isHTML(){return Util.bset(bitmap,BIT_HTML);}
    public boolean isSpecialProcessor(){return Util.bset(bitmap,BIT_SPECIAL);}
    public boolean isDisabled(){return Util.bset(bitmap,BIT_DISABLED);}
    public boolean isTextEater(){return Util.bset(bitmap,BIT_EATTEXT);}
    public String getDefinition(){return definition;}
    public void setDefinition(String defi){definition=defi;}
    public String getAttributes(){return attributes;}
    public boolean needsText(){return Util.bset(bitmap,BIT_NEEDTEXT);}
    public void setNotBasicElement(){basicElement=false;}
    public boolean isBasicElement(){return basicElement;}
    public boolean isGenerallySupported(){return !Util.bset(bitmap,BIT_NOTSUPPORTED);}
    public void setBitmap(int newBitmap){bitmap=newBitmap;}
    public int getBitmap(){return bitmap;}
    public Vector getUnsupportedParms()
    {
        if((unsupportedParms==null)||(unsupportedParms.trim().length()==0))
            return new Vector();
        return Util.parseSpaces(unsupportedParms,true);
    }
    public void setAttributes(String newAttributes)
    { 
        attributes=newAttributes;
        parsedAttributes=null;
        attributeValues=null;
        alternativeAttributes=null;
    }
    public String getAttributeValue(String tag)
    {
        getParsedAttributes();
        return (String)attributeValues.get(tag.toUpperCase().trim());
    }
    public void setAttributeValue(String tag, String value)
    {
        getParsedAttributes();
        attributeValues.remove(tag);
        if(value!=null)
            attributeValues.put(tag,value);
    }
    
    public synchronized Vector getParsedAttributes()
    {
        if(parsedAttributes!=null) return parsedAttributes;
        parsedAttributes=new Vector();
        attributeValues=new Hashtable();
        alternativeAttributes=new Hashtable();
        StringBuffer buf=new StringBuffer(attributes.trim());
        StringBuffer bit=new StringBuffer("");
        char quotes='\0';
        int i=-1;
        char lastC=' ';
        boolean firstEqual=false;
        while((bit!=null)&&((++i)<buf.length()))
        {
            switch(buf.charAt(i))
            {
            case '=':
                if((!firstEqual)&&(bit.length()>0))
                {
                    String tag=bit.toString().toUpperCase().trim();
                    bit=new StringBuffer("");
                    parsedAttributes.addElement(tag);
                    attributeValues.put(tag,bit);
                }
                else
                    bit.append(buf.charAt(i));
                firstEqual=true;
                break;
            case '\n':
            case '\r':
            case ' ':
            case '\t':
                if(quotes=='\0')
                {
                    if((!firstEqual)&&(bit.length()>0))
                        parsedAttributes.addElement(bit.toString().toUpperCase().trim());
                    bit=new StringBuffer("");
                    firstEqual=false;
                }
                else
                    bit.append(buf.charAt(i));
                break;
            case '"':
            case '\'':
                if(lastC=='\\')
                    bit.append(buf.charAt(i));
                else
                if((lastC=='=')
                ||(quotes!='\0')
                ||((quotes=='\0')&&((lastC==' ')||(lastC=='\t'))))
                {
                    if((quotes!='\0')&&(quotes==buf.charAt(i)))
                    {
                        quotes='\0';
                        if((!firstEqual)&&(bit.length()>0))
                            parsedAttributes.addElement(bit.toString().toUpperCase().trim());
                        bit=new StringBuffer("");
                        firstEqual=false;
                    }
                    else
                    {
                        if(quotes!='\0')
                            bit.append(buf.charAt(i));
                        else
                            quotes=buf.charAt(i);
                    }
                }
                else
                    bit.append(buf.charAt(i));
                break;
            default:
                bit.append(buf.charAt(i));
                break;
            }
            lastC=buf.charAt(i);
        }
        if((!firstEqual)&&(bit.length()>0))
            parsedAttributes.addElement(bit.toString().toUpperCase().trim());
        for(int p=parsedAttributes.size()-1;p>=0;p--)
        {
            String PA=(String)parsedAttributes.elementAt(p);
            StringBuffer VAL=(StringBuffer)attributeValues.get(PA);
            if((VAL!=null)&&(parsedAttributes.contains(VAL.toString())))
            {
                parsedAttributes.removeElementAt(p);
                attributeValues.remove(PA);
                alternativeAttributes.put(PA,VAL.toString());
            }
        }
        return parsedAttributes;
    }
    public String getFlag(){return flag;}
    public Vector getUserParms(){return userParms;}
    public void saveSettings(int insertPoint, Vector theUserParms)
    {
        bufInsert=insertPoint;
        userParms=theUserParms;
    }
    public int getBufInsert(){return bufInsert;}
    public void deleteAttribute(String name)
    {
        getParsedAttributes();
        attributeValues.remove(name.toUpperCase().trim());
    }
    
    public Vector getCloseTags(String desc)
    {
        StringBuffer buf=new StringBuffer(desc);
        Vector tags=new Vector();
        StringBuffer bit=null;
        char quotes='\0';
        int i=-1;
        char lastC=' ';
        while((++i)<buf.length())
        {
            switch(buf.charAt(i))
            {
            case '<':
                if(quotes!='\0')
                    bit=null;
                else
                if(bit!=null)
                {
                    if(MXP.tagDebug){System.out.println("/TAG/CLOSER2S="+Util.toStringList(tags)); System.out.flush();}
                    return tags;
                }
                else
                    bit=new StringBuffer("");
                break;
            case '>':
                if((quotes=='\0')&&(bit!=null)&&(bit.toString().trim().length()>0))
                    tags.add(bit.toString().toUpperCase().trim());
                bit=null;
                break;
            case ' ':
            case '\t':
                if((quotes=='\0')&&(bit!=null)&&(bit.toString().trim().length()>0))
                    tags.add(bit.toString().toUpperCase().trim());
                bit=null;
                break;
            case '"':
            case '\'':
                if(lastC=='\\')
                    bit=null;
                else
                if((quotes!='\0')
                &&(quotes==buf.charAt(i)))
                    quotes='\0';
                else
                if(quotes=='\0')
                    quotes=buf.charAt(i);
                bit=null;
                break;
            default:
                if((bit!=null)&&(Character.isLetterOrDigit(buf.charAt(i))))
                    bit.append(buf.charAt(i));
                else
                    bit=null;
                break;
            }
            lastC=buf.charAt(i);
        }
        if(MXP.tagDebug){System.out.println("/TAG/CLOSERS="+Util.toStringList(tags)); System.out.flush();}
        return tags;
    }
    
    public String getFoldedDefinition(String text)
    {
        Vector aV=getParsedAttributes();
        attributeValues.remove("TEXT");
        attributeValues.put("TEXT",text);
        if((userParms!=null)&&(userParms.size()>0))
        {
            int position=-1;
            String avParm=null;
            String userParm=null;
            for(int u=0;u<userParms.size();u++)
            {
                userParm=((String)userParms.elementAt(u)).toUpperCase().trim();
                int xx=userParm.indexOf("=");
                if((xx>0)&&(alternativeAttributes.containsKey(userParm.substring(0,xx).trim())))
                {
                    String newKey=(String)alternativeAttributes.get(userParm.substring(0,xx).trim());
                    String uu=(String)userParms.elementAt(u);
                    xx=uu.indexOf("=");
                    userParms.setElementAt(newKey+uu.substring(xx),u);
                    userParm=((String)userParms.elementAt(u)).toUpperCase().trim();
                }
                boolean found=false;
                for(int a=0;a<aV.size();a++)
                {
                    avParm=(String)aV.elementAt(a);
                    if((userParm.startsWith(avParm+"="))||(avParm.equals(userParm)))
                    {
                        found=true;
                        if(a>position) position=a;
                        attributeValues.remove(avParm);
                        String val=(avParm.equals(userParm))?"":((String)userParms.elementAt(u)).trim().substring(avParm.length()+1);
                        attributeValues.put(avParm,val);
                        break;
                    }
                }
                if((!found)&&(position<(aV.size()-1)))
                {
                    position++;
                    avParm=(String)aV.elementAt(position);
                    attributeValues.remove(avParm);
                    attributeValues.put(avParm,((String)userParms.elementAt(u)).trim());
                }
            }
        }
        return definition;
    }
}
