/***************************************************************************
 *   Copyright (C) 2008-2019 by Terraneo Federico                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   As a special exception, if other files instantiate templates or use   *
 *   macros or inline functions from this file, or you compile this file   *
 *   and link it with other works to produce a work based on this file,    *
 *   this file does not by itself cause the resulting work to be covered   *
 *   by the GNU General Public License. However the source code for this   *
 *   file must still be made available in accordance with the GNU General  *
 *   Public License. This exception does not invalidate any other reasons  *
 *   why a work based on this file might be covered by the GNU General     *
 *   Public License.                                                       *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, see <http://www.gnu.org/licenses/>   *
 ***************************************************************************/

#include "libstdcpp_integration.h"
#include <cstdlib>
#include <unistd.h>
#include <cxxabi.h>
#if _MIOSIX_GCC_PATCH_MAJOR > 2
#include <thread>
#endif
//// Settings
#include "config/miosix_settings.h"
//// Console
#include "kernel/logging.h"
//// kernel interface
#include "kernel/kernel.h"

using namespace std;

//
// C++ exception support
// =====================

#if __cplusplus >= 201703L
#warning: TODO: Override new with alignment (libsupc++/new_opa.cc, new_opv.cc, ...
#warning: TODO: FIX __gthread_key_t in libstdc++/include/std/memory_resource
#endif

#ifdef __NO_EXCEPTIONS
/*
 * If not using exceptions, ovverride the default new, delete with
 * an implementation that does not throw, to minimze code size
 */
void *operator new(size_t size) noexcept
{
    return malloc(size);
}

void *operator new(size_t size, const std::nothrow_t&) noexcept
{
    return malloc(size);
}

void *operator new[](size_t size) noexcept
{
    return malloc(size);
}

void *operator new[](size_t size, const std::nothrow_t&) noexcept
{
    return malloc(size);
}

void operator delete(void *p) noexcept
{
    free(p);
}

void operator delete[](void *p) noexcept
{
    free(p);
}


void operator delete(void *p, std::size_t size) noexcept
{
    (void) size;

    free(p);
}

void operator delete[](void *p, std::size_t size) noexcept
{
    (void) size;

    free(p);
}

/**
 * \internal
 * The default version of these functions provided with libstdc++ require
 * exception support. This means that a program using pure virtual functions
 * incurs in the code size penalty of exception support even when compiling
 * without exceptions. By replacing the default implementations with these one
 * the problem is fixed.
 */
extern "C" void __cxxabiv1::__cxa_pure_virtual(void)
{
    errorLog("\n***Pure virtual method called\n");
    _exit(1);
}

extern "C" void __cxxabiv1::__cxa_deleted_virtual(void)
{
    errorLog("\n***Deleted virtual method called\n");
    _exit(1);
}

#if _MIOSIX_GCC_PATCH_MAJOR > 2
namespace std {
void terminate()  noexcept { _exit(1); } //Since GCC 9.2.0
void unexpected() noexcept { _exit(1); } //Since GCC 9.2.0
/*
 * This one comes from thread.cc, the need to call the class destructor makes it
 * call __cxa_end_cleanup which pulls in exception code.
 */
extern "C" void* execute_native_thread_routine(void* __p)
{
    thread::_State_ptr __t{ static_cast<thread::_State*>(__p) };
    __t->_M_run();
    return nullptr;
}
} //namespace std
#endif

/*
 * If not using exceptions, ovverride these functions with
 * an implementation that does not throw, to minimze code size
 */
namespace std {
void __throw_bad_exception() { _exit(1); }
void __throw_bad_alloc()  { _exit(1); }
void __throw_bad_cast() { _exit(1); }
void __throw_bad_typeid()  { _exit(1); }
void __throw_logic_error(const char*) { _exit(1); }
void __throw_domain_error(const char*) { _exit(1); }
void __throw_invalid_argument(const char*) { _exit(1); }
void __throw_length_error(const char*) { _exit(1); }
void __throw_out_of_range(const char*) { _exit(1); }
void __throw_out_of_range_fmt(const char*, ...) { exit(1); } //Since GCC 9.2.0
void __throw_runtime_error(const char*) { _exit(1); }
void __throw_range_error(const char*) { _exit(1); }
void __throw_overflow_error(const char*) { _exit(1); }
void __throw_underflow_error(const char*) { _exit(1); }
#if !defined(_MIOSIX_GCC_PATCH_MAJOR) || _MIOSIX_GCC_PATCH_MAJOR <= 2
void __throw_ios_failure(const char*) { _exit(1); } //Unused since GCC 9.2.0
#endif
void __throw_system_error(int) { _exit(1); }
void __throw_future_error(int) { _exit(1); }
void __throw_bad_function_call() { _exit(1); }
} //namespace std

namespace __cxxabiv1 {
extern "C" void __cxa_throw_bad_array_length() { exit(1); } //Since GCC 9.2.0
extern "C" void __cxa_bad_cast() { exit(1); } //Since GCC 9.2.0
extern "C" void __cxa_bad_typeid() { exit(1); } //Since GCC 9.2.0
extern "C" void __cxa_throw_bad_array_new_length() { exit(1); } //Since GCC 9.2.0
} //namespace __cxxabiv1

#endif //__NO_EXCEPTIONS

namespace miosix {

class CppReentrancyAccessor
{
public:
    static __cxxabiv1::__cxa_eh_globals *getEhGlobals()
    {
        return &miosix::Thread::getCurrentThread()->cppReentrancyData.eh_globals;
    }

    #ifndef __ARM_EABI__
    static void *getSjljPtr()
    {
        return miosix::Thread::getCurrentThread()->cppReentrancyData.sjlj_ptr;
    }

    static void setSjljPtr(void *ptr)
    {
        miosix::Thread::getCurrentThread()->cppReentrancyData.sjlj_ptr=ptr;
    }
    #endif //__ARM_EABI__
};

} //namespace miosix

/*
 * If exception support enabled, ensure it is thread safe.
 * The functions __cxa_get_globals_fast() and __cxa_get_globals() need to
 * return a per-thread data structure. Given that thread local storage isn't
 * implemented in Miosix, libstdc++ was patched to make these functions syscalls
 */
namespace __cxxabiv1
{

extern "C" __cxa_eh_globals* __cxa_get_globals_fast()
{
    return miosix::CppReentrancyAccessor::getEhGlobals();
}

extern "C" __cxa_eh_globals* __cxa_get_globals()
{
    return miosix::CppReentrancyAccessor::getEhGlobals();
}

#ifndef __ARM_EABI__
extern "C" void _Miosix_set_sjlj_ptr(void* ptr)
{
    miosix::CppReentrancyAccessor::setSjljPtr(ptr);
}

extern "C" void *_Miosix_get_sjlj_ptr()
{
    return miosix::CppReentrancyAccessor::getSjljPtr();
}
#endif //__ARM_EABI__

} //namespace __cxxabiv1

namespace __gnu_cxx {

/**
 * \internal
 * Replaces the default verbose terminate handler.
 * Avoids the inclusion of code to demangle C++ names, which saves code size
 * when using exceptions.
 */
void __verbose_terminate_handler()
{
    errorLog("\n***Unhandled exception thrown\n");
    _exit(1);
}

} //namespace __gnu_cxx




//
// C++ static constructors support, to achieve thread safety
// =========================================================

//This is weird, despite almost everywhere in GCC's documentation it is said
//that __guard is 8 bytes, it is actually only four.
union MiosixGuard
{
    miosix::Thread *owner;
    unsigned int flag;
};

namespace __cxxabiv1
{
/**
 * Used to initialize static objects only once, in a threadsafe way
 * \param g guard struct
 * \return 0 if object already initialized, 1 if this thread has to initialize
 * it, or lock if another thread has already started initializing it
 */
extern "C" int __cxa_guard_acquire(__guard *g)
{
    miosix::InterruptDisableLock dLock;
    volatile MiosixGuard *guard=reinterpret_cast<volatile MiosixGuard*>(g);
    for(;;)
    {
        if(guard->flag==1) return 0; //Object already initialized, good
        
        if(guard->flag==0)
        {
            //Object uninitialized, and no other thread trying to initialize it
            guard->owner=miosix::Thread::IRQgetCurrentThread();

            //guard->owner serves the double task of being the thread id of
            //the thread initializing the object, and being the flag to signal
            //that the object is initialized or not. If bit #0 of guard->owner
            //is @ 1 the object is initialized. All this works on the assumption
            //that Thread* pointers never have bit #0 @ 1, and this assetion
            //checks that this condition really holds
            if(guard->flag & 1) miosix::errorHandler(miosix::UNEXPECTED);
            return 1;
        }

        //If we get here, the object is being initialized by another thread
        if(guard->owner==miosix::Thread::IRQgetCurrentThread())
        {
            //Wait, the other thread initializing the object is this thread?!?
            //We have a recursive initialization error. Not throwing an
            //exception to avoid pulling in exceptions even with -fno-exception
            IRQerrorLog("Recursive initialization\r\n");
            _exit(1);
        }

        {
            miosix::InterruptEnableLock eLock(dLock);
            miosix::Thread::yield(); //Sort of a spinlock, a "yieldlock"...
        }
    }
}

/**
 * Called after the thread has successfully initialized the object
 * \param g guard struct
 */
extern "C" void __cxa_guard_release(__guard *g) noexcept
{
    miosix::InterruptDisableLock dLock;
    MiosixGuard *guard=reinterpret_cast<MiosixGuard*>(g);
    guard->flag=1;
}

/**
 * Called if an exception was thrown while the object was being initialized
 * \param g guard struct
 */
extern "C" void __cxa_guard_abort(__guard *g) noexcept
{
    miosix::InterruptDisableLock dLock;
    MiosixGuard *guard=reinterpret_cast<MiosixGuard*>(g);
    guard->flag=0;
}

} //namespace __cxxabiv1

//
// libatomic support, to provide thread safe atomic operation fallbacks
// ====================================================================

// Not using the fast version, as these may be used before the kernel is started

extern "C" unsigned int libat_quick_lock_n(void *ptr)
{
    (void) ptr;

    miosix::disableInterrupts();
    return 0;
}

extern "C" void libat_quick_unlock_n(void *ptr, unsigned int token)
{
    (void) ptr;
    (void) token;

    miosix::enableInterrupts();
}

// These are to implement "heavy" atomic operations, which are not used in
// libstdc++. For now let's keep them disbaled.

// extern "C" void libat_lock_n(void *ptr, size_t n)
// {
//     miosix::pauseKernel();
// }
// 
// extern "C" void libat_unlock_n(void *ptr, size_t n)
// {
//     miosix::restartKernel();
// }
