/** \file pretty_scope.c
 * split out of exppp.c 9/21/13
 */

#include "pp.h"
#include "exppp.h"
#include "pretty_expr.h"
#include "pretty_rule.h"
#include "pretty_type.h"
#include "pretty_func.h"
#include "pretty_entity.h"
#include "pretty_proc.h"
#include "pretty_scope.h"

/** add items from s to list alphabetically */
void SCOPEadd_inorder( Linked_List list, Scope s ) {
    Link k = 0;

    LISTdo_links( list, link )
    if( 0 > strcmp(
                SCOPEget_name( s ),
                SCOPEget_name( ( Type )( link->data ) ) ) ) {
        k = link;
        break;
    } LISTod

    LISTadd_before( list, k, ( Generic )s );
}

/** like SCOPEadd_inorder, but for Variables */
void SCOPEaddvars_inorder( Linked_List list, Variable v ) {
    Link k = 0;

    LISTdo_links( list, link )
    if( 0 > strcmp( v->name->symbol.name,  ( ( Variable ) link->data )->name->symbol.name ) ) {
        k = link;
        break;
    } LISTod

    LISTadd_before( list, k, ( Generic )v );
}


/** print the rules in a scope */
void SCOPErules_out( Scope s, int level ) {
    Rule r;
    DictionaryEntry de;

    if( exppp_alphabetize == false ) {
        DICTdo_type_init( s->symbol_table, &de, OBJ_RULE );
        while( 0 != ( r = ( Rule )DICTdo( &de ) ) ) {
            RULE_out( r, level );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( s->symbol_table, &de, OBJ_RULE );
        while( 0 != ( r = ( Rule )DICTdo( &de ) ) ) {
            SCOPEadd_inorder( alpha, r );
        }

        LISTdo( alpha, ru, Rule ) {
            RULE_out( ru, level );
        } LISTod

        LISTfree( alpha );
    }

}

/** print the functions in a scope */
void SCOPEfuncs_out( Scope s, int level ) {
    Function f;
    DictionaryEntry de;

    if( exppp_alphabetize == false ) {
        DICTdo_type_init( s->symbol_table, &de, OBJ_FUNCTION );
        while( 0 != ( f = ( Function )DICTdo( &de ) ) ) {
            FUNC_out( f, level );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( s->symbol_table, &de, OBJ_FUNCTION );
        while( 0 != ( f = ( Function )DICTdo( &de ) ) ) {
            SCOPEadd_inorder( alpha, f );
        }

        LISTdo( alpha, fun, Function ) {
            FUNC_out( fun, level );
        } LISTod

        LISTfree( alpha );
    }

}

/* print the procs in a scope */
void SCOPEprocs_out( Scope s, int level ) {
    Procedure p;
    DictionaryEntry de;

    if( exppp_alphabetize == false ) {
        DICTdo_type_init( s->symbol_table, &de, OBJ_PROCEDURE );
        while( 0 != ( p = ( Procedure )DICTdo( &de ) ) ) {
            PROC_out( p, level );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( s->symbol_table, &de, OBJ_PROCEDURE );
        while( 0 != ( p = ( Procedure )DICTdo( &de ) ) ) {
            SCOPEadd_inorder( alpha, p );
        }

        LISTdo( alpha, pr, Procedure ) {
            PROC_out( pr, level );
        } LISTod

        LISTfree( alpha );
    }

}

/* output order for DIS & IS schemas:
 * CONSTANT
 * TYPE
 * ENTITY
 * RULE
 * FUNCTION
 * PROCEDURE
 *
 * Within each of those groups, declarations must be sorted alphabetically.
 */
/* print the algorithms in a scope */
void SCOPEalgs_out( Scope s, int level ) {
    /* Supplementary Directivies 2.1.1 requires rules to be separated */
    /* might as well separate funcs and procs, too */
    SCOPErules_out( s, level );
    SCOPEfuncs_out( s, level );
    SCOPEprocs_out( s, level );
}

/** output one const - used in SCOPEconsts_out, below */
void SCOPEconst_out( Variable v, int level, size_t max_indent ) {
    size_t old_indent2;

    /* print attribute name */
    raw( "%*s%-*s :", level + 2, "",
         max_indent, v->name->symbol.name );

    /* print attribute type */
    if( VARget_optional( v ) ) {
        wrap( " OPTIONAL" );
    }

    /* let type definition stick out a bit to the left if it's on a new line */
    old_indent2 = indent2;
    if( indent2 > 4 ) {
        indent2 -= 4;
    }
    TYPE_head_out( v->type, NOLEVEL );
    indent2 = old_indent2;

    if( v->initializer ) {
        int old_ll = exppp_linelength; /* so exppp_linelength can be restored */
        raw( " :=" );

        /* let '[' on first line of initializer stick out so strings are aligned */
        raw( "\n%*s", indent2 - 2, "" );

        if( exppp_aggressively_wrap_consts ) {
            /* causes wrap() to always begin new line */
            exppp_linelength = indent2;
        }
        EXPR_out( v->initializer, 0 );
        exppp_linelength = old_ll;
    }

    raw( ";\n" );
}

/** output all consts in this scope */
void SCOPEconsts_out( Scope s, int level ) {
    Variable v;
    DictionaryEntry de;
    size_t max_indent = 0;
    Dictionary d = s->symbol_table;

    /* checks length of constant names */
    DICTdo_type_init( d, &de, OBJ_VARIABLE );
    while( 0 != ( v = ( Variable )DICTdo( &de ) ) ) {
        if( !v->flags.constant ) {
            continue;
        }
        if( strlen( v->name->symbol.name ) > max_indent ) {
            max_indent = strlen( v->name->symbol.name );
        }
    }

    if( !max_indent ) {
        return;
    }

    first_newline();

    raw( "%*sCONSTANT\n", level, "" );

    /* if max_indent is too big, wrap() won't insert any newlines
     * fiddled with this until it looked ok on 242 arm
     */
    if( ( max_indent + 20 ) > ( size_t ) exppp_linelength / 2 ) {
        max_indent = ( size_t ) exppp_linelength / 3;
    }
    indent2 = level + max_indent + strlen( ": ab" ) + exppp_continuation_indent;

    if( !exppp_alphabetize ) {
        DICTdo_type_init( d, &de, OBJ_VARIABLE );
        while( 0 != ( v = ( Variable )DICTdo( &de ) ) ) {
            if( !v->flags.constant ) {
                continue;
            }
            SCOPEconst_out( v, level, max_indent );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( d, &de, OBJ_VARIABLE );
        while( 0 != ( v = ( Variable )DICTdo( &de ) ) ) {
            if( !v->flags.constant ) {
                continue;
            }
            SCOPEaddvars_inorder( alpha, v );
        }
        LISTdo( alpha, cnst, Variable ) {
            SCOPEconst_out( cnst, level, max_indent );
        } LISTod
        LISTfree( alpha );
    }
    raw( "%*sEND_CONSTANT;\n", level, "" );
}

/** insert variable v into list, keeping the list ordered by ascending v->offset */
void SCOPElocals_order( Linked_List list, Variable v ) {
    LISTdo_links( list, link ) {
        if( v->offset < ( (Variable) link->data )->offset ) {
            LISTadd_before( list, link, (Generic) v );
            return;
        }
    } LISTod
    LISTadd_last( list, (Generic) v );
}

void SCOPElocals_out( Scope s, int level ) {
    Variable v;
    DictionaryEntry de;
    Linked_List orderedLocals = 0; /**< this list is used to order the vars the same way they were in the file */
    size_t max_indent = 0;
    Dictionary d = s->symbol_table;

    DICTdo_type_init( d, &de, OBJ_VARIABLE );
    while( 0 != ( v = ( Variable )DICTdo( &de ) ) ) {
        if( v->flags.constant ) {
            continue;
        }
        if( v->flags.parameter ) {
            continue;
        }
        if( strlen( v->name->symbol.name ) > max_indent ) {
            max_indent = strlen( v->name->symbol.name );
        }
    }

    if( !max_indent ) {
        return;
    }

    first_newline();

    raw( "%*sLOCAL\n", level, "" );
    indent2 = level + max_indent + strlen( ": " ) + exppp_continuation_indent;

    DICTdo_type_init( d, &de, OBJ_VARIABLE );
    while( 0 != ( v = ( Variable )DICTdo( &de ) ) ) {
        if( v->flags.constant ) {
            continue;
        }
        if( v->flags.parameter ) {
            continue;
        }
        if( !orderedLocals ) {
            orderedLocals = LISTcreate();
            LISTadd_first( orderedLocals, (Generic) v );
        } else {
            /* sort by v->offset */
            SCOPElocals_order( orderedLocals, v );
        }
    }
    LISTdo( orderedLocals, var, Variable ) {
        /* print attribute name */
        raw( "%*s%-*s :", level + exppp_nesting_indent, "",
             max_indent, var->name->symbol.name );

        /* print attribute type */
        if( VARget_optional( var ) ) {
            wrap( " OPTIONAL" );
        }
        TYPE_head_out( var->type, NOLEVEL );

        if( var->initializer ) {
            wrap( " := " );
            EXPR_out( var->initializer, 0 );
        }

        raw( ";\n" );
    } LISTod
    LISTfree( orderedLocals );

    raw( "%*sEND_LOCAL;\n", level, "" );
}

/** print all entities in a scope */
void SCOPEentities_out( Scope s, int level ) {
    Entity e;
    DictionaryEntry de;

    if( exppp_alphabetize == false ) {
        DICTdo_type_init( s->symbol_table, &de, OBJ_ENTITY );
        while( 0 != ( e = ( Entity )DICTdo( &de ) ) ) {
            ENTITY_out( e, level );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( s->symbol_table, &de, OBJ_ENTITY );
        while( 0 != ( e = ( Entity )DICTdo( &de ) ) ) {
            SCOPEadd_inorder( alpha, e );
        }

        LISTdo( alpha, en, Entity ) {
            ENTITY_out( en, level );
        } LISTod

        LISTfree( alpha );
    }
}

/** print all types in a scope */
void SCOPEtypes_out( Scope s, int level ) {
    DictionaryEntry de;
    Type t;

    if( exppp_alphabetize == false ) {
        DICTdo_type_init( s->symbol_table, &de, OBJ_TYPE );
        while( 0 != ( t = ( Type )DICTdo( &de ) ) ) {
            TYPE_out( t, level );
        }
    } else {
        Linked_List alpha = LISTcreate();

        DICTdo_type_init( s->symbol_table, &de, OBJ_TYPE );
        while( 0 != ( t = ( Type )DICTdo( &de ) ) ) {
            SCOPEadd_inorder( alpha, t );
        }

        LISTdo( alpha, ty, Type ) {
            TYPE_out( ty, level );
        } LISTod

        LISTfree( alpha );
    }
}
