/*
 * This program source code file is part of KICAD, a free EDA CAD application.
 *
 * Copyright (C) 2011 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com>
 * Copyright (C) 2010 Kicad Developers, see change_log.txt for contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, you may find one here:
 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
 * or you may search the http://www.gnu.org website for the version 2 license,
 * or you may write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

#include <sch_sweet_parser.h>
#include <sch_part.h>
#include <sch_lib_table.h>
#include <sch_lpid.h>


using namespace SCH;
using namespace PR;


#define MAX_INHERITANCE_NESTING     6       ///< max depth of inheritance, no problem going larger


static inline int internal( const STRING& aCoord )
{
    return LogicalToInternal( strtod( aCoord.c_str(), NULL ) );
}

static inline int fromWidth( const STRING& aWidth )
{
    return WidthToInternal( strtod( aWidth.c_str(), NULL ) );
}


/**
 * Enum PartBit
 * is a set of bit positions that can be used to create flag bits within
 * PART::contains to indicate what state the PART is in and what it contains, i.e.
 * whether the PART has been parsed, and what the PART contains, categorically.
 */
enum PartBit
{
    parsed,     ///< have parsed this part already, otherwise 'body' text must be parsed
    extends,    ///< saw "extends" keyword, inheriting from another PART
    value,
    anchor,
    reference,
    footprint,
    datasheet,
    model,
    keywords,
};


/// Function PB
/// is a PartBit shifter for PART::contains field.
static inline const int PB( PartBit oneBitOnly )
{
    return ( 1 << oneBitOnly );
}


void SWEET_PARSER::parseExtends( PART* me )
{
    PART*   base;
    int     offset;

    if( contains & PB(extends) )
        Duplicate( T_extends );

    NeedSYMBOLorNUMBER();
    me->setExtends( new LPID() );

    offset = me->extends->Parse( CurText() );
    if( offset > -1 )   // -1 is success
        THROW_PARSE_ERROR( _("invalid extends LPID"),
            CurSource(),
            CurLine(),
            CurLineNumber(),
            CurOffset() + offset );

    base = libs->LookupPart( *me->extends, me->Owner() );

    // we could be going in circles here, recursively, or too deep, set limits
    // and disallow extending from self (even indirectly)
    int extendsDepth = 0;
    for( PART* ancestor = base; ancestor && extendsDepth<MAX_INHERITANCE_NESTING;
            ++extendsDepth, ancestor = ancestor->base )
    {
        if( ancestor == me )
        {
            THROW_PARSE_ERROR( _("'extends' may not have self as any ancestor"),
                CurSource(),
                CurLine(),
                CurLineNumber(),
                CurOffset() );
        }
    }

    if( extendsDepth == MAX_INHERITANCE_NESTING )
    {
        THROW_PARSE_ERROR( _("max allowed extends depth exceeded"),
            CurSource(),
            CurLine(),
            CurLineNumber(),
            CurOffset() );
    }

    me->inherit( *base );
    me->base = base;
    contains |= PB(extends);
}


void SWEET_PARSER::Parse( PART* me, LIB_TABLE* aTable ) throw( IO_ERROR, PARSE_ERROR )
{
    T       tok;

    libs = aTable;

    // empty everything out, could be re-parsing this object and it may not be empty.
    me->clear();

#if 0
    // Be flexible regarding the starting point of the stream.
    // Caller may not have read the first two tokens out of the
    // stream: T_LEFT and T_part, so ignore them if seen here.
    // The 1st two tokens T_LEFT and T_part are then optional in the grammar.
    if( ( tok = NextTok() ) == T_LEFT )
    {
        if( ( tok = NextTok() ) != T_part )
            Expecting( T_part );
    }

#else
    // "( part" are not optional
    NeedLEFT();

    if( ( tok = NextTok() ) != T_part )
        Expecting( T_part );
#endif

    NeedSYMBOLorNUMBER();       // toss NAME_HINT
    tok = NextTok();

    // extends must be _first_ thing, if it is present at all, after NAME_HINT
    if( tok == T_extends )
    {
        parseExtends( me );
        tok = NextTok();
    }

    for(  ; tok!=T_RIGHT;  tok = NextTok() )
    {
        if( tok == T_LEFT )
        {
            PROPERTY*   prop;

            tok = NextTok();

            // because exceptions are thrown, any 'new' allocation has to be stored
            // somewhere other than on the stack, ASAP.

            switch( tok )
            {
            default:
                // describe what we expect at this level
                Expecting(
                    "anchor|value|footprint|model|keywords|alternates\n"
                    "|property\n"
                    "  |property_del\n"
                    "|pin\n"
                    "  |pin_merge|pin_swap|pin_renum|pin_rename|route_pin_swap\n"
                    "|polyline|line|rectangle|circle|arc|bezier|text"
                 );
                break;

            case T_anchor:
                if( contains & PB(anchor) )
                    Duplicate( tok );
                NeedNUMBER( "anchor x" );
                me->anchor.x = internal( CurText() );
                NeedNUMBER( "anchor y" );
                me->anchor.y = internal( CurText() );
                contains |= PB(anchor);
                break;

            case T_line:
            case T_polyline:
                POLY_LINE*  pl;
                pl = new POLY_LINE( me );
                me->graphics.push_back( pl );
                parsePolyLine( pl );
                break;

            case T_rectangle:
                RECTANGLE* rect;
                rect = new RECTANGLE( me );
                me->graphics.push_back( rect );
                parseRectangle( rect );
                break;

            case T_circle:
                CIRCLE* circ;
                circ = new CIRCLE( me );
                me->graphics.push_back( circ );
                parseCircle( circ );
                break;

            case T_arc:
                ARC* arc;
                arc = new ARC( me );
                me->graphics.push_back( arc );
                parseArc( arc );
                break;

            case T_bezier:
                BEZIER* bezier;
                bezier = new BEZIER( me );
                me->graphics.push_back( bezier );
                parseBezier( bezier );
                break;

            case T_text:
                GR_TEXT* text;
                text = new GR_TEXT( me );
                me->graphics.push_back( text );
                parseText( text );
                break;

            case T_property:
                prop = new PROPERTY( me );
                // @todo check for uniqueness
                me->properties.push_back( prop );
                NeedSYMBOLorNUMBER();
                prop->name = FromUTF8();

            L_prop:
                NeedSYMBOLorNUMBER();
                prop->text = FromUTF8();
                tok = NextTok();
                if( tok == T_LEFT )
                {
                    tok = NextTok();
                    if( tok != T_effects )
                        Expecting( T_effects );
                    parseTextEffects( prop->EffectsLookup() );
                    NeedRIGHT();
                }
                else if( tok != T_RIGHT )
                    Expecting( ") | effects" );
                break;

            case T_property_del:
                NeedSYMBOLorNUMBER();
                me->PropertyDelete( FromUTF8() );
                NeedRIGHT();
                break;

            // reference in a PART is incomplete, it is just the prefix of an
            // unannotated reference. Only components have full reference designators.
            case T_reference:
                if( contains & PB(reference) )
                    Duplicate( tok );
                contains |= PB(reference);
                prop = me->FieldLookup( PART::REFERENCE );
                goto L_prop;

            case T_value:
                if( contains & PB(value) )
                    Duplicate( tok );
                contains |= PB(value);
                prop = me->FieldLookup( PART::VALUE );
                goto L_prop;

            case T_footprint:
                if( contains & PB(footprint) )
                    Duplicate( tok );
                contains |= PB(footprint);
                prop = me->FieldLookup( PART::FOOTPRINT );
                goto L_prop;

            case T_datasheet:
                if( contains & PB(datasheet) )
                    Duplicate( tok );
                contains |= PB(datasheet);
                prop = me->FieldLookup( PART::DATASHEET );
                goto L_prop;

            case T_model:
                if( contains & PB(model) )
                    Duplicate( tok );
                contains |= PB(model);
                prop = me->FieldLookup( PART::MODEL );
                goto L_prop;

            case T_pin:
                PIN* pin;
                pin = new PIN( me );
                me->pins.push_back( pin );
                parsePin( pin );
                break;

            case T_keywords:
                parseKeywords( me );
                break;


            /*
            @todo

            case T_alternates:
                break;

            // do we want to inherit alternates?
            case T_pin_merge:
                break;

            case T_pin_swap:
                break;

            case T_pin_renum:
                break;

            case T_pin_rename:
                break;

            case T_route_pin_swap:
                break;
            */
            }
        }

        else
        {
            switch( tok )
            {
            default:
                Unexpected( tok );
            }
        }
    }

    contains |= PB(parsed);

    me->contains |= contains;
}


void SWEET_PARSER::parseKeywords( PART* me )
{
    T   tok;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( !IsSymbol( tok ) && tok!=T_NUMBER )
            Expecting( "symbol|number" );

        // just insert them, duplicates are silently removed and tossed.
        me->keywords.insert( FromUTF8() );
    }
}


void SWEET_PARSER::parseFont( FONT* me )
{
    /*
        # The FONT value needs to be defined.  Currently, EESchema does not support
        # different fonts.  In the future this feature may be implemented and at
        # that time FONT will have to be defined.  Initially, only the font size and
        # style are required.  Italic and bold styles are optional.  The font size
        # height and width are in units yet to be determined.
        (font [FONT] (size HEIGHT WIDTH) [italic] [bold])
    */

    // handle the [FONT] 'position dependently', i.e. first
    T       tok = NextTok();
    bool    sawBold   = false;
    bool    sawItalic = false;
    bool    sawSize   = false;

    if( IsSymbol( tok ) )
    {
        me->name = FromUTF8();
        tok = NextTok();
    }

    for( ; tok != T_RIGHT; tok = NextTok() )
    {
        if( tok == T_LEFT )
        {
            tok = NextTok();

            switch( tok )
            {
            case T_size:
                if( sawSize )
                    Duplicate( T_size );
                sawSize = true;

                NeedNUMBER( "size height" );
                me->size.SetHeight( internal( CurText() ) );

                NeedNUMBER( "size width" );
                me->size.SetWidth( internal( CurText() ) );
                NeedRIGHT();
                break;

            default:
                Expecting( "size" );
            }
        }
        else
        {
            switch( tok )
            {
            case T_bold:
                if( sawBold )
                    Duplicate( T_bold );
                sawBold = true;
                me->bold = true;
                break;

            case T_italic:
                if( sawItalic )
                    Duplicate( T_italic );
                sawItalic = true;
                me->italic = true;
                break;

            default:
                Unexpected( "bold|italic" );
            }
        }
    }
}


void SWEET_PARSER::parseBool( bool* aBool )
{
    T   tok = NeedSYMBOL();

    switch( tok )
    {
    case T_yes:
    case T_no:
        *aBool = (tok == T_yes);
        break;
    default:
        Expecting( "yes|no" );
    }
}


void SWEET_PARSER::parsePinText( PINTEXT* me )
{
    /*  either:
        (signal SIGNAL   (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES))
        or
        (padname PADNAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES))
    */
    T       tok;
    bool    sawFont = false;
    bool    sawVis  = false;

    // padname or signal text
    NeedSYMBOLorNUMBER();
    me->text = FromUTF8();

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok == T_LEFT )
        {
            tok = NextTok();

            switch( tok )
            {
            case T_font:
                if( sawFont )
                    Duplicate( tok );
                sawFont = true;
                parseFont( &me->font );
                break;

            case T_visible:
                if( sawVis )
                    Duplicate( tok );
                sawVis = true;
                parseBool( &me->isVisible );
                NeedRIGHT();
                break;

            default:
                Expecting( "font" );
            }
        }

        else
        {
            switch( tok )
            {
            default:
                Expecting( T_LEFT );
            }
        }
    }
}


void SWEET_PARSER::parsePin( PIN* me )
{
    /*
        (pin TYPE SHAPE
            (at X Y [ANGLE])
            (length LENGTH)
            (signal NAME (font [FONT] (size HEIGHT WIDTH) [italic] [bold])(visible YES))
            (padname NUMBER (font [FONT] (size HEIGHT WIDTH) [italic] [bold] (visible YES))
            (visible YES)
        )
    */

    T       tok;
    bool    sawShape   = false;
    bool    sawType    = false;
    bool    sawAt      = false;
    bool    sawLen     = false;
    bool    sawSignal  = false;
    bool    sawPadName = false;
    bool    sawVis     = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok == T_LEFT )
        {
            tok = NextTok();

            switch( tok )
            {
            case T_at:
                if( sawAt )
                    Duplicate( tok );
                sawAt = true;
                parseAt( &me->pos, &me->angle );
                break;

            case T_length:
                if( sawLen )
                    Duplicate( tok );
                sawLen = true;
                NeedNUMBER( "length" );
                me->length = internal( CurText() );
                NeedRIGHT();
                break;

            case T_signal:
                if( sawSignal )
                    Duplicate( tok );
                sawSignal = true;
                parsePinText( &me->signal );
                break;

            case T_padname:
                if( sawPadName )
                    Duplicate( tok );
                sawPadName = true;
                parsePinText( &me->padname );
                break;

            case T_visible:
                if( sawVis )
                    Duplicate( tok );
                sawVis = true;
                parseBool( &me->isVisible );
                NeedRIGHT();
                break;

            default:
                Unexpected( tok );
            }
        }

        else    // not wrapped in parentheses
        {
            switch( tok )
            {
            case T_input:
            case T_output:
            case T_bidirectional:
            case T_tristate:
            case T_passive:
            case T_unspecified:
            case T_power_in:
            case T_power_out:
            case T_open_collector:
            case T_open_emitter:
            case T_unconnected:
                if( sawType )
                    Duplicate( tok );
                sawType = true;
                me->connectionType = tok;
                break;

            case T_none:
            case T_line:
            case T_inverted:
            case T_clock:
            case T_inverted_clk:
            case T_input_low:
            case T_clock_low:
            case T_falling_edge:
            case T_non_logic:
                if( sawShape )
                    Duplicate( tok );
                sawShape = true;
                me->shape = tok;
                break;

            default:
                Unexpected( tok );
            }
        }
    }
}


void SWEET_PARSER::parseTextEffects( TEXT_EFFECTS* me )
{
    /*
        (effects [PROPERTY]

            # Position requires X and Y coordinates.  Position coordinates can be
            # non-intergr.  Angle is in degrees and defaults to 0 if not defined.
            (at X Y [ANGLE])

            # The FONT value needs to be defined.  Currently, EESchema does not support
            # different fonts.  In the future this feature may be implemented and at
            # that time FONT will have to be defined.  Initially, only the font size and
            # style are required.  Italic and bold styles are optional.  The font size
            # height and width are in units yet to be determined.
            (font [FONT] (size HEIGHT WIDTH) [italic] [bold])

            # Valid visibility values are yes and no.
            (visible YES)
        )
    */

    bool    sawFont = false;
    bool    sawAt   = false;
    bool    sawVis  = false;

    T       tok = NextTok();

    if( IsSymbol( tok ) )
    {
        me->propName = FromUTF8();
        tok = NextTok();
    }

    for(  ; tok != T_RIGHT;  tok = NextTok() )
    {
        if( tok != T_LEFT )
            Expecting( T_LEFT );

        tok = NextTok();

        switch( tok )
        {
        case T_at:
            if( sawAt )
                Duplicate( tok );
            sawAt = true;
            parseAt( &me->pos, &me->angle );
            break;

        case T_font:
            if( sawFont )
                Duplicate( tok );
            sawFont = true;
            parseFont( &me->font );
            break;

        case T_visible:
            if( sawVis )
                Duplicate( sawVis );
            sawVis = true;
            parseBool( &me->isVisible );
            NeedRIGHT();
            break;

        default:
            Expecting( "at|font|visible" );
        }
    }
}


void SWEET_PARSER::parsePolyLine( POLY_LINE* me )
{
    /*
        (polyline|line
            (pts (xy X Y) (xy X Y) (xy X Y) (xy X Y) (xy X Y))

            # Line widths are in units as defined above.
            (line_width WIDTH)

            # Valid fill types are none, filled, and transparent.
            (fill FILL_TYPE)
        )
    */

    T       tok;
    int     count = 0;
    bool    sawWidth = false;
    bool    sawFill  = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok != T_LEFT )
            Expecting( T_LEFT );

        tok = NextTok();

        switch( tok )
        {
        case T_line_width:
            if( sawWidth )
                Duplicate( tok );
            NeedNUMBER( "line_width" );
            me->lineWidth = fromWidth( CurText() );
            NeedRIGHT();
            sawWidth = true;
            break;

        case T_pts:
            if( count )
                Duplicate( tok );
            for(  ;  ( tok = NextTok() ) != T_RIGHT;  ++count )
            {
                if( tok != T_LEFT )
                    Expecting( T_LEFT );

                tok = NeedSYMBOL();
                if( tok != T_xy )
                    Expecting( T_xy );

                me->pts.push_back( POINT() );

                NeedNUMBER( "x" );
                me->pts.back().x = internal( CurText() );

                NeedNUMBER( "y" );
                me->pts.back().y = internal( CurText() );

                NeedRIGHT();
            }
            if( count < 2 )
                Expecting( ">= 2 pts" );
            break;

        case T_fill:
            if( sawFill )
                Duplicate( tok );
            tok = NeedSYMBOL();
            switch( tok )
            {
            case T_none:
            case T_filled:
            case T_transparent:
                me->fillType = tok;
                break;
            default:
                Expecting( "none|filled|transparent" );
            }
            NeedRIGHT();
            sawFill = true;
            break;

        default:
            Expecting( "pts|line_width|fill" );
        }
    }
}


void SWEET_PARSER::parseBezier( BEZIER* me )
{
    parsePolyLine( me );
}


void SWEET_PARSER::parseRectangle( RECTANGLE* me )
{
    /*
        (rectangle (start X Y) (end X Y) (line_width WIDTH) (fill FILL_TYPE))
    */

    T       tok;
    bool    sawStart = false;
    bool    sawEnd   = false;
    bool    sawWidth = false;
    bool    sawFill  = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok != T_LEFT )
            Expecting( T_LEFT );

        tok = NextTok();

        switch( tok )
        {
        case T_line_width:
            if( sawWidth )
                Duplicate( tok );
            sawWidth = true;
            NeedNUMBER( "line_width" );
            me->lineWidth = fromWidth( CurText() );
            NeedRIGHT();
            break;

        case T_fill:
            if( sawFill )
                Duplicate( tok );
            sawFill = true;
            tok = NeedSYMBOL();
            switch( tok )
            {
            case T_none:
            case T_filled:
            case T_transparent:
                me->fillType = tok;
                break;
            default:
                Expecting( "none|filled|transparent" );
            }
            NeedRIGHT();
            break;

        case T_start:
            if( sawStart )
                Duplicate( tok );
            sawStart = true;
            NeedNUMBER( "x" );
            me->start.x = internal( CurText() );
            NeedNUMBER( "y" );
            me->start.y = internal( CurText() );
            NeedRIGHT();
            break;

        case T_end:
            if( sawEnd )
                Duplicate( tok );
            sawEnd = true;
            NeedNUMBER( "x" );
            me->end.x = internal( CurText() );
            NeedNUMBER( "y" );
            me->end.y = internal( CurText() );
            NeedRIGHT();
            break;

        default:
            Expecting( "start|end|line_width|fill" );
        }
    }
}


void SWEET_PARSER::parseCircle( CIRCLE* me )
{
    /*
        (circle (center X Y)
            # Radius length is in units if defined or mils.
            (radius LENGTH)
            (line_width WIDTH)
            (fill FILL_TYPE)
        )
    */

    T       tok;
    bool    sawCenter = false;
    bool    sawRadius = false;
    bool    sawWidth  = false;
    bool    sawFill   = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok != T_LEFT )
            Expecting( T_LEFT );

        tok = NextTok();

        switch( tok )
        {
        case T_line_width:
            if( sawWidth )
                Duplicate( tok );
            sawWidth = true;
            NeedNUMBER( "line_width" );
            me->lineWidth = fromWidth( CurText() );
            NeedRIGHT();
            break;

        case T_fill:
            if( sawFill )
                Duplicate( tok );
            sawFill = true;
            tok = NeedSYMBOL();
            switch( tok )
            {
            case T_none:
            case T_filled:
            case T_transparent:
                me->fillType = tok;
                break;
            default:
                Expecting( "none|filled|transparent" );
            }
            NeedRIGHT();
            break;

        case T_center:
            if( sawCenter )
                Duplicate( tok );
            sawCenter = true;
            NeedNUMBER( "center x" );
            me->center.x = internal( CurText() );
            NeedNUMBER( "center y" );
            me->center.y = internal( CurText() );
            NeedRIGHT();
            break;

        case T_radius:
            if( sawRadius )
                Duplicate( tok );
            sawRadius = true;
            NeedNUMBER( "radius" );
            me->radius = internal( CurText() );
            NeedRIGHT();
            break;

        default:
            Expecting( "center|radius|line_width|fill" );
        }
    }
}


void SWEET_PARSER::parseArc( ARC* me )
{
    /*
        (arc (pos X Y) (radius RADIUS) (start X Y) (end X Y)
            (line_width WIDTH)
            (fill FILL_TYPE)
        )
    */

    T       tok;
    bool    sawPos    = false;
    bool    sawStart  = false;
    bool    sawEnd    = false;
    bool    sawRadius = false;
    bool    sawWidth  = false;
    bool    sawFill   = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok != T_LEFT )
            Expecting( T_LEFT );

        tok = NextTok();

        switch( tok )
        {
        case T_line_width:
            if( sawWidth )
                Duplicate( tok );
            sawWidth = true;
            NeedNUMBER( "line_width" );
            me->lineWidth = fromWidth( CurText() );
            NeedRIGHT();
            break;

        case T_fill:
            if( sawFill )
                Duplicate( tok );
            sawFill = true;
            tok = NeedSYMBOL();
            switch( tok )
            {
            case T_none:
            case T_filled:
            case T_transparent:
                me->fillType = tok;
                break;
            default:
                Expecting( "none|filled|transparent" );
            }
            NeedRIGHT();
            break;

        case T_pos:
            if( sawPos )
                Duplicate( tok );
            sawPos = true;
            NeedNUMBER( "pos x" );
            me->pos.x = internal( CurText() );
            NeedNUMBER( "pos y" );
            me->pos.y = internal( CurText() );
            NeedRIGHT();
            break;

        case T_radius:
            if( sawRadius )
                Duplicate( tok );
            sawRadius = true;
            NeedNUMBER( "radius" );
            me->radius = internal( CurText() );
            NeedRIGHT();
            break;

        case T_start:
            if( sawStart )
                Duplicate( tok );
            sawStart = true;
            NeedNUMBER( "start x" );
            me->start.x = internal( CurText() );
            NeedNUMBER( "start y" );
            me->start.y = internal( CurText() );
            NeedRIGHT();
            break;

        case T_end:
            if( sawEnd )
                Duplicate( tok );
            sawEnd = true;
            NeedNUMBER( "end x" );
            me->end.x = internal( CurText() );
            NeedNUMBER( "end y" );
            me->end.y = internal( CurText() );
            NeedRIGHT();
            break;

        default:
            Expecting( "center|radius|line_width|fill" );
        }
    }
}


void SWEET_PARSER::parseAt( POINT* pos, float* angle )
{
    T       tok;

    NeedNUMBER( "at x" );
    pos->x = internal( CurText() );

    NeedNUMBER( "at y" );
    pos->y = internal( CurText() );

    tok = NextTok();
    if( angle && tok == T_NUMBER )
    {
        *angle = strtod( CurText(), NULL );
        tok = NextTok();
    }
    if( tok != T_RIGHT )
        Expecting( T_RIGHT );
}


void SWEET_PARSER::parseText( GR_TEXT* me )
{
    /*
        (text "This is the text that gets drawn."
            (at X Y [ANGLE])

            # Valid horizontal justification values are center, right, and left.  Valid
            # vertical justification values are center, top, bottom.
            (justify HORIZONTAL_JUSTIFY VERTICAL_JUSTIFY)
            (font [FONT] (size HEIGHT WIDTH) [italic] [bold])
            (visible YES)
            (fill FILL_TYPE)
        )
    */

    T       tok;
    bool    sawAt   = false;
    bool    sawFill = false;
    bool    sawFont = false;
    bool    sawVis  = false;
    bool    sawJust = false;
    bool    sawText = false;

    while( ( tok = NextTok() ) != T_RIGHT )
    {
        if( tok == T_LEFT )
        {
            tok = NextTok();

            switch( tok )
            {
            case T_at:
                if( sawAt )
                    Duplicate( tok );
                parseAt( &me->pos, &me->angle );
                sawAt = true;
                break;

            case T_fill:
                if( sawFill )
                    Duplicate( tok );
                tok = NeedSYMBOL();
                switch( tok )
                {
                case T_none:
                case T_filled:
                case T_transparent:
                    me->fillType = tok;
                    break;
                default:
                    Expecting( "none|filled|transparent" );
                }
                NeedRIGHT();
                sawFill = true;
                break;

            case T_justify:
                if( sawJust )
                    Duplicate( tok );
                tok = NeedSYMBOL();
                switch( tok )
                {
                case T_center:
                case T_right:
                case T_left:
                    me->hjustify = tok;
                    break;
                default:
                    Expecting( "center|right|left" );
                }

                tok = NeedSYMBOL();
                switch( tok )
                {
                case T_center:
                case T_top:
                case T_bottom:
                    me->vjustify = tok;
                    break;
                default:
                    Expecting( "center|top|bottom" );
                }
                NeedRIGHT();
                sawJust = true;
                break;

            case T_visible:
                if( sawVis )
                    Duplicate( tok );
                parseBool( &me->isVisible );
                NeedRIGHT();
                sawVis = true;
                break;

            case T_font:
                if( sawFont )
                    Duplicate( tok );
                sawFont = true;
                parseFont( &me->font );
                break;

            default:
                Expecting( "at|justify|font|visible|fill" );
            }
        }
        else
        {
            if( !IsSymbol( tok ) && tok != T_NUMBER )
                Expecting( T_STRING );

            if( sawText )
                Duplicate( tok );
            sawText = true;

            me->text = wxString::FromUTF8( CurText() );
        }
    }
}

