/*-------------------------------------------------------------------------
 * drawElements Quality Program Random Shader Generator
 * ----------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Shader Source Formatter.
 *//*--------------------------------------------------------------------*/

#include "rsgPrettyPrinter.hpp"
#include "deStringUtil.hpp"

namespace rsg
{

static const char *s_tokenStr[] = {
    DE_NULL,       // IDENTIFIER,
    "struct",      // STRUCT,
    "invariant",   // INVARIANT,
    "precision",   // PRECISION,
    "void",        // VOID,
    "break",       // BREAK,
    "continue",    // CONTINUE,
    "do ",         // DO,
    "while ",      // WHILE,
    "else ",       // ELSE,
    "for ",        // FOR,
    "if ",         // IF,
    "discard",     // DISCARD,
    "return ",     // RETURN,
    "++",          // INC_OP,
    "--",          // DEC_OP,
    "(",           // LEFT_PAREN,
    ")",           // RIGHT_PAREN,
    "[",           // LEFT_BRACKET,
    "]",           // RIGHT_BRACKET,
    "{",           // LEFT_BRACE,
    "}",           // RIGHT_BRACE,
    ".",           // DOT,
    ", ",          // COMMA,
    " : ",         // COLON,
    ";",           // SEMICOLON,
    " - ",         // MINUS,
    " + ",         // PLUS,
    " * ",         // MUL,
    " / ",         // DIV,
    " % ",         // MOD,
    " ? ",         // QUESTION,
    "bool",        // BOOL,
    "bvec2",       // BVEC2,
    "bvec3",       // BVEC3,
    "bvec4",       // BVEC4,
    "int",         // INT,
    "ivec2",       // IVEC2,
    "ivec3",       // IVEC3,
    "ivec4",       // IVEC4,
    "float",       // FLOAT,
    "vec2",        // VEC2,
    "vec3",        // VEC3,
    "vec4",        // VEC4,
    "mat2",        // MAT2,
    "mat3",        // MAT3,
    "mat4",        // MAT4,
    "sampler2D",   // SAMPLER2D,
    "samplerCube", // SAMPLERCUBE,
    DE_NULL,       // FLOAT_LITERAL,
    DE_NULL,       // INT_LITERAL,
    DE_NULL,       // BOOL_LITERAL,
    " = ",         // EQUAL,
    " *= ",        // MUL_ASSIGN,
    " /= ",        // DIV_ASSIGN,
    " += ",        // ADD_ASSIGN,
    " -= ",        // SUB_ASSIGN,
    " < ",         // CMP_LT,
    " > ",         // CMP_GT,
    " <= ",        // CMP_LE,
    " >= ",        // CMP_GE,
    " == ",        // CMP_EQ,
    " != ",        // CMP_NE,
    " && ",        // LOGICAL_AND,
    " || ",        // LOGICAL_OR,
    "!",           // LOGICAL_NOT,
    " ^^ ",        // LOGICAL_XOR,
    "attribute",   // ATTRIBUTE,
    "uniform",     // UNIFORM,
    "varying",     // VARYING,
    "const",       // CONST,
    "flat",        // FLAT,
    "highp",       // HIGH_PRECISION,
    "mediump",     // MEDIUM_PRECISION,
    "lowp",        // LOW_PRECISION,
    "in",          // IN,
    "out",         // OUT,
    "inout",       // INOUT,
    "layout",      // LAYOUT,
    "location",    // LOCATION,
    DE_NULL,       // INDENT_INC,
    DE_NULL,       // INDENT_DEC,
    "\n"           // NEWLINE,
};

PrettyPrinter::PrettyPrinter(std::ostringstream &str) : m_str(str), m_indentDepth(0)
{
}

inline const char *PrettyPrinter::getSimpleTokenStr(Token::Type token)
{
    DE_ASSERT(de::inBounds<int>(token, 0, (int)DE_LENGTH_OF_ARRAY(s_tokenStr)));
    return s_tokenStr[token];
}

void PrettyPrinter::append(const TokenStream &tokens)
{
    for (int ndx = 0; ndx < tokens.getSize(); ndx++)
        processToken(tokens[ndx]);
}

inline bool isIdentifierChar(char c)
{
    return de::inRange(c, 'a', 'z') || de::inRange(c, 'A', 'Z') || de::inRange(c, '0', '9') || c == '_';
}

void PrettyPrinter::processToken(const Token &token)
{
    bool prevIsIdentifierChar = m_line.length() > 0 && isIdentifierChar(m_line[m_line.length() - 1]);

    switch (token.getType())
    {
    case Token::IDENTIFIER:
        if (prevIsIdentifierChar)
            m_line += " ";
        m_line += token.getIdentifier();
        break;

    case Token::FLOAT_LITERAL:
    {
        std::string f = de::toString(token.getFloat());
        if (f.find('.') == std::string::npos)
            f += ".0"; // Make sure value parses as float
        m_line += f;
        break;
    }

    case Token::INT_LITERAL:
        m_line += de::toString(token.getInt());
        break;

    case Token::BOOL_LITERAL:
        m_line += (token.getBool() ? "true" : "false");
        break;

    case Token::INDENT_INC:
        m_indentDepth += 1;
        break;

    case Token::INDENT_DEC:
        m_indentDepth -= 1;
        break;

    case Token::NEWLINE:
        // Indent
        for (int i = 0; i < m_indentDepth; i++)
            m_str << "\t";

        // Flush line to source
        m_str << m_line + "\n";
        m_line = "";
        break;

    default:
    {
        const char *tokenStr = getSimpleTokenStr(token.getType());
        if (prevIsIdentifierChar && isIdentifierChar(tokenStr[0]))
            m_line += " ";
        m_line += tokenStr;
        break;
    }
    }
}

} // namespace rsg
