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

/* 
Copyright 2000-2006 Bo Zimmerman

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
public class MXP
{
    public final static boolean tagDebug=false;
    public final static boolean tagDebugLong=false;
    public final static boolean entityDebug=false;

    private int defaultMode=0;
    public static final int MODE_LINE_OPEN=0;
    public static final int MODE_LINE_SECURE=1;
    public static final int MODE_LINE_LOCKED=2;
    public static final int MODE_RESET=3;
    public static final int MODE_TEMP_SECURE=4;
    public static final int MODE_LOCK_OPEN=5;
    public static final int MODE_LOCK_SECURE=6;
    public static final int MODE_LOCK_LOCKED=7;
    public static final int MODE_LINE_ROOMNAME=10;
    public static final int MODE_LINE_ROOMDESC=11;
    public static final int MODE_LINE_ROOMEXITS=12;
    public static final int MODE_LINE_WELCOME=19;
    
    private Hashtable elements=new Hashtable();
    private Hashtable tags=new Hashtable();
    private Hashtable entities=new Hashtable();
    private Vector openElements=new Vector();
    public String lastForeground="WH";
    public String lastBackground="WH";
    private boolean eatTextUntilEOLN=false;
    private boolean eatNextEOLN=false;
    private boolean eatAllEOLN=false;
    private StringBuffer responses=new StringBuffer("");
    private StringBuffer jscriptBuffer=new StringBuffer("");
    private Vector gauges=new Vector();
    
    public MXP()
    {
        super();
        initMXP();
    }
    
    public void initMXP()
    {
        elements.clear();
        addElement(new MXPElement("B","<B>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("BOLD","<B>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("STRONG","<B>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("U","<U>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("UNDERLINE","<U>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("I","<I>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("ITALIC","<I>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("S","<S>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("STRIKEOUT","<S>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("EM","<I>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H1","<H1>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H2","<H2>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H3","<H3>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H4","<H4>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H5","<H5>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("H6","<H6>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("HR","<HR>","","",MXPElement.BIT_HTML|MXPElement.BIT_COMMAND));
        addElement(new MXPElement("SMALL","<SMALL>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("TT","<PRE>","","",MXPElement.BIT_HTML));
        addElement(new MXPElement("BR","<BR>","","",MXPElement.BIT_HTML|MXPElement.BIT_COMMAND));
        addElement(new MXPElement("SBR","&nbsp;","","",MXPElement.BIT_HTML|MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("P","","","",MXPElement.BIT_HTML|MXPElement.BIT_SPECIAL)); // special done
        addElement(new MXPElement("C","<FONT COLOR=&fore; BACK=&back;>","FORE BACK","",0));
        addElement(new MXPElement("COLOR","<FONT COLOR=&fore; BACK=&back;>","FORE BACK","",0));
        addElement(new MXPElement("HIGH","","","",MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("H","","","",MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("FONT","<FONT STYLE=\"color: &color;;background-color: &back;;font-family: &face;;font-size: &size;;\">","FACE SIZE COLOR BACK STYLE","",MXPElement.BIT_SPECIAL));
        addElement(new MXPElement("NOBR","","","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND));  // special done
        addElement(new MXPElement("A","<A STYLE=\"&lcc;\" ONMOUSEOVER=\"&onmouseover;\" ONCLICK=\"&onclick;\" HREF=\"&href;\" TITLE=\"&hint;\">","HREF HINT EXPIRE TITLE=HINT STYLE ONMOUSEOUT ONMOUSEOVER ONCLICK","",0,"EXPIRE"));
        addElement(new MXPElement("SEND","<A STYLE=\"&lcc;\" HREF=\"&href;\" ONMOUSEOUT=\"delayhidemenu();\" ONCLICK=\"&onclick;\" TITLE=\"&hint;\">","HREF HINT PROMPT EXPIRE STYLE","",MXPElement.BIT_SPECIAL,"EXPIRE")); // special done
        addElement(new MXPElement("EXPIRE","","NAME","",MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("VERSION","","","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND)); // special done
        addElement(new MXPElement("SUPPORT","","","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND)); // special done
        addElement(new MXPElement("GAUGE","","ENTITY MAX CAPTION COLOR","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND));
        addElement(new MXPElement("STAT","","ENTITY MAX CAPTION","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("FRAME","","NAME ACTION TITLE INTERNAL ALIGN LEFT TOP WIDTH HEIGHT SCROLLING FLOATING","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("DEST","","NAME","",MXPElement.BIT_SPECIAL|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("DESTINATION","","NAME","",MXPElement.BIT_SPECIAL|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("RELOCATE","","URL PORT","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("USER","","","",MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("PASSWORD","","","",MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("IMAGE","<IMG SRC=&url;&fname; HEIGHT=&h; WIDTH=&w; ALIGN=&align;>","FNAME URL T H W HSPACE VSPACE ALIGN ISMAP","",MXPElement.BIT_COMMAND,"HSPACE VSPACE ISMAP"));
        addElement(new MXPElement("IMG","<IMG SRC=&src; HEIGHT=&height; WIDTH=&width; ALIGN=&align;>","SRC HEIGHT=70 WIDTH=70 ALIGN","",MXPElement.BIT_COMMAND));
        addElement(new MXPElement("FILTER","","SRC DEST NAME","",MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("SCRIPT","","","",MXPElement.BIT_COMMAND|MXPElement.BIT_NOTSUPPORTED));
        addElement(new MXPElement("ENTITY","","NAME VALUE DESC PRIVATE PUBLISH DELETE ADD","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND,"PRIVATE PUBLISH ADD")); // special done
        addElement(new MXPElement("EN","","NAME VALUE DESC PRIVATE PUBLISH DELETE ADD","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND,"PRIVATE PUBLISH ADD")); // special done
        addElement(new MXPElement("TAG","","INDEX WINDOWNAME FORE BACK GAG ENABLE DISABLE","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND,"WINDOWNAME"));
        addElement(new MXPElement("VAR","","NAME DESC PRIVATE PUBLISH DELETE ADD REMOVE","",MXPElement.BIT_SPECIAL,"PRIVATE PUBLISH ADD REMOVE")); // special done
        addElement(new MXPElement("V","","NAME DESC PRIVATE PUBLISH DELETE ADD REMOVE","",MXPElement.BIT_SPECIAL,"PRIVATE PUBLISH ADD REMOVE")); // special done
        addElement(new MXPElement("ELEMENT","","NAME DEFINITION ATT TAG FLAG OPEN DELETE EMPTY","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND)); // special done
        addElement(new MXPElement("EL","","NAME DEFINITION ATT TAG FLAG OPEN DELETE EMPTY","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND)); // special done
        addElement(new MXPElement("ATTLIST","","NAME ATT","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND));
        addElement(new MXPElement("AT","","NAME ATT","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND));
        addElement(new MXPElement("SOUND","!!SOUND(&fname; V=&v; L=&l; P=&p; T=&t; U=&u;)","FNAME V=100 L=1 P=50 T U","",MXPElement.BIT_COMMAND));
        addElement(new MXPElement("MUSIC","!!MUSIC(&fname; V=&v; L=&l; P=&p; T=&t; U=&u;)","FNAME V=100 L=1 P=50 T U","",MXPElement.BIT_COMMAND));
        //-------------------------------------------------------------------------
        entities.clear();
        entities.put("nbsp",new MXPEntity("nbsp","&nbsp;"));
        entities.put("lt",new MXPEntity("lt","&lt;"));
        entities.put("gt",new MXPEntity("gt","&gt;"));
        entities.put("quot",new MXPEntity("quot","&quot;"));
        entities.put("amp",new MXPEntity("amp","&amp;"));
    }
    
    public void addElement(MXPElement E)
    {
        elements.put(E.name(),E);
    }
    
    public String getAnyResponses()
    {
        synchronized(responses)
        {
            if(responses.length()==0) return "";
            String s=responses.toString();
            responses.setLength(0);
            return s;
        }
    }
    
    public String getAnyJScript()
    {
        synchronized(jscriptBuffer)
        {
            if(jscriptBuffer.length()==0) return "";
            String s=jscriptBuffer.toString();
            jscriptBuffer.setLength(0);
            return s;
        }
    }
    
    private int mode=0;
    private int mode(){return mode;}
    private void setMode(int newMode){mode=newMode;}
    private int setModeAndExecute(int newMode, StringBuffer buf, int i)
    {
        setMode(newMode); 
        return executeMode(buf,i);
    }
    
    private int executeMode(StringBuffer buf, int i)
    {
        switch(mode())
        {
        case MODE_RESET:
            defaultMode=MODE_LINE_OPEN;
            mode=defaultMode;
            return closeAllTags(buf,i);
        case MODE_LOCK_OPEN:
        case MODE_LOCK_SECURE:
        case MODE_LOCK_LOCKED:
            defaultMode=mode;
            break;
        }
        return 0;
    }
    
    public int newlineDetected(StringBuffer buf, int i, boolean[] eatEOL)
    {
        if((mode()==MXP.MODE_LINE_LOCKED)||(mode()==MXP.MODE_LOCK_LOCKED))
        {
            eatEOL[0]=false;
            return 0;
        }
        eatEOL[0]=eatNextEOLN;
        eatNextEOLN=eatAllEOLN;
        if(eatTextUntilEOLN)
        {
            eatTextUntilEOLN=false;
            eatEOL[0]=true;
        }
        switch(mode())
        {
        case MODE_LINE_OPEN:
        {
            int ret=closeAllTags(buf,i);
            setModeAndExecute(defaultMode,buf,i);
            return ret;
        }
        case MODE_LINE_SECURE:
        case MODE_LINE_LOCKED:
        case MODE_TEMP_SECURE:
        {
            int ret=closeAllTags(buf,i);
            setModeAndExecute(defaultMode,buf,i);
            return ret;
        }
        }
        return 0;
    }

    // does not close Secure tags -- they are never ever closed
    private int closeAllTags(StringBuffer buf, int i)
    {
        MXPElement E=null;
        for(int x=openElements.size()-1;x>=0;x--)
        {
            E=(MXPElement)openElements.elementAt(x);
            if(E.isOpen())
            {
                String close=closeTag(E);
                if(close.length()>0)
                    buf.insert(i,close+">");
                openElements.removeElementAt(x);
            }
        }
        return 0;
    }
    
    public boolean isUIonHold()
    {
        if((mode()==MXP.MODE_LINE_LOCKED)||(mode()==MXP.MODE_LOCK_LOCKED))
            return false;
        MXPElement E=null;
        for(int i=0;i<openElements.size();i++)
        {
            E=(MXPElement)openElements.elementAt(i);
            if(E.needsText())
                return true;
        }
        return false;
    }
    
    private String closeTag(MXPElement E)
    {
        Vector endTags=E.getCloseTags(E.getDefinition());
        StringBuffer newEnd=new StringBuffer("");
        for(int e=endTags.size()-1;e>=0;e--)
            if(elements.containsKey((((String)endTags.elementAt(e))).toUpperCase().trim()))
                newEnd.append("</"+((String)endTags.elementAt(e)).toUpperCase().trim());
        return newEnd.toString();
    }
    
    public int escapeTranslate(String escapeString, StringBuffer buf, int i)
    {
        if(escapeString.endsWith("z"))
        {
            buf.delete(i,i+escapeString.length()+2);
            int code=Util.s0_int(escapeString.substring(0,escapeString.length()-1));
            if(code<20)
                return setModeAndExecute(code,buf,i);
            else
            if(code<100)
            {
                MXPElement replace=(MXPElement)tags.get(new Integer(code));
                if((replace!=null)&&(!replace.isDisabled()))
                {
                    buf.insert(i,replace.getFoldedDefinition(""));
                    if(replace.isTextEater()) 
                        eatTextUntilEOLN=true;
                }
            }
            return -1;
        }
        return escapeString.length();
    }

    public boolean eatTextUntilNextEOLN(){return eatTextUntilEOLN;}
    
    private void processAnyEntities(StringBuffer buf, MXPElement currentElement)
    {
        int i=0;
        while(i<buf.length())
        {
            switch(buf.charAt(i))
            {
            case '&':
            {
                 int x=processEntity(buf,i,currentElement,false);
                 if(x==Integer.MAX_VALUE) return;
                 i+=x;
                 break;
            }
            }
            i++;
        }
    }

    private String substr(String buf, int start, int end)
    {return substr(new StringBuffer(buf),start,end);}
    private String substr(StringBuffer buf, int start, int end)
    {
        if(start<0) return "?";
        if(end<start) end=start+80;
        if(end>buf.length()) end=buf.length();
        String s=buf.substring(start,end);
        s=Util.replaceAll(s,"\n","\\n");
        s=Util.replaceAll(s,"\r","\\r");
        return s;
    }
    
    public int processTag(StringBuffer buf, int i)
    {
        if((mode()==MXP.MODE_LINE_LOCKED)||(mode()==MXP.MODE_LOCK_LOCKED))
        {
            buf.setCharAt(i,'&');
            buf.insert(i+1,"lt;");
            return 3;
        }
        
        // first step is to parse the motherfather
        // if we can't parse it, we convert the < char at i into &lt;
        // remember, incomplete tags should nodify the main filterdude
        Vector parts=new Vector();
        int oldI=i;
        int oldOldI=i;
        char lastC=' ';
        char quotes='\0';
        StringBuffer bit=new StringBuffer("");
        
        //allowing the ! and / as a second char in a tag is an EXCEPTION! 
        if(((i+1)<buf.length())&&((buf.charAt(i+1)=='!')||(buf.charAt(i+1)=='/')))
        {
            i++;
            bit.append(buf.charAt(i));
        }
        if(tagDebug){System.out.println("/***:::!!!TAG/"+oldI+"/"+substr(buf,oldI,oldI+10)); System.out.flush();}
        if(tagDebugLong){System.out.println("/TAG>"+substr(buf,0,buf.length())); System.out.flush();}
        while((bit!=null)&&((++i)<buf.length()))
        {
            switch(buf.charAt(i))
            {
            case '\n':
            case '\r':
                buf.setCharAt(oldI,'&');
                buf.insert(oldI+1,"lt;");
                if(tagDebug){System.out.println("/TAG*/****/Tag has CR!!!!"); System.out.flush();}
                return 3;
            case ' ':
            case '\t':
                if(quotes=='\0')
                {
                    if(bit.length()>0) 
                        parts.addElement(bit.toString());
                    bit.setLength(0);
                }
                else
                    bit.append(buf.charAt(i));
                break;
            case '"':
            case '\'':
                if(lastC=='\\')
                {
                    if((quotes!='\0')
                    ||(bit.length()>0))
                        bit.append(buf.charAt(i));
                    else
                    {
                        // DANGER WILL ROBINSON! DANGER!
                        buf.setCharAt(oldI,'&');
                        buf.insert(oldI+1,"lt;");
                        if(tagDebug){System.out.println("/TAG*/****/Tag has wierd quote!!!!"); System.out.flush();}
                        return 3;
                    }
                }
                else
                if((lastC=='=')
                ||(quotes!='\0')
                ||((quotes=='\0')&&((lastC==' ')||(lastC=='\t'))))
                {
                    if((quotes!='\0')&&(quotes==buf.charAt(i)))
                    {
                        quotes='\0';
                        parts.addElement(bit.toString());
                        bit.setLength(0);
                    }
                    else
                    {
                        if(quotes!='\0')
                            bit.append(buf.charAt(i));
                        else
                            quotes=buf.charAt(i);
                    }
                }
                else
                    bit.append(buf.charAt(i));
                break;
            case '<':
                if(quotes!='\0')
                    bit.append(buf.charAt(i));
                else
                {
                    // argh! abort! abort!
                    buf.setCharAt(oldI,'&');
                    buf.insert(oldI+1,"lt;");
                    if(tagDebug){System.out.println("/TAG*/****/Tag has REOPEN!!!!"); System.out.flush();}
                    return 3;
                }
                break;
            case '>':
                if(quotes!='\0')
                    bit.append(buf.charAt(i));
                else
                {
                    if(bit.length()>0) 
                        parts.add(bit.toString());
                    bit=null;
                }
                break;
            default:
                if((quotes!='\0')
                ||(Character.isLetter(buf.charAt(i)))
                ||(bit.length()>0))
                    bit.append(buf.charAt(i));
                else
                {
                    // DANGER WILL ROBINSON! DANGER!
                    buf.setCharAt(oldI,'&');
                    buf.insert(oldI+1,"lt;");
                    if(tagDebug){System.out.println("/TAG*/****/Char is illegal!!!"); System.out.flush();}
                    return 3;
                }
                break;
            }
            lastC=buf.charAt(i);
        }
        // never hit the end, so let papa know
        if((i>=buf.length())||(buf.charAt(i)!='>'))
        {
            if(tagDebug){System.out.println("/TAG*/****/Tag is unclosed!!!!"); System.out.flush();}
            return Integer.MAX_VALUE;
        }
        int endI=i+1;
        
        //nothing doin
        String tag=(parts.size()>0)?((String)parts.firstElement()).toUpperCase().trim():"";
        String oldString=buf.substring(oldI,endI);
        if(tag.startsWith("!")) tag=tag.substring(1);
        boolean endTag=tag.startsWith("/");
        if(endTag)tag=tag.substring(1);
        tag=tag.toUpperCase().trim();
        if((tag.length()==0)||(!elements.containsKey(tag)))
        {
            buf.setCharAt(oldI,'&');
            buf.insert(oldI+1,"lt;");
            if(tagDebug){System.out.println("/TAG*/****/Tag is unknown!!!!"); System.out.flush();}
            return 3;
        }
        MXPElement E=(MXPElement)elements.get(tag);
        String text="";
        if(endTag)
        {
            MXPElement troubleE=null;
            int foundAt=-1;
            for(int x=openElements.size()-1;x>=0;x--)
            {
                E=(MXPElement)openElements.elementAt(x);
                if(E.name().equals(tag))
                {
                    foundAt=x;
                    openElements.removeElementAt(x);
                    break;
                }
                if(E.needsText())
                    troubleE=E;
            }
            // as of this moment, we need the telnet parser
            // to back up one character so continue.
            buf.delete(oldI,endI);
            if(foundAt<0) 
            {
                if(tagDebug){System.out.println("/TAG*/****/Closed tag never opened!!!"); System.out.flush();}
                return -1;
            }
            // a close tag of an mxp element always erases an 
            //**INTERIOR** needstext element
            if(troubleE!=null)
                openElements.removeElement(troubleE);
            String close=closeTag(E);
            if(tagDebug){System.out.println("/TAG/ENDENTITY="+E.name()+"/CLOSE="+close); System.out.flush();}
            if(close.length()>0)
                buf.insert(oldI,close.toString()+">");
            if((tagDebug)&&(!E.needsText()))
            {
                if(tagDebugLong)
                    System.out.println("/TAG>"+substr(buf,0,buf.length()));
                System.out.println("/TAG/END/!2!/"+substr(buf,oldI+1+close.length(),oldI+40));
                System.out.flush();
            }
            if(E.needsText())
            {
                text=buf.substring(E.getBufInsert(),oldI);
                text=Util.stripBadHTMLTags(Util.replaceAll(text,"&nbsp;"," "));
                oldI=E.getBufInsert();
                if(tagDebug){System.out.println("/TAG/END/text="+substr(text,0,100)); System.out.flush();}
            }
            else
            if(E.isHTML())
            {
                if(E.isSpecialProcessor())
                    specialProcessorElements(E,true);
                if(tagDebug){System.out.println("/TAG*/END/3/adji=!2.5!/"+(close.length()-1)); System.out.flush();}
                return close.length();
            }
            else
            if(close.equals("</"+E.name()))
            {
                if(tagDebug){System.out.println("/TAG*/END/4/adji=!2.5!/"+(close.length()-1)); System.out.flush();}
                return close.length();
            }
            else
            if(E.getBufInsert()<oldI)
            {
                if(tagDebug){System.out.println("/TAG/END/5/adji="+(-((oldI-E.getBufInsert())+1))); System.out.flush();}
                if(tagDebug){System.out.println("/TAG*/END/6/willbe="+substr(buf,oldI-((oldI-E.getBufInsert())),oldI+80)); System.out.flush();}
                return -((oldI-E.getBufInsert())+1);
            }
            else
            {
                if(tagDebug){System.out.println("/TAG*/END/6/adji=!2.5!/"+(close.length()-1)); System.out.flush();}
                return close.length();
            }
        }
        else
        {
            E=E.copyOf();
            parts.removeElementAt(0); // because the TAG itself is 0
            E.saveSettings(oldI,parts);
            if(!E.isCommand())
                openElements.addElement(E);
            buf.delete(oldI,endI);
            if(tagDebug){System.out.println("/TAG/ENTITY="+E.name()); System.out.flush();}
            if(E.needsText())
            {
                if(tagDebugLong){System.out.println("/TAG>"+substr(buf,0,buf.length())); System.out.flush();}
                if(tagDebug){System.out.println("/TAG*/Entity needs text, so purge and look for close."); ; System.out.flush();}           
                return -1; // we want it to continue to look for closing tag 
            }
        }
        String totalDefinition=E.getFoldedDefinition(text);
        if((endTag)&&(!E.isCommand())&&(E.getFlag()!=null)&&(E.getFlag().length()>0))
        {
            String f=E.getFlag().trim();
            if(f.toUpperCase().startsWith("SET "))
                f=f.substring(4).trim();
            modifyEntity(f,text);
        }
        
        if(E.isSpecialProcessor())
            specialProcessorElements(E,endTag);
        if((E.isHTML())
        ||(totalDefinition.equalsIgnoreCase(oldString)))
        {
            buf.insert(oldI,totalDefinition);
            if(tagDebugLong){System.out.println("/TAG>"+substr(buf,0,buf.length())); System.out.flush();}
            if((endTag)&&(oldI<oldOldI))
            {
                if(tagDebug){ System.out.println("/TAG*/ENDEND1/"+substr(buf,(oldOldI+1+(-((oldOldI-oldI)+1))),oldOldI+80)); System.out.flush();}
                return -((oldOldI-oldI)+1);
            }
            if(tagDebug){ System.out.println("/TAG*/THEEND1/"+substr(buf,(oldOldI+(totalDefinition.length()-1))+1,oldOldI+80)); System.out.flush();}
            return totalDefinition.length()-1;
        }
        StringBuffer def=new StringBuffer(totalDefinition);
        processAnyEntities(def,E);
        buf.insert(oldI,def.toString());
        if(tagDebugLong){System.out.println("/TAG/"+substr(buf,0,buf.length())); System.out.flush();}
        if((endTag)&&(oldI<oldOldI)) 
        {
            if(tagDebug){ System.out.println("/TAG*/ENDEND2/"+oldI+"/"+oldOldI+"/"+substr(buf,oldOldI+1+(-((oldOldI-oldI)+1)),oldOldI+80)); System.out.flush();}
            return -((oldOldI-oldI)+1);
        }
        if((def.toString().equalsIgnoreCase(oldString))
        ||(E.name().toUpperCase().trim().equals(getFirstTag(def.toString().trim()))))
        {
            if(tagDebug){ System.out.println("/TAG*/THEEND2/"+substr(buf,oldOldI+1+(def.toString().length()-1),oldOldI+80)); System.out.flush();}
            return def.toString().length()-1;
        }
        if(tagDebug){ System.out.println("/TAG*/THEEND3/"+substr(buf,(oldOldI-1)+1,oldOldI+80)); System.out.flush();}
        return -1;
    }
    
    public String getFirstTag(String s)
    {
        if(!s.startsWith("<"))
            return "";
        int x=s.indexOf(" ");
        if(x<0)x=s.indexOf(">");
        if(x<0) return "";
        return s.substring(1,x).toUpperCase().trim();
    }
    
    private void specialProcessorElements(MXPElement E, boolean endTag)
    {
        if(E.name().equals("FONT"))
        {
            String style=E.getAttributeValue("STYLE");
            String color=E.getAttributeValue("COLOR");
            String back=E.getAttributeValue("BACK");
            String face=E.getAttributeValue("FACE");
            String size=E.getAttributeValue("SIZE");
            if((style!=null)
            &&(color==null)
            &&(back==null)
            &&(face==null)
            &&(size==null))
            {
                String s=null;
                String v=null;
                while(style.length()>0)
                {
                    int x=style.indexOf(";");
                    if(x>=0)
                    {
                        s=style.substring(0,x).trim();
                        style=style.substring(x+1).trim();
                    }
                    else
                    {
                        s=style.trim();
                        style="";
                    }
                    int y=s.indexOf(":");
                    if(y>=0)
                    {
                        v=s.substring(y+1);
                        s=s.substring(0,y);
                        if(s.equalsIgnoreCase("color"))
                            E.setAttributeValue("COLOR",v);
                        else
                        if(s.equalsIgnoreCase("background-color"))
                            E.setAttributeValue("BACK",v);
                        else
                        if(s.equalsIgnoreCase("font-size"))
                            E.setAttributeValue("SIZE",v);
                        else
                        if(s.equalsIgnoreCase("font-family"))
                            E.setAttributeValue("FACE",v);
                    }
                }
                E.setAttributeValue("STYLE",null);
            }
        }
        else
        if(E.name().equals("NOBR"))
            eatNextEOLN=true;
        else
        if(E.name().equals("P"))
        {
            if(endTag)
            {
                eatAllEOLN=false;
                eatNextEOLN=false;
            }
            else
            {
                eatAllEOLN=true;
                eatNextEOLN=true;
            }
        }
        else
        if(E.name().equals("SEND"))
        {
            String prompt=E.getAttributeValue("PROMPT");
            if((prompt!=null)&&(prompt.length()>0))
                return;
            if(prompt==null) prompt="false"; else prompt="true";
            E.setAttributeValue("PROMPT",prompt);
            String href=E.getAttributeValue("HREF");
            String hint=E.getAttributeValue("HINT");
            if((href==null)||(href.trim().length()==0)) href="alert('Nothing done.');";
            if((hint==null)||(hint.trim().length()==0)) hint="Click here!";
            hint=Util.replaceAllIgnoreCase(hint,"RIGHT-CLICK","click");
            hint=Util.replaceAllIgnoreCase(hint,"RIGHT-MOUSE","click mouse");
            E.setAttributeValue("ONCLICK","");
            E.setAttributeValue("HREF","");
            E.setAttributeValue("HINT","");
            Vector hrefV=Util.parsePipes(href,true);
            Vector hintV=Util.parsePipes(hint,true);
            if(hrefV.size()==1)
            {
                href=Util.replaceAll(((String)hrefV.firstElement()),"'","\\'");
                E.setAttributeValue("HREF","javascript:addToPrompt('"+href+"',"+prompt+")");
                if(hintV.size()>1) hint=(String)hintV.firstElement();
                E.setAttributeValue("HINT",hint);
            }
            else
            if(hintV.size()>hrefV.size())
            {
                E.setAttributeValue("HINT",((String)hintV.firstElement()));
                hintV.removeElementAt(0);
                E.setAttributeValue("HREF","javascript:goDefault(0);");
                StringBuffer newHint=new StringBuffer("");
                for(int i=0;i<hintV.size();i++)
                {
                    newHint.append((String)hintV.elementAt(i));
                    if(i<(hintV.size()-1)) newHint.append("|");
                }
                href=Util.replaceAll(href,"'","\\'");
                hint=Util.replaceAll(newHint.toString(),"'","\\'");
                E.setAttributeValue("ONCLICK","return dropdownmenu(this, event, getSendMenu(this,'"+href+"','"+hint+"','"+prompt+"'), '200px');");
            }
            else
            {
                E.setAttributeValue("HINT","Click to open menu");
                E.setAttributeValue("HREF","javascript:goDefault(0);");
                href=Util.replaceAll(href,"'","\\'");
                hint=Util.replaceAll(hint,"'","\\'");
                E.setAttributeValue("ONCLICK","return dropdownmenu(this, event, getSendMenu(this,'"+href+"','"+hint+"','"+prompt+"'), '200px');");
            }
        }
        else
        if(E.name().equals("ELEMENT")||E.name().equals("EL"))
        {
            String name=E.getAttributeValue("NAME");
            String definition=E.getAttributeValue("DEFINITION");
            String attributes=E.getAttributeValue("ATT");
            String tag=E.getAttributeValue("TAG");
            String flags=E.getAttributeValue("FLAG");
            String OPEN=E.getAttributeValue("OPEN");
            String DELETE=E.getAttributeValue("DELETE");
            String EMPTY=E.getAttributeValue("EMPTY");
            if(name==null) return;
            if((DELETE!=null)&&(elements.containsKey(name)))
            {
                E=(MXPElement)elements.get(name);
                if(E.isOpen())
                    elements.remove(name);
                return;
            }
            if(definition==null) definition="";
            if(attributes==null) attributes="";
            int bitmap=0;
            if(OPEN!=null) bitmap|=MXPElement.BIT_OPEN;
            if(EMPTY!=null) bitmap|=MXPElement.BIT_COMMAND;
            MXPElement L=new MXPElement(name.toUpperCase().trim(),definition,attributes,flags,bitmap);
            L.setNotBasicElement();
            elements.remove(L.name());
            elements.put(L.name(),L);
            if((tag!=null)&&(Util.isInteger(tag))&&(Util.s_int(tag)>19)&&(Util.s_int(tag)<100))
            {
                int tagNum=Util.s_int(tag);
                if(tags.containsKey(new Integer(tagNum))) tags.remove(new Integer(tagNum));
                tags.put(new Integer(tagNum),L);
            }
            return;
        }
        else
        if(E.name().equals("ENTITY")||E.name().equals("EN"))
        {
            String name=E.getAttributeValue("NAME");
            String value=E.getAttributeValue("VALUE");
            //String desc=E.getAttributeValue("DESC");
            //String PRIVATE=E.getAttributeValue("PRIVATE");
            //String PUBLISH=E.getAttributeValue("PUBLISH");
            String DELETE=E.getAttributeValue("DELETE");
            String REMOVE=E.getAttributeValue("REMOVE");
            String ADD=E.getAttributeValue("ADD");
            if((name==null)||(name.length()==0)) return;
            if(DELETE!=null)
            {
                entities.remove(name);
                return;
            }
            if(REMOVE!=null)
            {
                // whatever a string list is (| separated things) this removes it
            }
            else
            if(ADD!=null)
            {
                // whatever a string list is (| separated things) this removes it
            }
            else
                modifyEntity(name,value);
            return;
        }
        else
        if((E.name().equals("VAR")||E.name().equals("V"))&&(endTag))
        {
            String name=E.getAttributeValue("NAME");
            //String PRIVATE=E.getAttributeValue("PRIVATE");
            //String PUBLISH=E.getAttributeValue("PUBLISH");
            String DELETE=E.getAttributeValue("DELETE");
            String REMOVE=E.getAttributeValue("REMOVE");
            String VALUE=E.getAttributeValue("TEXT");
            if(VALUE==null) VALUE="";
            String ADD=E.getAttributeValue("ADD");
            if((name==null)||(name.length()==0)) return;
            if(DELETE!=null)
            {
                entities.remove(name);
                return;
            }
            if(REMOVE!=null)
            {
                // whatever a string list is (| separated things) this removes it
            }
            else
            if(ADD!=null)
            {
                // whatever a string list is (| separated things) this removes it
            }
            else
                modifyEntity(name,VALUE);
            return;
        }
        else
        if(E.name().equalsIgnoreCase("VERSION"))
            responses.append("\033[1z<VERSION MXP=1.0 STYLE=1.0 CLIENT=Siplet VERSION="+TelnetFilter.getSipletVersion()+" REGISTERED=NO>\n");
        else
        if(E.name().equalsIgnoreCase("GAUGE"))
        {
            String ENTITY=E.getAttributeValue("ENTITY");
            String MAX=E.getAttributeValue("MAX");
            if((ENTITY==null)||(MAX==null)) return;
            ENTITY=ENTITY.toLowerCase();
            MAX=MAX.toLowerCase();
            String CAPTION=E.getAttributeValue("CAPTION");
            if(CAPTION==null) CAPTION="";
            String COLOR=E.getAttributeValue("COLOR");
            if(COLOR==null) COLOR="WHITE";
            String initEntity=getEntityValue(ENTITY,null);
            int initValue=0;
            if((initEntity!=null)&&(Util.isInteger(initEntity))) 
                initValue=Util.s_int(initEntity);
            String maxEntity=getEntityValue(MAX,null);
            int maxValue=100;
            if((maxEntity!=null)&&(Util.isInteger(maxEntity))) 
                maxValue=Util.s_int(maxEntity);
            if(maxValue<initValue)  maxValue=(initValue<=0)?100:initValue;
            if(initValue>0) initValue=(int)Math.round(Util.mul(100.0,initValue/maxValue));
            synchronized(jscriptBuffer)
            {
                jscriptBuffer.append("createGauge('"+ENTITY+"','"+CAPTION+"','"+COLOR+"',"+initValue+","+maxValue+");");
                String[] gauge=new String[2];
                gauge[0]=ENTITY;
                gauge[1]=MAX;
                gauges.addElement(gauge);
            }
        }
        else
        if(E.name().equalsIgnoreCase("ATTLIST")||E.name().equalsIgnoreCase("ATT"))
        {
            String name=E.getAttributeValue("NAME");
            String value=E.getAttributeValue("ATT");
            if((name==null)||(value==null)) return;
            MXPElement E2=(MXPElement)elements.get(name.toUpperCase().trim());
            if(E2==null) return;
            E2.setAttributes(value);
        }
        else
        if(E.name().equalsIgnoreCase("SUPPORT"))
        {
            StringBuffer supportResponse=new StringBuffer("");
            Vector V=E.getUserParms();
            if((V==null)||(V.size()==0))
            {
                for(Enumeration e=elements.elements();e.hasMoreElements();)
                {
                    MXPElement E2=(MXPElement)e.nextElement();
                    if(!E2.isBasicElement()) continue;
                    Vector unsupportedParms=E2.getUnsupportedParms();
                    if(!E2.isGenerallySupported())
                        supportResponse.append(" -"+E2.name());
                    else
                    {
                        supportResponse.append(" +"+E2.name());
                        if(unsupportedParms.size()>0)
                        {
                            for(int x=0;x<unsupportedParms.size();x++)
                                supportResponse.append(" -"+E2.name()+"."+((String)unsupportedParms.elementAt(x)));
                        }
                    }
                }
            }
            else
            for(int v=0;v<V.size();v++)
            {
                String request=((String)V.elementAt(v)).trim().toUpperCase();
                if(request.startsWith("\"")) request=request.substring(1).trim();
                if(request.endsWith("\"")) request=request.substring(0,request.length()-1).trim();
                if(request.startsWith("\'")) request=request.substring(1).trim();
                if(request.endsWith("\'")) request=request.substring(0,request.length()-1).trim();
                int x=request.indexOf(".");
                String tag=request;
                String parm="";
                if(x>0)
                {
                    tag=request.substring(0,x).trim();;
                    parm=request.substring(x+1).trim();
                }
                MXPElement RE=(MXPElement)elements.get(tag);
                if((RE==null)
                ||(!RE.isGenerallySupported()))
                {
                    if((parm.length()>0)&&(!parm.equals("*")))
                        supportResponse.append(" -"+tag+"."+parm);
                    else
                        supportResponse.append(" -"+tag);
                    continue;
                }
                if(parm.length()==0)
                {
                    supportResponse.append(" +"+tag);
                    continue;
                }
                Vector unsupportedParms=RE.getUnsupportedParms();
                Vector allAttributes=RE.getParsedAttributes();
                if(parm.equals("*"))
                {
                    for(int a=0;a<allAttributes.size();a++)
                    {
                        String att=(String)allAttributes.elementAt(a);
                        if(!unsupportedParms.contains(att))
                            supportResponse.append(" +"+tag+"."+att);
                    }
                    continue;
                }
                if((unsupportedParms.contains(parm))
                ||(!allAttributes.contains(parm)))
                    supportResponse.append(" -"+tag+"."+parm);
                else
                    supportResponse.append(" +"+tag+"."+parm);
            }
            responses.append("\033[1z<SUPPORTS"+supportResponse.toString()+">\n");
        }
        else
        if(E.name().equals("TAG"))
        {
            addElement(new MXPElement("TAG","","INDEX WINDOWNAME FORE BACK GAG ENABLE DISABLE","",MXPElement.BIT_SPECIAL|MXPElement.BIT_COMMAND));
            //String window=E.getAttributeValue("WINDOWNAME");
            String index=E.getAttributeValue("INDEX");
            if(!Util.isNumber(index)) return;
            int number=Util.s_int(index);
            if((number<20)||(number>99)) return;
            String foreColor=E.getAttributeValue("FORE");
            if(foreColor==null) foreColor="";
            String backColor=E.getAttributeValue("BACK");
            if(backColor==null) backColor="";
            String gag=E.getAttributeValue("GAG");
            String enable=E.getAttributeValue("ENABLE");
            String disable=E.getAttributeValue("DISABLE");
            StringBuffer parms=new StringBuffer("");
            if((foreColor.length()>0)||(backColor.length()>0))
            {
                parms.append("<FONT ");
                if(foreColor.length()>0) parms.append(" COLOR="+foreColor);
                if(backColor.length()>0) parms.append(" BACK="+backColor);
                parms.append(">");
            }
            MXPElement L=(MXPElement)tags.get(new Integer(number));
            if(L==null) return;
            int newBitmap=L.getBitmap();
            if(gag!=null) 
                newBitmap|=MXPElement.BIT_EATTEXT;
            else
            if(disable!=null)
                newBitmap|=MXPElement.BIT_DISABLED;
            else
            if(L.isDisabled()&&(enable!=null))
                newBitmap-=MXPElement.BIT_DISABLED;
            L.setBitmap(newBitmap);
            if(parms.length()>0)
            {
                String definition=Util.stripBadHTMLTags(L.getDefinition());
                L.setDefinition(definition+parms.toString());
            }
            return;
        }
    }

    public String getEntityValue(String tag, MXPElement currentE)
    {
        String val=null;
        if(tag.equalsIgnoreCase("lcc")) val="color: "+lastForeground+"; background-color: "+lastBackground;
        if(val==null) val=(currentE!=null)?currentE.getAttributeValue(tag):null;
        if((val==null)&&(currentE!=null)) val=currentE.getAttributeValue(tag.toLowerCase());
        if((val==null)&&(currentE!=null)) val=currentE.getAttributeValue(tag.toUpperCase());
        if((val==null)&&(currentE==null))
        {
            if(val==null)
                for(int x=openElements.size()-1;x>=0;x--)
                {
                    MXPElement E=(MXPElement)openElements.elementAt(x);
                    val=E.getAttributeValue(tag);
                    if(val!=null) break;
                }
            if(val==null)
                for(int x=openElements.size()-1;x>=0;x--)
                {
                    MXPElement E=(MXPElement)openElements.elementAt(x);
                    val=E.getAttributeValue(tag.toLowerCase());
                    if(val!=null) break;
                }
            if(val==null)
                for(int x=openElements.size()-1;x>=0;x--)
                {
                    MXPElement E=(MXPElement)openElements.elementAt(x);
                    val=E.getAttributeValue(tag.toUpperCase());
                    if(val!=null) break;
                }
        }
        if(val==null)
        {
            MXPEntity N=(MXPEntity)entities.get(tag);
            if(N==null) N=(MXPEntity)entities.get(tag.toLowerCase());
            if(N==null) N=(MXPEntity)entities.get(tag.toUpperCase());
            if(N!=null) val=N.getDefinition();
        }
        return val;
    }

    
    public void modifyEntity(String name, String value)
    {
        name=name.toLowerCase();
        MXPEntity X=(MXPEntity)entities.get(name);
        if(X==null) 
        {
            X=new MXPEntity(name,value);
            entities.put(name,X);
        }
        else
        {
            if(X.getDefinition().equalsIgnoreCase(value))
                return;
            X.setDefinition(value);
        }
        String[] gauge=null;
        for(int g=0;g<gauges.size();g++)
        {
            gauge=(String[])gauges.elementAt(g);
            if((gauge[0].equalsIgnoreCase(name))
            ||(gauge[1].equalsIgnoreCase(name)))
            {
                String initEntity=getEntityValue(gauge[0],null);
                int initValue=0;
                if((initEntity!=null)&&(Util.isInteger(initEntity))) 
                    initValue=Util.s_int(initEntity);
                String maxEntity=getEntityValue(gauge[1],null);
                int maxValue=100;
                if((maxEntity!=null)&&(Util.isInteger(maxEntity))) 
                    maxValue=Util.s_int(maxEntity);
                if(maxValue<initValue)  maxValue=(initValue<=0)?100:initValue;
                if(initValue>0) 
                    initValue=(int)Math.round(Util.mul(100.0,Util.div(initValue,maxValue)));
                synchronized(jscriptBuffer)
                {
                    jscriptBuffer.append("modifyGauge('"+gauge[0]+"',"+initValue+","+maxValue+");");
                }
            }
        }
    }
    
    public void shutdownMXP()
    {
        openElements.clear();
        eatAllEOLN=false;
        eatNextEOLN=false;
        eatTextUntilEOLN=false;
        initMXP();
        jscriptBuffer.setLength(0);
        responses.setLength(0);
        while(gauges.size()>0)
        {
            synchronized(jscriptBuffer)
            {
                jscriptBuffer.append("removeGauge('"+((String[])gauges.elementAt(0))[0]+"');");
            }
            gauges.removeElementAt(0);
        }
        mode=0;
        defaultMode=0;
        tags.clear();
    }
    
    
    public int processEntity(StringBuffer buf, int i, MXPElement currentE, boolean convertIfNecessary)
    {
        if((mode()==MXP.MODE_LINE_LOCKED)||(mode()==MXP.MODE_LOCK_LOCKED))
            return 0;
        boolean convertIt=false;
        int oldI=i;
        StringBuffer content=new StringBuffer("");
        if((buf.charAt(i+1)=='#')&&(Character.isDigit(buf.charAt(i+2))))
        {
            i++; // skip to the hash, the next line will skip to the digit
            while((++i)<buf.length())
            {
                if(buf.charAt(i)==';')
                {
                    convertIt=false;
                    break;
                }
                else
                if(!Character.isDigit(buf.charAt(i)))
                {
                    convertIt=true;
                    break;
                }
            }
        }
        else
        while((++i)<buf.length())
        {
            if(buf.charAt(i)==';')
            {
                convertIt=false;
                break;
            }
            else
            if(!Character.isLetterOrDigit(buf.charAt(i)))
            {
                convertIt=true;
                break;
            }
            else
            if((!Character.isLetter(buf.charAt(i)))&&(content.length()==0))
            {
                convertIt=true;
                break;
            }
            content.append(buf.charAt(i));
            if(content.length()>20) break;
        }
        if((i>=buf.length())&&(content.length()>0)&&((buf.length()-i)<10))
        {
            if(entityDebug) System.out.println("e=INCOMPLETE: "+content.toString());
            return Integer.MAX_VALUE;
        }
        if((convertIt)||(content.length()==0)||(buf.charAt(i)!=';'))
        {
            if(entityDebug) System.out.println("e=ILLEGAL1: "+content.toString());
            if(convertIfNecessary)
            {
                buf.insert(oldI+1,"amp;");
                return 4;
            }
            return 0;
        }
        String tag=content.toString().trim();
        String val=getEntityValue(tag,currentE);
        String oldValue=buf.substring(oldI,i+1);
        if(entityDebug) System.out.println("entity="+tag+", val="+val);
        buf.delete(oldI,i+1);
        if(val!=null)
        {
            if((currentE!=null)&&(currentE.name().equalsIgnoreCase("FONT")))
            {
                if(tag.equalsIgnoreCase("COLOR"))
                    lastForeground=val;
                else
                if(tag.equalsIgnoreCase("BACK"))
                    lastBackground=val;
            }
            buf.insert(oldI,val);
            if((val.equalsIgnoreCase(oldValue))
            ||(currentE!=null))
                return val.length()-1;
            return -1;
        }
        return -1;
    }
    
}
