/*
 * [The "BSD license"]
 *  Copyright (c) 2010 Terence Parr
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions
 *  are met:
 *  1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *  2. 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.
 *  3. The name of the author may not be used to endorse or promote products
 *      derived from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
 */

package org.antlr.tool;

import org.antlr.runtime.SerializedGrammar;

import java.io.*;

/** Serialize a grammar into a highly compressed form with
 *  only the info needed to recognize sentences.
 *  FORMAT:
 *
 *  file ::= $ANTLR<version:byte><grammartype:byte><name:string>;<numRules:short><rules>
 *  rule ::= R<rulename:string>;B<nalts:short><alts>.
 *  alt  ::= A<elems>;
 *  elem ::= t<tokentype:short> | r<ruleIndex:short> | -<char:uchar><char:uchar> | ~<tokentype> | w
 */
public class GrammarSerializerFoo {
    protected DataOutputStream out;
    protected String filename;
    protected Grammar g;

    protected ByteArrayOutputStream altBuf;
    protected int numElementsInAlt = 0;

    public GrammarSerializerFoo(Grammar g) {
        this.g = g;
    }

    public void open(String filename) throws IOException {
        this.filename = filename;
        FileOutputStream fos = new FileOutputStream(filename);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        out = new DataOutputStream(bos);
        writeString(out, SerializedGrammar.COOKIE);
        out.writeByte(SerializedGrammar.FORMAT_VERSION);
    }

    public void close() throws IOException {
        if ( out!=null ) out.close();
        out = null;
    }


    // WRITE

    public void grammar(int grammarTokenType, String name) {
        try {
            /*
            switch ( grammarTokenType ) {
                case ANTLRParser.LEXER_GRAMMAR : out.writeByte('l'); break;
                case ANTLRParser.PARSER_GRAMMAR : out.writeByte('p'); break;
                case ANTLRParser.TREE_GRAMMAR: out.writeByte('t'); break;
                case ANTLRParser.COMBINED_GRAMMAR : out.writeByte('c'); break;
            }
            writeString(out, name);
            */
            out.writeShort(g.getRules().size());
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void rule(String name) {
        try {
            out.writeByte('R');
            writeString(out, name);
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void endRule() {
        try {
            out.writeByte('.');
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void block(int nalts) {
        try {
            out.writeByte('B');
            out.writeShort(nalts);
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void alt(GrammarAST alt) {
        numElementsInAlt = 0;
        try {
            out.writeByte('A');
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
        //streams.push(out);
        //altBuf = new ByteArrayOutputStream();
        //out = new DataOutputStream(altBuf);
    }

    public void endAlt() {
        try {
            //out.flush();
            //out = (DataOutputStream)streams.pop(); // restore previous stream
            out.writeByte(';');
            //out.writeShort(numElementsInAlt);
            //out.write(altBuf.toByteArray());
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void ruleRef(GrammarAST t) {
        numElementsInAlt++;
        try {
            out.writeByte('r');
            out.writeShort(g.getRuleIndex(t.getText()));
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void token(GrammarAST t) {
        numElementsInAlt++;
        try {
            out.writeByte('t');
            int ttype = g.getTokenType(t.getText());
            out.writeShort(ttype);
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void charLiteral(GrammarAST t) {
        numElementsInAlt++;
        try {
            if ( g.type!=Grammar.LEXER ) {
                out.writeByte('t');
                int ttype = g.getTokenType(t.getText());
                out.writeShort(ttype);
            }
            // else lexer???
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void wildcard(GrammarAST t) {
        numElementsInAlt++;
        try {
            out.writeByte('w');
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void range() { // must be char range
        numElementsInAlt++;
        try {
            out.writeByte('-');
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void not() {
        try {
            out.writeByte('~');
        }
        catch (IOException ioe) {
            ErrorManager.error(ErrorManager.MSG_CANNOT_WRITE_FILE, filename);
        }
    }

    public void writeString(DataOutputStream out, String s) throws IOException {
        out.writeBytes(s);
        out.writeByte(';');
    }
}
