// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "glslast.h"
#include "glslastvisitor.h"
#include "glslparsertable_p.h"

using namespace GLSL;

void AST::accept(Visitor *visitor)
{
    if (visitor->preVisit(this))
        accept0(visitor);
    visitor->postVisit(this);
}

void AST::accept(AST *ast, Visitor *visitor)
{
    if (ast)
        ast->accept(visitor);
}

void TranslationUnitAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(declarations, visitor);
    }
    visitor->endVisit(this);
}

void IdentifierExpressionAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

void LiteralExpressionAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

void BinaryExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(left, visitor);
        accept(right, visitor);
    }
    visitor->endVisit(this);
}

void UnaryExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expr, visitor);
    visitor->endVisit(this);
}

void TernaryExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(first, visitor);
        accept(second, visitor);
        accept(third, visitor);
    }
    visitor->endVisit(this);
}

void AssignmentExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(variable, visitor);
        accept(value, visitor);
    }
    visitor->endVisit(this);
}

void MemberAccessExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expr, visitor);
    visitor->endVisit(this);
}

void FunctionCallExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(expr, visitor);
        accept(id, visitor);
        accept(arguments, visitor);
    }
    visitor->endVisit(this);
}

void FunctionIdentifierAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(type, visitor);
    visitor->endVisit(this);
}

void DeclarationExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(type, visitor);
        accept(initializer, visitor);
    }
    visitor->endVisit(this);
}

void ExpressionStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expr, visitor);
    visitor->endVisit(this);
}

void CompoundStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(statements, visitor);
    visitor->endVisit(this);
}

void IfStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(condition, visitor);
        accept(thenClause, visitor);
        accept(elseClause, visitor);
    }
    visitor->endVisit(this);
}

void WhileStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(condition, visitor);
        accept(body, visitor);
    }
    visitor->endVisit(this);
}

void DoStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(body, visitor);
        accept(condition, visitor);
    }
    visitor->endVisit(this);
}

void ForStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(init, visitor);
        accept(condition, visitor);
        accept(increment, visitor);
        accept(body, visitor);
    }
    visitor->endVisit(this);
}

void JumpStatementAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

void ReturnStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expr, visitor);
    visitor->endVisit(this);
}

void SwitchStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(expr, visitor);
        accept(body, visitor);
    }
    visitor->endVisit(this);
}

void CaseLabelStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expr, visitor);
    visitor->endVisit(this);
}

void DeclarationStatementAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(decl, visitor);
    visitor->endVisit(this);
}

BasicTypeAST::BasicTypeAST(int _token, const char *_name)
    : TypeAST(Kind_BasicType), token(_token), name(_name)
{
    switch (token) {
    case GLSLParserTable::T_VOID:
    case GLSLParserTable::T_BOOL:
    case GLSLParserTable::T_BVEC2:
    case GLSLParserTable::T_BVEC3:
    case GLSLParserTable::T_BVEC4:
        prec = PrecNotValid;
        break;
    default:
        prec = PrecUnspecified;
        break;
    }
}

void BasicTypeAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

TypeAST::Precision BasicTypeAST::precision() const
{
    return prec;
}

bool BasicTypeAST::setPrecision(Precision precision)
{
    if (prec == PrecNotValid)
        return false;
    prec = precision;
    return true;
}

void NamedTypeAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

TypeAST::Precision NamedTypeAST::precision() const
{
    // Named types are typically structs, which cannot have their precision set.
    return PrecNotValid;
}

bool NamedTypeAST::setPrecision(Precision)
{
    return false;
}

void ArrayTypeAST::ArraySpecAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(size, visitor);
    }
}

void ArrayTypeAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(elementType, visitor); // REMOVE
        accept(arraySpecifier, visitor);
        accept(size, visitor); // REMOVE
    }
    visitor->endVisit(this);
}

TypeAST::Precision ArrayTypeAST::precision() const
{
    return elementType ? elementType->precision() : PrecNotValid;
}

bool ArrayTypeAST::setPrecision(Precision precision)
{
    if (elementType)
        return elementType->setPrecision(precision);
    else
        return false;
}

void StructTypeAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(fields, visitor);
    visitor->endVisit(this);
}

TypeAST::Precision StructTypeAST::precision() const
{
    return PrecNotValid;
}

bool StructTypeAST::setPrecision(Precision)
{
    // Structs cannot have a precision set.
    return false;
}

void StructTypeAST::Field::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(type, visitor);
    visitor->endVisit(this);
}

void StructTypeAST::Field::setInnerType(TypeAST *innerType)
{
    if (!innerType)
        return;
    TypeAST **parent = &type;
    TypeAST *inner = type;
    while (inner != nullptr) {
        ArrayTypeAST *array = inner->asArrayType();
        if (!array)
            break;
        parent = &(array->elementType);
        inner = array->elementType;
    }
    *parent = innerType;
}

List<StructTypeAST::Field *> *StructTypeAST::fixInnerTypes(TypeAST *innerType, List<Field *> *fields)
{
    if (!fields)
        return fields;
    List<Field *> *head = fields->next;
    List<Field *> *current = head;
    do {
        current->value->setInnerType(innerType);
        current = current->next;
    } while (current && current != head);
    return fields;
}

void LayoutQualifierAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
    }
    visitor->endVisit(this);
}

void QualifiedTypeAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(type, visitor);
        accept(layout_list, visitor);
    }
    visitor->endVisit(this);
}

void PrecisionDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(type, visitor);
    visitor->endVisit(this);
}

void ParameterDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(type, visitor);
    visitor->endVisit(this);
}

void VariableDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(type, visitor);
        accept(initializer, visitor);
    }
    visitor->endVisit(this);
}

TypeAST *VariableDeclarationAST::declarationType(List<DeclarationAST *> *decls)
{
    VariableDeclarationAST *var = decls->value->asVariableDeclaration();
    return var ? var->type : nullptr;
}

void TypeDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(type, visitor);
    visitor->endVisit(this);
}

void TypeAndVariableDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(typeDecl, visitor);
        accept(varDecl, visitor);
    }
    visitor->endVisit(this);
}

void InvariantDeclarationAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    visitor->endVisit(this);
}

void InitDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(decls, visitor);
    visitor->endVisit(this);
}

void FunctionDeclarationAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this)) {
        accept(returnType, visitor);
        accept(params, visitor);
        accept(body, visitor);
    }
    visitor->endVisit(this);
}

void InterfaceBlockAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(fields, visitor);
    visitor->endVisit(this);
}

TypeAST::Precision InterfaceBlockAST::precision() const
{
    return PrecNotValid;
}

bool InterfaceBlockAST::setPrecision(Precision)
{
    // interface blocks cannot have a precision set.
    return false;
}

void SubroutineTypeAST::accept0(Visitor *visitor)
{
    visitor->visit(this);
    // ignore the function decl to avoid declaring it for real
    visitor->endVisit(this);
}

TypeAST::Precision SubroutineTypeAST::precision() const
{
    return PrecNotValid;
}

bool SubroutineTypeAST::setPrecision(Precision)
{
    return false;
}

void InitializerListExpressionAST::accept0(Visitor *visitor)
{
    if (visitor->visit(this))
        accept(expressions, visitor);
    visitor->endVisit(this);
}
