/* Copyright (c) 2012-2017 The ANTLR Project. All rights reserved.
 * Use of this file is governed by the BSD 3-clause license that
 * can be found in the LICENSE.txt file in the project root.
 */

#pragma once

#include "antlr4-common.h"

namespace antlrcpp {

std::string join(std::vector<std::string> strings, const std::string& separator);
std::map<std::string, size_t> toMap(const std::vector<std::string>& keys);
std::string escapeWhitespace(std::string str, bool escapeSpaces);
std::string toHexString(const int t);
std::string arrayToString(const std::vector<std::string>& data);
std::string replaceString(const std::string& s, const std::string& from, const std::string& to);
std::vector<std::string> split(const std::string& s, const std::string& sep, int count);
std::string indent(
    const std::string& s, const std::string& indentation, bool includingFirst = true);

// Using RAII + a lambda to implement a "finally" replacement.
struct FinalAction {
    FinalAction(std::function<void()> f) : _cleanUp{f} {}
    FinalAction(FinalAction&& other)
        : _cleanUp(std::move(other._cleanUp)), _enabled(other._enabled) {
        other._enabled = false; // Don't trigger the lambda after ownership has moved.
    }
    ~FinalAction() {
        if (_enabled)
            _cleanUp();
    }

    void disable() { _enabled = false; }

private:
    std::function<void()> _cleanUp;
    bool _enabled{true};
};

ANTLR4CPP_PUBLIC FinalAction finally(std::function<void()> f);

// Convenience functions to avoid lengthy dynamic_cast() != nullptr checks in many places.
template<typename T1, typename T2>
inline bool is(T2* obj) { // For pointer types.
    return dynamic_cast<typename std::add_const<T1>::type>(obj) != nullptr;
}

template<typename T1, typename T2>
inline bool is(Ref<T2> const& obj) { // For shared pointers.
    return dynamic_cast<T1*>(obj.get()) != nullptr;
}

template<typename T>
std::string toString(const T& o) {
    std::stringstream ss;
    // typeid gives the mangled class name, but that's all what's possible
    // in a portable way.
    ss << typeid(o).name() << "@" << std::hex << reinterpret_cast<uintptr_t>(&o);
    return ss.str();
}

// Get the error text from an exception pointer or the current exception.
std::string what(std::exception_ptr eptr = std::current_exception());

class SingleWriteMultipleReadLock {
public:
    void readLock();
    void readUnlock();
    void writeLock();
    void writeUnlock();

private:
    std::condition_variable _readerGate;
    std::condition_variable _writerGate;

    std::mutex _mutex;
    size_t _activeReaders = 0;
    size_t _waitingWriters = 0;
    size_t _activeWriters = 0;
};

} // namespace antlrcpp
