/* *************************************************************************
                          format.out.g  -  interpreter for formatted output
                             -------------------
    begin                : July 22 2002
    copyright            : (C) 2002 by Marc Schellens
    email                : m_schellens@hotmail.com
 ***************************************************************************/

/* *************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

header "pre_include_cpp" {
#include "includefirst.hpp"
}

header "post_include_cpp" {
    // gets inserted after the antlr generated includes in the cpp file
}

header {

#include <fstream>
#include <cassert>

//***test
#include "print_tree.hpp"

#include "envt.hpp"

#include "fmtnode.hpp"
//#include "getfmtast.hpp"    // for FMTNodeFactory;    

#include "gdljournal.hpp"
}

options {
	language="Cpp";
//	genHashLines = true;
	genHashLines = false;
	namespaceStd="std";         // cosmetic option to get rid of long defines
	namespaceAntlr="antlr";     // cosmetic option to get rid of long defines
}	

// the format Parser *********************************************
class FMTOut extends TreeParser;

options {
	importVocab = FMT;	// use vocab generated by format lexer
	buildAST = false;
  	ASTLabelType = "RefFMTNode";
    defaultErrorHandler = false;
//    defaultErrorHandler = true;
//    codeGenBitsetTestThreshold=999;
//    codeGenMakeSwitchThreshold=1;
}

{
public:
    FMTOut( RefFMTNode fmt, std::ostream* os_, EnvT* e_, int parOffset)
    : antlr::TreeParser(), os(os_), e( e_), nextParIx( parOffset),
	valIx(0), termFlag(false), nonlFlag(false), nElements(0)
    {
        std::ostringstream* osLocal;
        std::auto_ptr<std::ostream> osLocalGuard;
        if( *os_ == std::cout)
            {
                osLocal = new std::ostringstream();
                osLocalGuard.reset( osLocal);
                os = osLocal;
            }
        else
            {
                os = os_;
            }

        nParam = e->NParam();

        NextPar();
    
        GDLStream* j = lib::get_journal();

        if( j != NULL && j->OStream() == (*os)) 
            (*os) << lib::JOURNALCOMMENT;

        format( fmt);
        
        SizeT nextParIxComp = nextParIx;
        SizeT valIxComp = valIx;

        // format reversion
        while( actPar != NULL)
        {
           (*os) << '\n';
            
            if( j != NULL && j->OStream() == (*os)) 
                (*os) << lib::JOURNALCOMMENT;

            format_reversion( reversionAnker);            
 
           if( (nextParIx == nextParIxComp) && (valIx == valIxComp))   
                throw GDLException("Infinite format loop detected.");
         }
        
        os->seekp( 0, std::ios_base::end);

        if( !nonlFlag)
            {
                (*os) << '\n';
            }
        (*os) << std::flush;

        if( *os_ == std::cout)
            {
                os = os_;
                (*os) << osLocal->str();
                (*os) << std::flush;
            }
    }
    
private:
    void NextPar()
    {
        valIx = 0;
        if( nextParIx < nParam)
        {
            actPar = e->GetPar( nextParIx);
            if( actPar != NULL)
            nElements = actPar->ToTransfer();
            else
            nElements = 0;
        } 
        else 
        {
            actPar = NULL;
            nElements = 0;
        }
        nextParIx++;
    }

    void NextVal( SizeT n=1)
    {
        valIx += n;
        if( valIx >= nElements)
            NextPar();
    }
    
    std::ostream* os;
    EnvT*    e;
    SizeT   nextParIx;
    SizeT   valIx;

    bool termFlag;
    bool nonlFlag;

    SizeT   nParam;
    BaseGDL* actPar;
    SizeT nElements;

    RefFMTNode reversionAnker;
}

format
    : #(fmt:FORMAT 
            { goto realCode; } // fool ANTLR 
            q (f q)+ // this gets never executed
            {
                realCode:

                reversionAnker = #fmt;
                
                RefFMTNode blk = _t; // q (f q)+

                for( int r = #fmt->getRep(); r > 0; r--)
                {
                    q( blk);
                    _t = _retTree;

                    for (;;) 
                    {
                        if( _t == static_cast<RefFMTNode>(antlr::nullAST))
                            _t = ASTNULL;

                        switch ( _t->getType()) {
                        case FORMAT:
                        case TL:
                        case TR:
                        case TERM:
                        case NONL:
                        case Q: case T: case X: case A:
                        case F: case D: case E: case G:
                        case I: case O: case B: case Z: case ZZ: case C:
                            {
                                if( actPar == NULL && termFlag) goto endFMT;
                                // no break
                            }
                        case STRING:
                        case CSTRING:
                            {
                                f(_t);
//                                if( actPar == NULL && termFlag) goto endFMT;
                                _t = _retTree;
                                q(_t);
                                _t = _retTree;
                                break; // out of switch
                            }
                        default:
                            goto endFMT;
                        }
                    }
                    
                    endFMT: // end of one repetition
                    if( actPar == NULL && termFlag) break;
                }
            }
        )
    ;

format_reversion
    : format 
        { goto realCode;}
        q (f q)*
        {
            realCode:
            
            q( _t);
            _t = _retTree;
            
            for (;;) 
            {
                if( _t == static_cast<RefFMTNode>(antlr::nullAST))
                _t = ASTNULL;
                
                switch ( _t->getType()) {
                case FORMAT:
                case STRING:
                case CSTRING:
                case TL:
                case TR:
                case TERM:
                case NONL:
                case Q: case T: case X: case A:
                case F: case D: case E: case G:
                case I: case O: case B: case Z: case ZZ: case C:
                    {
                        f(_t);
                        if( actPar == NULL) goto endFMT;
                        _t = _retTree;
                        q(_t);
                        _t = _retTree;
                        break; // out of switch
                    }
                default:
                    goto endFMT;
                }
            }
            endFMT: // end of one repetition
        }
    ;

q
    : (s:SLASH 
            {
                // only one newline to journal file
                GDLStream* j = lib::get_journal();
                if( j != NULL && j->OStream() == (*os))
                    (*os) << '\n' << lib::JOURNALCOMMENT;
                else
                    for( int r=s->getRep(); r > 0; r--) (*os) << '\n';
            }
        )?
    ;

f_csubcode // note: IDL doesn't allow hollerith strings inside C()
    : s:STRING { (*os) << s->getText(); }
//    | CSTRING // *** requires special handling
    | tl:TL 
        { 
            SizeT actP  = os->tellp(); 
            int    tlVal = tl->getW();
            if( tlVal > actP)
                os->seekp( 0);
            else
                os->seekp( actP - tlVal);
        }
    | tr:TR 
        { 
            int    tlVal = tl->getW();
            for( int i=tlVal; i>0; --i)
            (*os) << " ";
//            os->seekp( tlVal, std::ios_base::cur);
        }
    ;

f
    : TERM { termFlag = true; }
    | NONL { nonlFlag = true; }
    | Q // ignored on output
    | t:T
        { 
            int    tVal = t->getW();
            assert( tVal >= 1);
            os->seekp( tVal-1, std::ios_base::beg);
        }
    | f_csubcode
    | x
    | format // following are repeatable formats
    | a:A 
        {
            if( actPar == NULL) break;

            int r = a->getRep();
            int w = a->getW();
            do {
                SizeT tCount = actPar->OFmtA( os, valIx, r, w);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | ff:F
        {
            if( actPar == NULL) break;
            
            int r = ff->getRep();
            int w = ff->getW();
            int d = ff->getD();
            char f = ff->getFill();
            do {
                SizeT tCount = actPar->OFmtF( os, valIx, r, w, d, f);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
//  | d:D // D is transformed to F
    | ee:E
        {
            if( actPar == NULL) break;
            
            int r = ee->getRep();
            int w = ee->getW();
            int d = ee->getD();
            char f = ee->getFill();
            do {
                SizeT tCount = actPar->OFmtF( os, valIx, r, w, d, f,
                                               BaseGDL::SCIENTIFIC);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | g:G
        {
            if( actPar == NULL) break;
            
            int r = g->getRep();
            int w = g->getW();
            int d = g->getD();
            int f = g->getFill();
            do {
                SizeT tCount = actPar->OFmtF( os, valIx, r, w, d, f,
                                               BaseGDL::AUTO);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | i:I
        {
            if( actPar == NULL) break;
            
            int r = i->getRep();
            int w = i->getW();
            int d = i->getD();
            int f = i->getFill();
            do {
                SizeT tCount = actPar->OFmtI( os, valIx, r, w, d, f,
                                               BaseGDL::DEC);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | o:O
        {
            if( actPar == NULL) break;
            
            int r = o->getRep();
            int w = o->getW();
            int d = o->getD();
            int f = o->getFill();
            do {
                SizeT tCount = actPar->OFmtI( os, valIx, r, w, d, f,
                                               BaseGDL::OCT);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | b:B
        {
            if( actPar == NULL) break;
            
            int r = b->getRep();
            int w = b->getW();
            int d = b->getD();
            int f = b->getFill();
            do {
                SizeT tCount = actPar->OFmtI( os, valIx, r, w, d, f,
                                               BaseGDL::BIN);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | z:Z
        {
            if( actPar == NULL) break;
            
            int r = z->getRep();
            int w = z->getW();
            int d = z->getD();
            int f = z->getFill();
            do {
                SizeT tCount = actPar->OFmtI( os, valIx, r, w, d, f,
                                               BaseGDL::HEX);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | zz:ZZ
        {
            if( actPar == NULL) break;
            
            int r = zz->getRep();
            int w = zz->getW();
            int d = zz->getD();
            int f = zz->getFill();
            do {
                SizeT tCount = actPar->OFmtI( os, valIx, r, w, d, f,
                                               BaseGDL::HEXL);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | #(c:C (csubcode)+) 
    ;   

csubcode
    : c1:CMOA
        {
            if( actPar == NULL) break;
            
            int r = c1->getRep();
            int w = c1->getW();
            int d = c1->getD();
            int f = c1->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CMOA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }

    | c2:CMoA
        {
            if( actPar == NULL) break;
            
            int r = c2->getRep();
            int w = c2->getW();
            int d = c2->getD();
            int f = c2->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CMoA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c3:CmoA
        {
            if( actPar == NULL) break;
            
            int r = c3->getRep();
            int w = c3->getW();
            int d = c3->getD();
            int f = c3->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CmoA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c4:CHI
        {
            if( actPar == NULL) break;
            
            int r = c4->getRep();
            int w = c4->getW();
            int d = c4->getD();
            int f = c4->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CHI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c5:ChI
        {
            if( actPar == NULL) break;
            
            int r = c5->getRep();
            int w = c5->getW();
            int d = c5->getD();
            int f = c5->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::ChI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c6:CDWA
        {
            if( actPar == NULL) break;
            
            int r = c6->getRep();
            int w = c6->getW();
            int d = c6->getD();
            int f = c6->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CDWA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c7:CDwA
        {
            if( actPar == NULL) break;
            
            int r = c7->getRep();
            int w = c7->getW();
            int d = c7->getD();
            int f = c7->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CDwA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c8:CdwA
        {
            if( actPar == NULL) break;
            
            int r = c8->getRep();
            int w = c8->getW();
            int d = c8->getD();
            int f = c8->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CdwA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c9:CAPA
        {
            if( actPar == NULL) break;
            
            int r = c9->getRep();
            int w = c9->getW();
            int d = c9->getD();
            int f = c9->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CAPA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c10:CApA
        {
            if( actPar == NULL) break;
            
            int r = c10->getRep();
            int w = c10->getW();
            int d = c10->getD();
            int f = c10->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CApA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c11:CapA
        {
            if( actPar == NULL) break;
            
            int r = c11->getRep();
            int w = c11->getW();
            int d = c11->getD();
            int f = c11->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CapA);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c12:CMOI
        {
            if( actPar == NULL) break;
            
            int r = c12->getRep();
            int w = c12->getW();
            int d = c12->getD();
            int f = c12->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CMOI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c13:CDI 
        {
            if( actPar == NULL) break;
            
            int r = c13->getRep();
            int w = c13->getW();
            int d = c13->getD();
            int f = c13->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CDI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c14:CYI
        {
            if( actPar == NULL) break;
            
            int r = c14->getRep();
            int w = c14->getW();
            int d = c14->getD();
            int f = c14->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CYI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c15:CMI
        {
            if( actPar == NULL) break;
            
            int r = c15->getRep();
            int w = c15->getW();
            int d = c15->getD();
            int f = c15->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CMI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c16:CSI
        {
            if( actPar == NULL) break;
            
            int r = c16->getRep();
            int w = c16->getW();
            int d = c16->getD();
            int f = c16->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CSI);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | c17:CSF
        {
            if( actPar == NULL) break;
            
            int r = c17->getRep();
            int w = c17->getW();
            int d = c17->getD();
            int f = c17->getFill();
            do {
                SizeT tCount = actPar->OFmtCal( os, valIx, r, w, d, f,
                                               BaseGDL::CSF);
                r -= tCount;
                NextVal( tCount);
                if( actPar == NULL) break;
            } while( r>0);
        }
    | x
    | f_csubcode
    ;

x
    : tl:X 
        {
            if( _t != static_cast<RefFMTNode>(antlr::nullAST))
            {
                int    tlVal = #tl->getW();
                for( int i=tlVal; i>0; --i)
                (*os) << " ";
//                os->seekp( tlVal, std::ios_base::cur);
            }
            // for( int r=x->getW(); r > 0; r--) (*os) << ' ';
        }
    ;
