package org.jacorb.idl;

/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-2014 Gerald Brose / The JacORB Team.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library 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
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

import java.util.Enumeration;

/**
 * @author Gerald Brose
 */

public class Case
    extends IdlSymbol
{
    /** the labels for this case */
    public SymbolList case_label_list = null;

    /** only set after parsing */
    private IdlSymbol[] labels;

    /** this case's element's type's spec */
    public ElementSpec element_spec = null;

    /** the switch type's spec */
    TypeSpec type_spec = null;

    public Case( int num )
    {
        super( num );
    }

    public void setPackage( String s )
    {
        s = parser.pack_replace( s );
        if( pack_name.length() > 0 )
            pack_name = s + "." + pack_name;
        else
            pack_name = s;

        element_spec.setPackage( s );
        Enumeration e = case_label_list.v.elements();

        for( ; e.hasMoreElements(); )
        {
            IdlSymbol sym = (IdlSymbol)e.nextElement();
            if( sym != null )
                sym.setPackage( s );
        }

        if( type_spec != null )
            type_spec.setPackage( s );
    }

    /**
     * pass a reference to the containing union through
     * to the case elements, which pass it on
     */

    public void setUnion( UnionType ut )
    {
        element_spec.setUnion( ut );
    }

    public void setEnclosingSymbol( IdlSymbol s )
    {
        if( enclosing_symbol != null && enclosing_symbol != s )
            throw new RuntimeException( "Compiler Error: trying to reassign container for " + name );

        enclosing_symbol = s;
        element_spec.setEnclosingSymbol( s );
    }

    public void setTypeSpec( TypeSpec s )
    {
        type_spec = s;
    }

    private String enumTypeName()
    {
        // and enum type name if necessary
        if( type_spec.type_spec instanceof ConstrTypeSpec )
        {
            return ( (ConstrTypeSpec)type_spec.type_spec ).full_name();
        }
        else if( type_spec.type_spec instanceof ScopedName )
        {
            TypeSpec ts = ( (ScopedName)type_spec.type_spec ).resolvedTypeSpec();

            while( ts instanceof ScopedName || ts instanceof AliasTypeSpec )
            {
                if( ts instanceof ScopedName )
                    ts = ( (ScopedName)ts ).resolvedTypeSpec();
                if( ts instanceof AliasTypeSpec )
                    ts = ( (AliasTypeSpec)ts ).originalType();
            }

            if( ts instanceof ConstrTypeSpec )
                return ( (ConstrTypeSpec)ts ).c_type_spec.full_name();
        }
        // all else
        return null;
    }


    public void parse()
    {
        element_spec.parse();

        labels = new IdlSymbol[ case_label_list.v.size() ];
        int label_idx = 0;

        for( Enumeration e = case_label_list.v.elements();
             e.hasMoreElements(); )
        {
            IdlSymbol sym = (IdlSymbol)e.nextElement();
            // remember label names
            labels[ label_idx++ ] = sym;

            // check that no literals are used in case labels when
            // the switch type spec doesn't allow it (i.e. bool, int, char)

            TypeSpec ts = type_spec.typeSpec();

            if( sym != null )
            {
                // null means "default" label in union
                if( ( (ConstExpr)sym ).or_expr.xor_expr.and_expr.shift_expr.add_expr.
                        mult_expr.unary_expr.primary_expr.symbol instanceof Literal )
                {
                    Literal literal =
                            (Literal)( (ConstExpr)sym ).or_expr.xor_expr.and_expr.shift_expr.add_expr.mult_expr.unary_expr.primary_expr.symbol;

                    if( ts instanceof ScopedName )
                    {
                        while( ts instanceof ScopedName )
                        {
                            ts = ( (ScopedName)type_spec.typeSpec() ).resolvedTypeSpec();
                            if( ts instanceof AliasTypeSpec )
                                ts = ( (AliasTypeSpec)ts ).originalType();
                        }
                    }

                    /* make sure  that case  label  and discriminator
                       value are compatible */

                    if(
                            ( !( ts instanceof BooleanType ||
                            ts instanceof IntType ||
                            ts instanceof CharType ||
                            ( ts instanceof BaseType && ( (BaseType)ts ).isSwitchType() )
                            )
                            )
                            ||
                            ( ts instanceof BooleanType &&
                            !( literal.string.equals( "true" ) || literal.string.equals( "false" ) ) )
                            ||
                            ( ts instanceof CharType &&
                            !literal.string.startsWith( "'" ) )
                    )
                    {
                        parser.error( "Illegal case label <" + literal.string +
                                "> for switch type " + type_spec.typeName(), token );
                        return; // abort parsing the case here (we'd get other errors)
                    }

                    if( ts instanceof IntType )
                    {
                        try
                        {
                            Integer.parseInt( literal.string );
                        }
                        catch( NumberFormatException ne )
                        {
                            parser.error( "Illegal case label <" + literal.string +
                                    "> for integral switch type " + type_spec.typeName(), token );
                            return;
                        }
                    }
                }
            }

            // if the switch type for the union we're part of
            // is an enumeration type, the enum type name has
            // been set.

            if( enumTypeName() == null )
            {
                // no enum
                if( sym != null )
                {
                    // null means "default" label in union
                    sym.parse();
                }
            }
            else
            {
                // case label const expressions refer to enum values
                if( sym != null )
                {
                    // now, if this is not the default case label...
                    // get the case label (a const expr) as a scoped name
                    ScopedName sn =
                            (ScopedName)( (ConstExpr)sym ).or_expr.xor_expr.and_expr.
                            shift_expr.add_expr.mult_expr.unary_expr.primary_expr.symbol;

                    // replace the original case label by a new, fully
                    // scoped name for the enum type value

                    int idx = case_label_list.v.indexOf( sym );
                    sym = new ScopedName( new_num() );
                    ( (ScopedName)sym ).setId( sn.typeName );
                    sym.setPackage( pack_name );
                    sym.parse();
                    case_label_list.v.setElementAt( sym, idx );
                }
            }
        } // for

    }

    IdlSymbol[] getLabels()
    {
        if (labels == null)
        {
            throw new RuntimeException ("Case labels not initialized!" );
        }
        return labels;
    }


    public void print( java.io.PrintWriter ps )
    {
        element_spec.print( ps );
    }
}
