/** \file
 *  Implementation of the tree parser and overrides for the base recognizer
 */

// [The "BSD licence"]
// Copyright (c) 2005-2009 Jim Idle, Temporal Wave LLC
// http://www.temporal-wave.com
// http://www.linkedin.com/in/jimidle
//
// 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.

#include    <antlr3treeparser.h>

/* BASE Recognizer overrides
 */
static void                             mismatch            (pANTLR3_BASE_RECOGNIZER recognizer, ANTLR3_UINT32 ttype, pANTLR3_BITSET_LIST follow);

/* Tree parser API
 */
static void                     setTreeNodeStream           (pANTLR3_TREE_PARSER parser, pANTLR3_COMMON_TREE_NODE_STREAM input);
static pANTLR3_COMMON_TREE_NODE_STREAM
                                        getTreeNodeStream           (pANTLR3_TREE_PARSER parser);
static void                     freeParser                              (pANTLR3_TREE_PARSER parser);
static void *           getCurrentInputSymbol   (pANTLR3_BASE_RECOGNIZER recognizer, pANTLR3_INT_STREAM istream);
static void *           getMissingSymbol                (pANTLR3_BASE_RECOGNIZER recognizer, pANTLR3_INT_STREAM istream, pANTLR3_EXCEPTION      e,
                                                                                                ANTLR3_UINT32 expectedTokenType, pANTLR3_BITSET_LIST follow);


ANTLR3_API pANTLR3_TREE_PARSER
antlr3TreeParserNewStream(ANTLR3_UINT32 sizeHint, pANTLR3_COMMON_TREE_NODE_STREAM ctnstream, pANTLR3_RECOGNIZER_SHARED_STATE state)
{
        pANTLR3_TREE_PARSER         parser;

        /** Allocate tree parser memory
        */
        parser  =(pANTLR3_TREE_PARSER) ANTLR3_MALLOC(sizeof(ANTLR3_TREE_PARSER));

        if      (parser == NULL)
        {
                return  NULL;
        }

        /* Create and install a base recognizer which does most of the work for us
        */
        parser->rec =  antlr3BaseRecognizerNew(ANTLR3_TYPE_PARSER, sizeHint, state);

        if      (parser->rec == NULL)
        {
                parser->free(parser);
                return  NULL;
        }

        /* Ensure we can track back to the tree parser super structure
        * from the base recognizer structure
        */
        parser->rec->super      = parser;
        parser->rec->type       = ANTLR3_TYPE_TREE_PARSER;

        /* Install our base recognizer overrides
        */
        parser->rec->mismatch                           = mismatch;
        parser->rec->exConstruct                        = antlr3MTNExceptionNew;
        parser->rec->getCurrentInputSymbol      = getCurrentInputSymbol;
        parser->rec->getMissingSymbol           = getMissingSymbol;

        /* Install tree parser API
        */
        parser->getTreeNodeStream       =  getTreeNodeStream;
        parser->setTreeNodeStream       =  setTreeNodeStream;
        parser->free            =  freeParser;

        /* Install the tree node stream
        */
        parser->setTreeNodeStream(parser, ctnstream);

        return  parser;
}

/**
 * \brief
 * Creates a new Mismatched Tree Nde Exception and inserts in the recognizer
 * exception stack.
 *
 * \param recognizer
 * Context pointer for this recognizer
 *
 */
ANTLR3_API      void
antlr3MTNExceptionNew(pANTLR3_BASE_RECOGNIZER recognizer)
{
    /* Create a basic recognition exception structure
     */
    antlr3RecognitionExceptionNew(recognizer);

    /* Now update it to indicate this is a Mismatched token exception
     */
    recognizer->state->exception->name          = ANTLR3_MISMATCHED_TREE_NODE_NAME;
    recognizer->state->exception->type          = ANTLR3_MISMATCHED_TREE_NODE_EXCEPTION;

    return;
}


static void
freeParser      (pANTLR3_TREE_PARSER parser)
{
        if      (parser->rec != NULL)
        {
                // This may have ben a delegate or delegator parser, in which case the
                // state may already have been freed (and set to NULL therefore)
                // so we ignore the state if we don't have it.
                //
                if      (parser->rec->state != NULL)
                {
                        if      (parser->rec->state->following != NULL)
                        {
                                parser->rec->state->following->free(parser->rec->state->following);
                                parser->rec->state->following = NULL;
                        }
                }
            parser->rec->free(parser->rec);
            parser->rec = NULL;
    }

    ANTLR3_FREE(parser);
}

/** Set the input stream and reset the parser
 */
static void
setTreeNodeStream       (pANTLR3_TREE_PARSER parser, pANTLR3_COMMON_TREE_NODE_STREAM input)
{
    parser->ctnstream = input;
    parser->rec->reset          (parser->rec);
    parser->ctnstream->reset    (parser->ctnstream);
}

/** Return a pointer to the input stream
 */
static pANTLR3_COMMON_TREE_NODE_STREAM
getTreeNodeStream       (pANTLR3_TREE_PARSER parser)
{
    return  parser->ctnstream;
}


/** Override for standard base recognizer mismatch function
 *  as we have DOWN/UP nodes in the stream that have no line info,
 *  plus we want to alter the exception type.
 */
static void
mismatch            (pANTLR3_BASE_RECOGNIZER recognizer, ANTLR3_UINT32 ttype, pANTLR3_BITSET_LIST follow)
{
    recognizer->exConstruct(recognizer);
    recognizer->recoverFromMismatchedToken(recognizer, ttype, follow);
}

#ifdef ANTLR3_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4100)
#endif

// Default implementation is for parser and assumes a token stream as supplied by the runtime.
// You MAY need override this function if the standard TOKEN_STREAM is not what you are using.
//
static void *
getCurrentInputSymbol           (pANTLR3_BASE_RECOGNIZER recognizer, pANTLR3_INT_STREAM istream)
{
        pANTLR3_TREE_NODE_STREAM                tns;
    pANTLR3_COMMON_TREE_NODE_STREAM     ctns;

    tns     = (pANTLR3_TREE_NODE_STREAM)(istream->super);
    ctns    = tns->ctns;
        return tns->_LT(tns, 1);
}


// Default implementation is for parser and assumes a token stream as supplied by the runtime.
// You MAY need override this function if the standard BASE_TREE is not what you are using.
//
static void *
getMissingSymbol                        (pANTLR3_BASE_RECOGNIZER recognizer, pANTLR3_INT_STREAM istream, pANTLR3_EXCEPTION      e,
                                                                        ANTLR3_UINT32 expectedTokenType, pANTLR3_BITSET_LIST follow)
{
        pANTLR3_TREE_NODE_STREAM                tns;
    pANTLR3_COMMON_TREE_NODE_STREAM     ctns;
        pANTLR3_BASE_TREE                               node;
        pANTLR3_BASE_TREE                               current;
        pANTLR3_COMMON_TOKEN                    token;
        pANTLR3_STRING                                  text;
    ANTLR3_INT32                   i;

        // Dereference the standard pointers
        //
    tns     = (pANTLR3_TREE_NODE_STREAM)(istream->super);
    ctns    = tns->ctns;

        // Create a new empty node, by stealing the current one, or the previous one if the current one is EOF
        //
        current = tns->_LT(tns, 1);
    i       = -1;

        if      (current == &ctns->EOF_NODE.baseTree)
        {
                current = tns->_LT(tns, -1);
        i--;
        }
    while (((pANTLR3_COMMON_TREE)(current->super))->factory == NULL)
        {
                current = tns->_LT(tns, i--);
    }

        node    = (pANTLR3_BASE_TREE)current->dupNode(current);

        // Find the newly dupicated token
        //
        token   = node->getToken(node);

        // Create the token text that shows it has been inserted
        //
        token->setText8                 (token, (pANTLR3_UINT8)"<missing ");
        text = token->getText   (token);
        text->append8                   (text, (const char *)recognizer->state->tokenNames[expectedTokenType]);
        text->append8                   (text, (const char *)">");

        // Finally return the pointer to our new node
        //
        return  node;
}
#ifdef ANTLR3_WINDOWS
#pragma warning (pop)
#endif
