//
// Copyright (c) 2012-2013 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//

#include "compiler/DirectiveHandler.h"

#include <sstream>

#include "compiler/debug.h"
#include "compiler/Diagnostics.h"

static TBehavior getBehavior(const std::string& str)
{
    static const std::string kRequire("require");
    static const std::string kEnable("enable");
    static const std::string kDisable("disable");
    static const std::string kWarn("warn");

    if (str == kRequire) return EBhRequire;
    else if (str == kEnable) return EBhEnable;
    else if (str == kDisable) return EBhDisable;
    else if (str == kWarn) return EBhWarn;
    return EBhUndefined;
}

TDirectiveHandler::TDirectiveHandler(TExtensionBehavior& extBehavior,
                                     TDiagnostics& diagnostics,
                                     int& shaderVersion)
    : mExtensionBehavior(extBehavior),
      mDiagnostics(diagnostics),
      mShaderVersion(shaderVersion)
{
}

TDirectiveHandler::~TDirectiveHandler()
{
}

void TDirectiveHandler::handleError(const pp::SourceLocation& loc,
                                    const std::string& msg)
{
    mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc, msg, "", "");
}

void TDirectiveHandler::handlePragma(const pp::SourceLocation& loc,
                                     const std::string& name,
                                     const std::string& value)
{
    static const std::string kSTDGL("STDGL");
    static const std::string kOptimize("optimize");
    static const std::string kDebug("debug");
    static const std::string kOn("on");
    static const std::string kOff("off");

    bool invalidValue = false;
    if (name == kSTDGL)
    {
        // The STDGL pragma is used to reserve pragmas for use by future
        // revisions of GLSL. Ignore it.
        return;
    }
    else if (name == kOptimize)
    {
        if (value == kOn) mPragma.optimize = true;
        else if (value == kOff) mPragma.optimize = false;
        else invalidValue = true;
    }
    else if (name == kDebug)
    {
        if (value == kOn) mPragma.debug = true;
        else if (value == kOff) mPragma.debug = false;
        else invalidValue = true;
    }
    else
    {
        mDiagnostics.report(pp::Diagnostics::UNRECOGNIZED_PRAGMA, loc, name);
        return;
    }

    if (invalidValue)
      mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc,
                             "invalid pragma value", value,
                             "'on' or 'off' expected");
}

void TDirectiveHandler::handleExtension(const pp::SourceLocation& loc,
                                        const std::string& name,
                                        const std::string& behavior)
{
    static const std::string kExtAll("all");

    TBehavior behaviorVal = getBehavior(behavior);
    if (behaviorVal == EBhUndefined)
    {
        mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc,
                               "behavior", name, "invalid");
        return;
    }

    if (name == kExtAll)
    {
        if (behaviorVal == EBhRequire)
        {
            mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc,
                                   "extension", name,
                                   "cannot have 'require' behavior");
        }
        else if (behaviorVal == EBhEnable)
        {
            mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc,
                                   "extension", name,
                                   "cannot have 'enable' behavior");
        }
        else
        {
            for (TExtensionBehavior::iterator iter = mExtensionBehavior.begin();
                 iter != mExtensionBehavior.end(); ++iter)
                iter->second = behaviorVal;
        }
        return;
    }

    TExtensionBehavior::iterator iter = mExtensionBehavior.find(name);
    if (iter != mExtensionBehavior.end())
    {
        iter->second = behaviorVal;
        return;
    }

    pp::Diagnostics::Severity severity = pp::Diagnostics::ERROR;
    switch (behaviorVal) {
      case EBhRequire:
        severity = pp::Diagnostics::ERROR;
        break;
      case EBhEnable:
      case EBhWarn:
      case EBhDisable:
        severity = pp::Diagnostics::WARNING;
        break;
      default:
        UNREACHABLE();
        break;
    }
    mDiagnostics.writeInfo(severity, loc,
                           "extension", name, "is not supported");
}

void TDirectiveHandler::handleVersion(const pp::SourceLocation& loc,
                                      int version)
{
    if (version == 100 ||
        version == 300)
    {
        mShaderVersion = version;
    }
    else
    {
        std::stringstream stream;
        stream << version;
        std::string str = stream.str();
        mDiagnostics.writeInfo(pp::Diagnostics::ERROR, loc,
                               "version number", str, "not supported");
    }
}
