/**
 * SVG Salamander
 * Copyright (c) 2004, Mark McKay
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or 
 * without modification, are permitted provided that the following
 * conditions are met:
 *
 *   - Redistributions of source code must retain the above 
 *     copyright notice, this list of conditions and the following
 *     disclaimer.
 *   - Redistributions in binary form must reproduce the above
 *     copyright notice, this list of conditions and the following
 *     disclaimer in the documentation and/or other materials 
 *     provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 * 
 * Mark McKay can be contacted at mark@kitfox.com.  Salamander and other
 * projects can be found at http://www.kitfox.com
 */


options {
   MULTI=true;
   STATIC=false;
}

PARSER_BEGIN(AnimTimeParser)

package com.kitfox.svg.animation.parser;

import com.kitfox.svg.SVGConst;
import com.kitfox.svg.animation.TimeBase;
import com.kitfox.svg.animation.TimeCompound;
import com.kitfox.svg.animation.TimeDiscrete;
import com.kitfox.svg.animation.TimeIndefinite;
import com.kitfox.svg.animation.TimeLookup;
import com.kitfox.svg.animation.TimeSum;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

public class AnimTimeParser 
{
    /**
     * Test the parser
     */
    public static void main(String args[]) throws ParseException 
    {
//        AnimTimeParser parser = new AnimTimeParser(System.in);
        StringReader reader;

        reader = new StringReader("1:30 + 5ms");
        AnimTimeParser parser = new AnimTimeParser(reader);
        TimeBase tc;

        tc = parser.Expr();
        System.err.println("AnimTimeParser eval to " + tc.evalTime());

        reader = new StringReader("19");
        parser.ReInit(reader);
        tc = parser.Expr();
        System.err.println("AnimTimeParser eval to " + tc.evalTime());
    }
  
}

PARSER_END(AnimTimeParser)




/**
 * Tokens
 */

SKIP : /* WHITE SPACE */
{
  " "
| "\t"
| "\n"
| "\r"
| "\f"
}

TOKEN :
{
  < #LETTER: [ "a"-"z", "A"-"Z" ] >
|
  < #DIGIT: [ "0"-"9"] >
|
  < INTEGER: (<DIGIT>)+ >
|
  < FLOAT: (["+", "-"])? (((<DIGIT>)* "." (<DIGIT>)+) | ((<DIGIT>)+)) (["E", "e"] (["+", "-"])? (<DIGIT>)+)? >
|
  < INDEFINITE: "indefinite" >
|
  < MOUSE_OVER: "mouseover" >
|
  < WHEN_NOT_ACTIVE: "whenNotActive" >
|
  < UNITS: "ms" | "s" | "min" | "h" >
|
  < IDENTIFIER: <LETTER> (<LETTER>|<DIGIT>|"_"|"-")* >
  
}







/**
 * Expression structure
 */


TimeBase Expr() :
{
    TimeBase term;
    ArrayList list = new ArrayList();
}
{
    ( term = Sum() 
        {
            list.add(term);
        }
    )?
    ( LOOKAHEAD(2) ";" term = Sum() 
        {
            list.add(term);
        }
    ) *
    (";")?

    {
        switch (list.size())
        {
            case 0:
                return new TimeIndefinite();
            case 1:
                return (TimeBase)list.get(0);
            default:
                return new TimeCompound(list);
        }
    }
}

TimeBase Sum() :
{
    Token t = null;
    TimeBase t1;
    TimeBase t2 = null;
}
{
    t1=Term() ( (t="+" | t="-") t2 = Term() )?
        {
            if (t2 == null) return t1;

            if (t.image.equals("-"))
            {
                return new TimeSum(t1, t2, false);
            }
            else
            {
                return new TimeSum(t1, t2, true);
            }
        }
}


TimeBase Term() :
{
    TimeBase base;
}
{
    base=IndefiniteTime() 
        { return base; }
    | base=LiteralTime() 
        { return base; }
    | base=LookupTime()
        { return base; }
    | base=EventTime()
        { return base; }
}

TimeIndefinite IndefiniteTime() :
{}
{
    <INDEFINITE>
    {
        return new TimeIndefinite();
    }
}

TimeDiscrete EventTime() :
{}
{
    (<MOUSE_OVER> | <WHEN_NOT_ACTIVE>)
        {
            //For now, map all events to the zero time
            return new TimeDiscrete(0);
        }
}

TimeDiscrete LiteralTime() :
{
    double t1, t2, t3 = Double.NaN, value;
    Token t;
}
{
    t1=Number() 
        {
            value = t1;
        }
    (
    
    (":" t2=Number() (":" t3=Number())?
        {
            //Return clock time format (convert to seconds)
            if (Double.isNaN(t3))
            {
                value = t1 * 60 + t2;
            }
            else
            {
                value = t1 * 3600 + t2 * 60 + t3;
            }
        }
    )
    
    | 
    
    (t=<UNITS>
        {
            //Return units format (convert to seconds)
            if (t.image.equals("ms")) value = t1 / 1000;
            if (t.image.equals("min")) value = t1 * 60;
            if (t.image.equals("h")) value = t1 * 3600;
        }
    )
    )?
    {
        return new TimeDiscrete(value);
    }
}


TimeLookup LookupTime() :
{
    double paramNum = 0.0;
    Token node, event;
}
{
    node=<IDENTIFIER> "." event=<IDENTIFIER> (paramNum=ParamList())?
    {
        return new TimeLookup(null, node.image, event.image, "" + paramNum);
    }
}

double ParamList() :
{
    double num;
}
{
    "(" num=Number() ")"
    {
        return num;
    }
}

double Number() :
{
    Token t;
}
{
    t=<FLOAT> 
    {
        try {
            return Double.parseDouble(t.image);
        }
        catch (Exception e) {
            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
                "Could not parse double '" + t.image + "'", e);
        }
        
        return 0.0;
    }
    | t=<INTEGER>
    {
        try {
            return Double.parseDouble(t.image);
        }
        catch (Exception e) {
            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
                "Could not parse double '" + t.image + "'", e);
        }
        
        return 0.0;
    }
}

int Integer() :
{
    Token t;
}
{
    t=<INTEGER>
    {
        try {
            return Integer.parseInt(t.image);
        }
        catch (Exception e) {
            Logger.getLogger(SVGConst.SVG_LOGGER).log(Level.WARNING, 
                "Could not parse int '" + t.image + "'", e);
        }
        
        return 0;
    }
}

