package W3C.PICS;
import java.util.*;
import java.io.*;
/**
 * PICSTokenizer provides a high-level interface for parsing pics-service machine
 * readable. It wraps the StreamTokenizer in java.io package to support convenient
 * and customized parsing services.
 *
 * @see W3C.PICS.Service
 * @see W3C.PICS.Category
 * @see W3C.PICS.Enum
 */
class PICSTokenizer
{
   final static int T_OK=0;
   final static int T_OPTIONPAIR=1; //Option Pair
   final static int T_EXTENSION=2;  //Extension
   final static int T_MAX=3;
   final static int T_BOOLOPTION=4; //Boolean Option
   final static int T_PAIR=5;
   final static int T_CATEGORY=6;
   final static int T_ENUM=7;
   final static int T_MIN=8;
   final static int T_DEFAULT=9;
   final static int T_ENDOFRANGE=10; //End of Range
   final static int T_CLOSEPAREN=11; //Close Paranthesis


   public PICSTokenizer(InputStream ins)
    {
        tokenizer=new StreamTokenizer(ins);
        tokenizer.ordinaryChar('(');
        tokenizer.ordinaryChar(')');
    }

   /**
    * Note: Currently, version 1.0 and 1.1 are accepted
    */
   public void checkVersion() throws PICSException, IOException
   {
     if (tokenizer.nextToken()==')')
       throw new PICSException("Version missing");
     beginPair();
     if (tokenizer.sval.equals("PICS-version"))
        {

            if (tokenizer.nextToken()!=StreamTokenizer.TT_NUMBER)
               throw new PICSException("Version number missing" +
               tokenizer.ttype);
            else
                if (tokenizer.nval==1.0 || tokenizer.nval==1.1)
                   {   endPair();
                      return ;
                   }
                else
                   throw new PICSException("Version number error" + tokenizer.nval);
        }
     else
        throw new PICSException("Version Missing");

   }
   /**
    * Parse the next pair in parentheses
    */
   public int nextPair(String word,int type) throws PICSException, IOException
   {
      if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;

      beginPair();
      if (tokenizer.sval.equals(word))
         { if (tokenizer.nextToken()!=type) throw new PICSException("Wrong type at " +tokenizer.lineno());

           sval=tokenizer.sval;
           nval=(float)tokenizer.nval;

           endPair();
         }
         else throw new PICSException("A word is expected at " + tokenizer.lineno());
      return T_PAIR;

   }

   public Vector nextAllowBlockValue() throws PICSException, IOException
   {
    Vector v=new Vector();
    if (tokenizer.nextToken()!='(')
        throw new PICSException("[nextAllowBlockValue]" + tokenizer.lineno());
    while (true)
        {
         if (tokenizer.nextToken()==StreamTokenizer.TT_NUMBER)
            v.addElement(new Double(tokenizer.nval));
         else if (tokenizer.ttype==')')
            break;
         else throw new PICSException("nextAllowBlockValue" + tokenizer.lineno());
        }
//     System.out.println(v.size());
     return v;
   }


   /**
    * @see W3C.PICS.Range
    */
   public int nextRangeValue() throws PICSException, IOException
   {
    if (tokenizer.nextToken()==StreamTokenizer.TT_NUMBER)
        {
         nval=tokenizer.nval;
         if (tokenizer.nextToken()!=':')
            throw new PICSException("[nextRangeValue]" + tokenizer.lineno());
         if (tokenizer.nextToken()!=StreamTokenizer.TT_NUMBER)
            throw new PICSException("[nextRangeValue]" + tokenizer.lineno());
         nval2=tokenizer.nval;
         return 0;
        }
     else if (tokenizer.ttype==')' || tokenizer.ttype=='"')
        {tokenizer.pushBack();
         return T_ENDOFRANGE;
        }
     else throw new PICSException(" [nextRangeValue] "+ tokenizer.lineno());
   }

   public int nextCategoryOption() throws PICSException, IOException
   {

     if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;

      beginPair();
      option=tokenizer.sval;


      int val=nextDefaultableOption();
      if (val!=-1) return val;
       else
          if (option.equals("label"))
            {
            return T_ENUM;}
          else
            return nextOption();

   }

   public int nextEnumOption() throws PICSException, IOException
   {
     if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;

     beginPair();

     option=tokenizer.sval;;
     int type=tokenizer.nextToken();
     if (type!='"' && type!=StreamTokenizer.TT_NUMBER)
            throw new PICSException("A quoted string or a number expected at line " + tokenizer.lineno());

      else
      {
       sval=tokenizer.sval;
       nval=tokenizer.nval;
       endPair();
      return T_OPTIONPAIR;
      }
   }



   public int nextServiceOption() throws PICSException, IOException

   {
     if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;

     beginPair();

     option=tokenizer.sval;
     if (option.equals("default"))
        return T_DEFAULT;
     return nextOption();

   }


   public int getDefaultableOption() throws PICSException, IOException
   {
     if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;

     beginPair();
     option=tokenizer.sval;;

     return nextDefaultableOption();
   }

   public int nextRange() throws PICSException, IOException
   {
     if (tokenizer.nextToken()==')')
        return T_CLOSEPAREN;
     if (tokenizer.ttype != '"')
            throw new PICSException(" [nextRange] Quoted word not found at line " + tokenizer.lineno());
     tokenizer.pushBack();
     return 0;
   }

   public int nextTransmitas() throws PICSException, IOException
   {
     if (tokenizer.nextToken()=='"')
        {
         sval=tokenizer.sval;
         return 0;
        }
     else throw new PICSException("[nextTransmitas] "+tokenizer.lineno());
   }

   private int nextDefaultableOption() throws PICSException, IOException
   {
      if (option.equals("max") || option.equals("min"))
      {
         tokenizer.nextToken();
         if (tokenizer.ttype==StreamTokenizer.TT_NUMBER)
            nval=tokenizer.nval;
         else if (tokenizer.ttype==StreamTokenizer.TT_WORD)
            {if (tokenizer.sval=="+INF")
               nval=Double.POSITIVE_INFINITY;
             else if (tokenizer.sval=="-INF")
               nval=Double.NEGATIVE_INFINITY;
             else throw new PICSException( option + " Error at " + tokenizer.lineno());

            }
         endPair();
         if (option.equals("max"))
            return T_MAX;
            else return T_MIN;

      }


      if (option.equals("integer") || option.equals("label-only") ||
          option.equals("multivalue") || option.equals("unordered"))

          {
            if (tokenizer.nextToken()==')')
               {bval=true;
                return T_BOOLOPTION;
               }
            else if (tokenizer.ttype==StreamTokenizer.TT_WORD)
               {if (tokenizer.sval.equals("t") || tokenizer.sval.equals("true"))
                  bval=true;
                else if (tokenizer.sval.equals("f") || tokenizer.sval.equals("false"))
                  bval=false;
                else throw new PICSException("Defaultable Option " + option + " Error at " + tokenizer.lineno());
               }

            endPair();
            return T_BOOLOPTION;

          }

        return -1;

   }

   int nextOption()throws PICSException, IOException

    {
       if (option.equals("extension"))
          {
             skipToCLOSEPAREN();
             return T_EXTENSION;
           }

       if (option.equals("category"))
         {
            return T_CATEGORY;
         }

        if (option.equals("description") || option.equals("icon")
            || option.equals("name"))
            {
               if (tokenizer.nextToken()!='"')
                   throw new PICSException("A quoted string expected at line " + tokenizer.lineno());
               sval=tokenizer.sval;
               endPair();

                return T_OPTIONPAIR   ;
            }
       throw new PICSException("[nextOption] Invalid Option at " + tokenizer.lineno());

    }

   void firstParen() throws PICSException, IOException
   {
    if (tokenizer.nextToken()!='(')
     throw new PICSException("Initial ( missing");
   }

   /**
    *   Called to check the appropriate format of the beginning of a pair
    */
   private void beginPair() throws IOException, PICSException
   {
      if (tokenizer.ttype != '(')
            throw new PICSException(" [beginPair] ( not found at line " + tokenizer.lineno());
      if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
            throw new PICSException(" [begin pair] A word expected at line " + tokenizer.lineno());

   }

   void endPair() throws IOException, PICSException
   {
      if (tokenizer.nextToken() != ')')
           throw new PICSException(" [endPair] ) not found at line " + tokenizer.lineno());

   }

   private void skipToCLOSEPAREN() throws IOException
      {
         while (tokenizer.nextToken()!=')');
      }

    private StreamTokenizer tokenizer;

    public String option=null;
    public String sval=null;
    public double nval=0;
    public double nval2=0;
    public Vector vval=null;
    public boolean bval=false;

    int ttype=0;

}

