//
// Copyright 2002 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.
//

#ifndef COMPILER_TRANSLATOR_INFOSINK_H_
#define COMPILER_TRANSLATOR_INFOSINK_H_

#include <math.h>
#include <stdlib.h>
#include "GLSLANG/ShaderLang.h"
#include "compiler/translator/Common.h"
#include "compiler/translator/Severity.h"

namespace sh
{

class ImmutableString;
class TField;
class TSymbol;
class TType;

// Returns the fractional part of the given floating-point number.
inline float fractionalPart(float f)
{
    float intPart = 0.0f;
    return modff(f, &intPart);
}

class ImmutableString;

//
// Encapsulate info logs for all objects that have them.
//
// The methods are a general set of tools for getting a variety of
// messages and types inserted into the log.
//
class TInfoSinkBase
{
  public:
    TInfoSinkBase() {}

    template <typename T>
    TInfoSinkBase &operator<<(const T &t)
    {
        TPersistStringStream stream = sh::InitializeStream<TPersistStringStream>();
        stream << t;
        sink.append(stream.str());
        return *this;
    }
    // Override << operator for specific types. It is faster to append strings
    // and characters directly to the sink.
    TInfoSinkBase &operator<<(char c)
    {
        sink.append(1, c);
        return *this;
    }
    TInfoSinkBase &operator<<(const char *str)
    {
        sink.append(str);
        return *this;
    }
    TInfoSinkBase &operator<<(const TPersistString &str)
    {
        sink.append(str);
        return *this;
    }
    TInfoSinkBase &operator<<(const TString &str)
    {
        sink.append(str.c_str());
        return *this;
    }
    TInfoSinkBase &operator<<(const ImmutableString &str);

    TInfoSinkBase &operator<<(const TType &type);
    TInfoSinkBase &operator<<(const TSymbol &symbol);
    TInfoSinkBase &operator<<(const TField &symbol);

    // Make sure floats are written with correct precision.
    TInfoSinkBase &operator<<(float f)
    {
        // Make sure that at least one decimal point is written. If a number
        // does not have a fractional part, the default precision format does
        // not write the decimal portion which gets interpreted as integer by
        // the compiler.
        TPersistStringStream stream = sh::InitializeStream<TPersistStringStream>();
        if (fractionalPart(f) == 0.0f)
        {
            stream.precision(1);
            stream << std::showpoint << std::fixed << f;
        }
        else
        {
            stream.unsetf(std::ios::fixed);
            stream.unsetf(std::ios::scientific);
            stream.precision(9);
            stream << f;
        }
        sink.append(stream.str());
        return *this;
    }
    // Write boolean values as their names instead of integral value.
    TInfoSinkBase &operator<<(bool b)
    {
        const char *str = b ? "true" : "false";
        sink.append(str);
        return *this;
    }

    void erase()
    {
        sink.clear();
        binarySink.clear();
    }
    int size() { return static_cast<int>(isBinary() ? binarySink.size() : sink.size()); }

    const TPersistString &str() const
    {
        ASSERT(!isBinary());
        return sink;
    }
    const char *c_str() const
    {
        ASSERT(!isBinary());
        return sink.c_str();
    }

    void prefix(Severity severity);
    void location(int file, int line);

    bool isBinary() const { return !binarySink.empty(); }
    void setBinary(BinaryBlob &&binary) { binarySink = std::move(binary); }
    const BinaryBlob &getBinary() const
    {
        ASSERT(isBinary());
        return binarySink;
    }

  private:
    // The data in the info sink is either in human readable form (|sink|) or binary (|binarySink|).
    TPersistString sink;
    BinaryBlob binarySink;
};

class TInfoSink
{
  public:
    TInfoSinkBase info;
    TInfoSinkBase debug;
    TInfoSinkBase obj;
};

}  // namespace sh

#endif  // COMPILER_TRANSLATOR_INFOSINK_H_
