/*
 * Session process wrapper
 *
 *
 */

#include <QSocketNotifier>

#include "Configuration.h"
#include "UserSession.h"
#include "HelperApp.h"
#include "VirtualTerminal.h"
#include "XAuth.h"
#include "xorguserhelper.h"
#include "waylandhelper.h"

#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#ifdef Q_OS_FREEBSD
#include <login_cap.h>
#endif

namespace SDDM {
    UserSession::UserSession(HelperApp *parent)
        : QProcess(parent)
    {
        connect(this, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &UserSession::finished);
    }

    bool UserSession::start() {
        auto helper = qobject_cast<HelperApp*>(parent());
        QProcessEnvironment env = helper->session()->processEnvironment();

        bool isWaylandGreeter = false;
        if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("x11")) {
            QString command;
            if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) {
                command = m_path;
            } else {
                command = QStringLiteral("%1 \"%2\"").arg(mainConfig.X11.SessionCommand.get()).arg(m_path);
            }

            qInfo() << "Starting X11 session:" << m_displayServerCmd << command;
            if (m_displayServerCmd.isEmpty()) {
                auto args = QProcess::splitCommand(command);
                const auto program = args.takeFirst();
                QProcess::start(program, args);
            } else {
                QProcess::start(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-x11user"), {m_displayServerCmd, command});
            }

        } else if (env.value(QStringLiteral("XDG_SESSION_TYPE")) == QLatin1String("wayland")) {
            if (env.value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter")) {
                Q_ASSERT(!m_displayServerCmd.isEmpty());
                QProcess::start(QStringLiteral(LIBEXEC_INSTALL_DIR "/sddm-helper-start-wayland"), {m_displayServerCmd, m_path});
                isWaylandGreeter = true;
            } else {
                const QString cmd = QStringLiteral("%1 %2").arg(mainConfig.Wayland.SessionCommand.get()).arg(m_path);
                qInfo() << "Starting Wayland user session:" << cmd;
                QProcess::start(mainConfig.Wayland.SessionCommand.get(), QStringList{m_path});
                closeWriteChannel();
                closeReadChannel(QProcess::StandardOutput);
            }
        } else {
            qCritical() << "Unable to run user session: unknown session type";
        }

        const bool started = waitForStarted();
        m_cachedProcessId = processId();
        if (started) {
            return true;
        } else if (isWaylandGreeter) {
            // This is probably fine, we need the compositor to start first
            return true;
        }

        return false;
    }

    void UserSession::stop()
    {
        terminate();
        const bool isGreeter = processEnvironment().value(QStringLiteral("XDG_SESSION_CLASS")) == QLatin1String("greeter");

        // Wait longer for a session than a greeter
        if (!waitForFinished(isGreeter ? 5000 : 60000)) {
            kill();
            if (!waitForFinished(5000)) {
                qWarning() << "Could not fully finish the process" << program();
            }
        }

        Q_EMIT finished(Auth::HELPER_OTHER_ERROR);
    }

    QString UserSession::displayServerCommand() const
    {
        return m_displayServerCmd;
    }

    void UserSession::setDisplayServerCommand(const QString &command)
    {
        m_displayServerCmd = command;
    }

    void UserSession::setPath(const QString& path) {
        m_path = path;
    }

    QString UserSession::path() const {
        return m_path;
    }

    void UserSession::setupChildProcess() {
        // Session type
        QString sessionType = processEnvironment().value(QStringLiteral("XDG_SESSION_TYPE"));
        QString sessionClass = pr

        const bool hasDisplayServer = !m_displayServerCmd.isEmpty();
        const bool x11UserSession = sessionType == QLatin1String("x11") && sessionClass == QLatin1String("user");
        const bool waylandUserSession = sessionType == QLatin1String("wayland") && sessionClass == QLatin1String("user");

        // When the display server is part of the session, we leak the VT into
        // the session as stdin so that it stays open without races
        if (hasDisplayServer || waylandUserSession) {
            // open VT and get the fd
            int vtNumber = processEnvironment().value(QStringLiteral("XDG_VTNR")).toInt();
            QString ttyString = QStringLiteral("/dev/tty%1").arg(vtNumber);
            int vtFd = ::open(qPrintable(ttyString), O_RDWR | O_NOCTTY);

            // when this is true we'll take control of the tty
            bool takeControl = false;

            if (vtFd > 0) {
                dup2(vtFd, STDIN_FILENO);
                ::close(vtFd);
                takeControl = true;
            } else {
                int stdinFd = ::open("/dev/null", O_RDWR);
                dup2(stdinFd, STDIN_FILENO);
                ::close(stdinFd);
            }

            // set this process as session leader
            if (setsid() < 0) {
                qCritical("Failed to set pid %lld as leader of the new session and process group: %s",
                          QCoreApplication::applicationPid(), strerror(errno));
                exit(Auth::HELPER_OTHER_ERROR);
            }

            // take control of the tty
            if (takeControl) {
                if (ioctl(STDIN_FILENO, TIOCSCTTY) < 0) {
                    qCritical("Failed to take control of %s: %s", qPrintable(ttyString), strerror(errno));
                    exit(Auth::HELPER_OTHER_ERROR);
                }
            }

            VirtualTerminal::jumpToVt(vtNumber, true);
        }

#ifdef Q_OS_LINUX
        // enter Linux namespaces
        for (const QString &ns: mainConfig.Namespaces.get()) {
            qInfo() << "Entering namespace" << ns;
            int fd = ::open(qPrintable(ns), O_RDONLY);
            if (fd < 0) {
                qCritical("open(%s) failed: %s", qPrintable(ns), strerror(errno));
                exit(Auth::HELPER_OTHER_ERROR);
            }
            if (setns(fd, 0) != 0) {
                qCritical("setns(open(%s), 0) failed: %s", qPrintable(ns), strerror(errno));
                exit(Auth::HELPER_OTHER_ERROR);
            }
            ::close(fd);
        }
#endif

        const QByteArray username = qobject_cast<HelperApp*>(parent())->user().toLocal8Bit();
        struct passwd pw;
        struct passwd *rpw;
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
        if (bufsize == -1)
            bufsize = 16384;
        QScopedPointer<char, QScopedPointerPodDeleter> buffer(static_cast<char*>(malloc(bufsize)));
        if (buffer.isNull())
            exit(Auth::HELPER_OTHER_ERROR);
        int err = getpwnam_r(username.constData(), &pw, buffer.data(), bufsize, &rpw);
        if (rpw == NULL) {
            if (err == 0)
                qCritical() << "getpwnam_r(" << username << ") username not found!";
            else
                qCritical() << "getpwnam_r(" << username << ") failed with error: " << strerror(err);
            exit(Auth::HELPER_OTHER_ERROR);
        }
#if defined(Q_OS_FREEBSD)
        // execve() uses the environment prepared in Backend::openSession(),
        // therefore environment variables which are set here are ignored.
        if (setusercontext(NULL, &pw, pw.pw_uid, LOGIN_SETALL) != 0) {
            qCritical() << "setusercontext(NULL, *, " << pw.pw_uid << ", LOGIN_SETALL) failed for user: " << username;
            exit(Auth::HELPER_OTHER_ERROR);
        }
#else
        if (setgid(pw.pw_gid) != 0) {
            qCritical() << "setgid(" << pw.pw_gid << ") failed for user: " << username;
            exit(Auth::HELPER_OTHER_ERROR);
        }
        qputenv("XDG_RUNTIME_DIR", QByteArrayLiteral("/run/user/") + QByteArray::number(pw.pw_uid));

#ifdef USE_PAM

        // fetch ambient groups from PAM's environment;
        // these are set by modules such as pam_groups.so
        int n_pam_groups = getgroups(0, NULL);
        gid_t *pam_groups = NULL;
        if (n_pam_groups > 0) {
            pam_groups = new gid_t[n_pam_groups];
            if ((n_pam_groups = getgroups(n_pam_groups, pam_groups)) == -1) {
                qCritical() << "getgroups() failed to fetch supplemental"
                            << "PAM groups for user:" << username;
                exit(Auth::HELPER_OTHER_ERROR);
            }
        } else {
            n_pam_groups = 0;
        }

        // fetch session's user's groups
        int n_user_groups = 0;
        gid_t *user_groups = NULL;
        if (-1 == getgrouplist(pw.pw_name, pw.pw_gid,
                               NULL, &n_user_groups)) {
            user_groups = new gid_t[n_user_groups];
            if ((n_user_groups = getgrouplist(pw.pw_name,
                                              pw.pw_gid, user_groups,
                                              &n_user_groups)) == -1 ) {
                qCritical() << "getgrouplist(" << pw.pw_name << ", " << pw.pw_gid
                            << ") failed";
                exit(Auth::HELPER_OTHER_ERROR);
            }
        }

        // set groups to concatenation of PAM's ambient
        // groups and the session's user's groups
        int n_groups = n_pam_groups + n_user_groups;
        if (n_groups > 0) {
            gid_t *groups = new gid_t[n_groups];
            memcpy(groups, pam_groups, (n_pam_groups * sizeof(gid_t)));
            memcpy((groups + n_pam_groups), user_groups,
                   (n_user_groups * sizeof(gid_t)));

            // setgroups(2) handles duplicate groups
            if (setgroups(n_groups, groups) != 0) {
                qCritical() << "setgroups() failed for user: " << username;
                exit (Auth::HELPER_OTHER_ERROR);
            }
            delete[] groups;
        }
        delete[] pam_groups;
        delete[] user_groups;

#else

        if (initgroups(pw.pw_name, pw.pw_gid) != 0) {
            qCritical() << "initgroups(" << pw.pw_name << ", " << pw.pw_gid << ") failed for user: " << username;
            exit(Auth::HELPER_OTHER_ERROR);
        }

#endif /* USE_PAM */

        if (setuid(pw.pw_uid) != 0) {
            qCritical() << "setuid(" << pw.pw_uid << ") failed for user: " << username;
            exit(Auth::HELPER_OTHER_ERROR);
        }
#endif /* Q_OS_FREEBSD */
        if (chdir(pw.pw_dir) != 0) {
            qCritical() << "chdir(" << pw.pw_dir << ") failed for user: " << username;
            qCritical() << "verify directory exist and has sufficient permissions";
            exit(Auth::HELPER_OTHER_ERROR);
        }

        if (sessionClass != QLatin1String("greeter")) {
            //we cannot use setStandardError file as this code is run in the child process
            //we want to redirect after we setuid so that the log file is owned by the user

            // determine stderr log file based on session type
            QString sessionLog = QStringLiteral("%1/%2")
                    .arg(QString::fromLocal8Bit(pw.pw_dir))
                    .arg(sessionType == QLatin1String("x11")
                         ? mainConfig.X11.SessionLogFile.get()
                         : mainConfig.Wayland.SessionLogFile.get());

            // create the path
            QFileInfo finfo(sessionLog);
            QDir().mkpath(finfo.absolutePath());

            setStandardErrorFile(sessionLog);
            setStandardOutputFile(QProcess::nullDevice());
        } else {
            setProcessChannelMode(QProcess::ForwardedChannels);
        }

        // set X authority for X11 sessions only
        if (x11UserSession) {
            QString cookie = qobject_cast<HelperApp*>(parent())->cookie();
            if (!cookie.isEmpty()) {
                QString file = processEnvironment().value(QStringLiteral("XAUTHORITY"));
                QString display = processEnvironment().value(QStringLiteral("DISPLAY"));

                // Create the path
                QFileInfo finfo(file);
                QDir().mkpath(finfo.absolutePath());

                XAuth::addCookieToFile(display, file, cookie);
            }
        }
    }

    qint64 UserSession::cachedProcessId() {
        return m_cachedProcessId;
    }

}


InsertStatement::InsertStatement() : SQLStatement(StatementType::INSERT_STATEMENT), schema(DEFAULT_SCHEMA)

#include "duckdb/parser/statement/insert_statement.hpp"

namespace duckdb {

{
}

unique_ptr<SQLStatement> InsertStatement::Copy() const {
	auto result = make_unique<InsertStatement>();
	result->select_statement = unique_ptr_cast<SQLStatement, SelectStatement>(select_statement->Copy());
	result->columns = columns;
	result->table = table;
	result->schema = schema;
	return move(result);
}

} // namespace duckdb


#include "duckdb/parser/statement/insert_statement.hpp"

namespace duckdb {

InsertStatement::InsertStatement() : SQLStatement(StatementType::INSERT_STATEMENT), schema(DEFAULT_SCHEMA)

unique_ptr<SQLStatement> InsertStatement::Copy() const {
	auto result = make_unique<InsertStatement>();
	result->select_statement = unique_ptr_cast<SQLStatement, SelectStatement>(select_statement->Copy());
	result->columns = columns;
	result->table = table;
	result->schema = schema;
	return move(result);
}

} // namespace duckdb


#include "duckdb/parser/statement/insert_statement.hpp"

namespace duckdb {

InsertStatement::InsertStatement() : SQLStatement(StatementType::INSERT_STATEMENT), schema(DEFAULT_SCHEMA) {
}

unique_ptr<SQLStatement> InsertStatement::Copy() const {
	auto result = make_unique<InsertStatement>();
	result->select_statement = unique_ptr_cast<SQLStatement, SelectStatement>(select_statement->Copy());
	result->columns = co

	result->table = table;
	result->schema = schema;
	return move(result);
}

} // namespace duckdb


/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

// vim: expandtab

#include "qwt_plot_dict.h"

class QwtPlotDict::PrivateData
{
public:

#if QT_VERSION < 0x040000
    class ItemList: public QValueList<QwtPlotItem *>
#else
    class ItemList: public QList<QwtPlotItem *>
#endif
    {
    public:
        void insertItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            // Unfortunately there is no inSort operation
            // for lists in Qt4. The implementation below
            // is slow, but there shouldn't be many plot items.

#ifdef __GNUC__
#endif

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( *it == item )
                    return;

                if ( (*it)->z() > item->z() )
                {
                    insert(it, item);
                    return;
                }
            }
            append(item);
        }

        void removeItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            int i = 0;

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( item == *it )
                {
#if QT_VERSION < 0x040000
                    remove(it);
#else
                    removeAt(i);
#endif
                    return;
                }
                i++;
            }
        }
    };

    ItemList itemList;
    bool autoDelete;
};

/*!
   Constructor

   Auto deletion is enabled.
   \sa setAutoDelete(), attachItem()
*/
QwtPlotDict::QwtPlotDict()
{
    d_data = new QwtPlotDict::PrivateData;
    d_data->autoDelete = true;
}

/*!
   Destructor

   If autoDelete is on, all attached items will be deleted
   \sa setAutoDelete(), autoDelete(), attachItem()
*/
QwtPlotDict::~QwtPlotDict()
{
    detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete);
    delete d_data;
}

/*!
   En/Disable Auto deletion

   If Auto deletion is on all attached plot items will be deleted
   in the destructor of QwtPlotDict. The default value is on.

   \sa autoDelete(), attachItem()
*/

{
    d_data->autoDelete = autoDelete;
}

/*!
   \return true if auto deletion is enabled
   \sa setAutoDelete(), attachItem()
*/
bool QwtPlotDict::autoDelete() const
{
    return d_data->autoDelete;
}

/*!
   Attach/Detach a plot item

   Attached items will be deleted in the destructor,
   if auto deletion is enabled (default). Manually detached
   items are not deleted.

   \param item Plot item to attach/detach
   \ on If true attach, else detach the item

   \sa setAutoDelete(), ~QwtPlotDict()
*/
void QwtPlotDict::attachItem(QwtPlotItem *item, bool on)
{
    if ( on )
        d_data->itemList.insertItem(item);
    else
        d_data->itemList.removeItem(item);
}

/*!
   Detach items from the dictionary

   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
               otherwise only those items of the type rtti.
   \param autoDelete If true, delete all detached items
*/
void QwtPlotDict::detachItems(int rtti, bool autoDelete)
{
    PrivateData::ItemList list = d_data->itemList;
    QwtPlotItemIterator it = list.begin();
    while ( it != list.end() )
    {
        QwtPlotItem *item = *it;

        ++it; // increment before removing item from the list

        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
        {
            item->attach(NULL);
            if ( autoDelete )
                delete item;
        }
    }
}

//! \brief A QwtPlotItemList of all attached plot items.
/// Use caution when iterating these lists, as removing/detaching an item will
/// invalidate the iterator. Instead you can place pointers to objects to be
/// removed in a removal list, and traverse that list later.
//! \return List of all attached plot items.
const QwtPlotItemList &QwtPlotDict::itemList() const
{
    return d_data->itemList;
}


/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

// vim: expandtab

#include "qwt_plot_dict.h"

class QwtPlotDict::PrivateData
{
public:

#if QT_VERSION < 0x040000
    class ItemList: public QValueList<QwtPlotItem *>
#else
    class ItemList: public QList<QwtPlotItem *>
#endif
    {
    public:
        void insertItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            // Unfortunately there is no inSort operation
            // for lists in Qt4. The implementation below
            // is slow, but there shouldn't be many plot items.

#ifdef __GNUC__
#endif

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( *it == item )
                    return;

                if ( (*it)->z() > item->z() )
                {
                    insert(it, item);
                    return;
                }
            }
            append(item);
        }

        void removeItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            int i = 0;

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( item == *it )
                {
#if QT_VERSION < 0x040000
                    remove(it);
#else
                    removeAt(i);
#endif
                    return;
                }
                i++;
            }
        }
    };

    ItemList itemList;
    bool autoDelete;
};

/*!
   Constructor

   Auto deletion is enabled.
   \sa setAutoDelete(), attachItem()
*/
QwtPlotDict::QwtPlotDict()
{
    d_data = new QwtPlotDict::PrivateData;
    d_data->autoDelete = true;
}

/*!
   Destructor

   If autoDelete is on, all attached items will be deleted
   \sa setAutoDelete(), autoDelete(), attachItem()
*/
QwtPlotDict::~QwtPlotDict()
{
    detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete);
    delete d_data;
}

/*!
   En/Disable Auto deletion

   If Auto deletion is on all attached plot items will be deleted
   in the destructor of QwtPlotDict. The default value is on.

   \sa autoDelete(), attachItem()
*/
void QwtPlotDict::setAutoDelete(bool autoDelete)
{
    d_data->autoDelete = autoDelete;
}

/*!
   \return true if auto deletion is enabled
   \sa setAutoDelete(), attachItem()
*/
bool QwtPlotDict::autoDelete() const
{
    return d_data->autoDelete;
}

/*!
   Attach/Detach a plot item

   Attached items will be deleted in the destructor,
   if auto deletion is enabled (default). Manually detached
   items are not deleted.

   \param item Plot item to attach/detach
   \ on If true attach, else detach the item

   \sa setAutoDelete(), ~QwtPlotDict()
*/
void QwtPlotDict::attachItem(QwtPlotItem *item, bool on)
{
    if ( on )
        d_data->itemList.insertItem(item);
    else
        d_data->itemList.removeItem(item);
}

/*!
   Detach items from the dictionary

   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
               otherwise only those items of the type rtti.
   \param autoDelete If true, delete all detached items
*/
void QwtPlotDict::detachItems(int rtti, bool autoDelete)
{
    PrivateData::ItemList list = d_data->itemList;
    QwtPlotItemIterator it = list.begin();
    while ( it != list.end() )
    {
        QwtPlotItem *item = *it;

        ++it; // increment before removing item from the list

        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
        {
            item->attach(NULL);
            if ( autoDelete )
                delete item;
        }
    }
}

//! \brief A QwtPlotItemList of all attached plot items.
/// Use caution when iterating these lists, as removing/detaching an item will
/// invalidate the iterator. Instead you can place pointers to objects to be
/// removed in a removal list, and traverse that list later.
//! \return List of all attached plot items.
const QwtPlotItemList &QwtPlotDict::itemList() const


/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

// vim: expandtab

#include "qwt_plot_dict.h"

class QwtPlotDict::PrivateData
{
public:

#if QT_VERSION < 0x040000
    class ItemList: public QValueList<QwtPlotItem *>
#else
    class ItemList: public QList<QwtPlotItem *>
#endif
    {
    public:
        void insertItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            // Unfortunately there is no inSort operation
            // for lists in Qt4. The implementation below
            // is slow, but there shouldn't be many plot items.

#ifdef __GNUC__
#endif

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( *it == item )
                    return;

                if ( (*it)->z() > item->z() )
                {
                    insert(it, item);
                    return;
                }
            }
            append(item);
        }

        void removeItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            int i = 0;

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( item == *it )
                {
#if QT_VERSION < 0x040000
                    remove(it);
#else
                    removeAt(i);
#endif
                    return;
                }
                i++;
            }
        }
    };

    ItemList itemList;
    bool autoDelete;
};

/*!
   Constructor

   Auto deletion is enabled.
   \sa setAutoDelete(), attachItem()
*/
QwtPlotDict::QwtPlotDict()
{
    d_data = new QwtPlotDict::PrivateData;
    d_data->autoDelete = true;
}

/*!
   Destructor

   If autoDelete is on, all attached items will be deleted
   \sa setAutoDelete(), autoDelete(), attachItem()
*/
QwtPlotDict::~QwtPlotDict()
{
    detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete);
    delete d_data;
}

/*!
   En/Disable Auto deletion

   If Auto deletion is on all attached plot items will be deleted
   in the destructor of QwtPlotDict. The default value is on.

   \sa autoDelete(), attachItem()
*/
void QwtPlotDict::setAutoDelete(bool autoDelete)
{
    d_data->autoDelete = autoDelete;
}

/*!
   \return true if auto deletion is enabled
   \sa setAutoDelete(), attachItem()
*/
bool QwtPlotDict::autoDelete() const
{
    return d_data->autoDelete;
}

/*!
   Attach/Detach a plot item

   Attached items will be deleted in the destructor,
   if auto deletion is enabled (default). Manually detached
   items are not deleted.

   \param item Plot item to attach/detach
   \ on If true attach, else detach the item

   \sa setAutoDelete(), ~QwtPlotDict()
*/
void QwtPlotDict::attachItem(QwtPlotItem *item, bool on)
{
    if ( on )
        d_data->itemList.insertItem(item);
    else
        d_data->itemList.removeItem(item);
}

/*!
   Detach items from the dictionary

   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
               otherwise only those items of the type rtti.
   \param autoDelete If true, delete all detached items
*/
void QwtPlotDict::detachItems(int rtti, bool autoDelete)
{
    PrivateData::ItemList list = d_data->itemList;
    QwtPlotItemIterator it = list.begin();
    while ( it != list.end() )
    {
        QwtPlotItem *item = *it;

        ++it; // increment before removing item from the list

        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
        {
            item->attach(NULL);
            if ( autoDelete )
                delete item;
        }
    }
}

//! \brief A QwtPlotItemList of all attached plot items.
/// Use caution when iterating these lists, as removing/detaching an item will
/// invalidate the iterator. Instead you can place pointers to objects to be
/// removed in a removal list, and traverse that list later.
//! \return List of all attached plot items.
const QwtPlotItemList &QwtPlotDict::itemList() const
{
    return d_dat


QwtPlotDict::~QwtPlotDict()
{
    detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete);
    delete d_data;
}

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

// vim: expandtab

#include "qwt_plot_dict.h"

class QwtPlotDict::PrivateData
{
public:

#if QT_VERSION < 0x040000
    class ItemList: public QValueList<QwtPlotItem *>
#else
    class ItemList: public QList<QwtPlotItem *>
#endif
    {
    public:
        void insertItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            // Unfortunately there is no inSort operation
            // for lists in Qt4. The implementation below
            // is slow, but there shouldn't be many plot items.

#ifdef __GNUC__
#endif

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( *it == item )
                    return;

                if ( (*it)->z() > item->z() )
                {
                    insert(it, item);
                    return;
                }
            }
            append(item);
        }

        void removeItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            int i = 0;

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( item == *it )
                {
#if QT_VERSION < 0x040000
                    remove(it);
#else
                    removeAt(i);
#endif
                    return;
                }
                i++;
            }
        }
    };

    ItemList itemList;
    bool autoDelete;
};

/*!
   Constructor

   Auto deletion is enabled.
   \sa setAutoDelete(), attachItem()
*/
QwtPlotDict::QwtPlotDict()
{
    d_data = new QwtPlotDict::PrivateData;
    d_data->autoDelete = true;
}

/*!
   Destructor

   If autoDelete is on, all attached items will be deleted
   \sa setAutoDelete(), autoDelete(), attachItem()
*/

/*!
   En/Disable Auto deletion

   If Auto deletion is on all attached plot items will be deleted
   in the destructor of QwtPlotDict. The default value is on.

   \sa autoDelete(), attachItem()
*/
void QwtPlotDict::setAutoDelete(bool autoDelete)
{
    d_data->autoDelete = autoDelete;
}

/*!
   \return true if auto deletion is enabled
   \sa setAutoDelete(), attachItem()
*/
bool QwtPlotDict::autoDelete() const
{
    return d_data->autoDelete;
}

/*!
   Attach/Detach a plot item

   Attached items will be deleted in the destructor,
   if auto deletion is enabled (default). Manually detached
   items are not deleted.

   \param item Plot item to attach/detach
   \ on If true attach, else detach the item

   \sa setAutoDelete(), ~QwtPlotDict()
*/
void QwtPlotDict::attachItem(QwtPlotItem *item, bool on)
{
    if ( on )
        d_data->itemList.insertItem(item);
    else
        d_data->itemList.removeItem(item);
}

/*!
   Detach items from the dictionary

   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
               otherwise only those items of the type rtti.
   \param autoDelete If true, delete all detached items
*/
void QwtPlotDict::detachItems(int rtti, bool autoDelete)
{
    PrivateData::ItemList list = d_data->itemList;
    QwtPlotItemIterator it = list.begin();
    while ( it != list.end() )
    {
        QwtPlotItem *item = *it;

        ++it; // increment before removing item from the list

        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
        {
            item->attach(NULL);
            if ( autoDelete )
                delete item;
        }
    }
}

//! \brief A QwtPlotItemList of all attached plot items.
/// Use caution when iterating these lists, as removing/detaching an item will
/// invalidate the iterator. Instead you can place pointers to objects to be
/// removed in a removal list, and traverse that list later.
//! \return List of all attached plot items.
const QwtPlotItemList &QwtPlotDict::itemList() const
{
    return d_data->itemList;
}


while ( it != list.end() )
    {
        QwtPlotItem *item = *it;

        ++it; // increment before removing item from the list

        if ( rtti == QwtPlotItem::Rtti_PlotItem || item->rtti() == rtti )
        {
            item->attach(NULL);
            if ( autoDelete )
                delete item;
        }
    }

/* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
 * Qwt Widget Library
 *
 *****************************************************************************/

// vim: expandtab

#include "qwt_plot_dict.h"

class QwtPlotDict::PrivateData
{
public:

#if QT_VERSION < 0x040000
    class ItemList: public QValueList<QwtPlotItem *>
#else
    class ItemList: public QList<QwtPlotItem *>
#endif
    {
    public:
        void insertItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            // Unfortunately there is no inSort operation
            // for lists in Qt4. The implementation below
            // is slow, but there shouldn't be many plot items.

#ifdef __GNUC__
#endif

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( *it == item )
                    return;

                if ( (*it)->z() > item->z() )
                {
                    insert(it, item);
                    return;
                }
            }
            append(item);
        }

        void removeItem(QwtPlotItem *item)
        {
            if ( item == NULL )
                return;

            int i = 0;

#if QT_VERSION < 0x040000
            QValueListIterator<QwtPlotItem *> it;
#else
            QList<QwtPlotItem *>::Iterator it;
#endif
            for ( it = begin(); it != end(); ++it )
            {
                if ( item == *it )
                {
#if QT_VERSION < 0x040000
                    remove(it);
#else
                    removeAt(i);
#endif
                    return;
                }
                i++;
            }
        }
    };

    ItemList itemList;
    bool autoDelete;
};

/*!
   Constructor

   Auto deletion is enabled.
   \sa setAutoDelete(), attachItem()
*/
QwtPlotDict::QwtPlotDict()
{
    d_data = new QwtPlotDict::PrivateData;
    d_data->autoDelete = true;
}

/*!
   Destructor

   If autoDelete is on, all attached items will be deleted
   \sa setAutoDelete(), autoDelete(), attachItem()
*/
QwtPlotDict::~QwtPlotDict()
{
    detachItems(QwtPlotItem::Rtti_PlotItem, d_data->autoDelete);
    delete d_data;
}

/*!
   En/Disable Auto deletion

   If Auto deletion is on all attached plot items will be deleted
   in the destructor of QwtPlotDict. The default value is on.

   \sa autoDelete(), attachItem()
*/
void QwtPlotDict::setAutoDelete(bool autoDelete)
{
    d_data->autoDelete = autoDelete;
}

/*!
   \return true if auto deletion is enabled
   \sa setAutoDelete(), attachItem()
*/
bool QwtPlotDict::autoDelete() const
{
    return d_data->autoDelete;
}

/*!
   Attach/Detach a plot item

   Attached items will be deleted in the destructor,
   if auto deletion is enabled (default). Manually detached
   items are not deleted.

   \param item Plot item to attach/detach
   \ on If true attach, else detach the item

   \sa setAutoDelete(), ~QwtPlotDict()
*/
void QwtPlotDict::attachItem(QwtPlotItem *item, bool on)
{
    if ( on )
        d_data->itemList.insertItem(item);
    else
        d_data->itemList.removeItem(item);
}

/*!
   Detach items from the dictionary

   \param rtti In case of QwtPlotItem::Rtti_PlotItem detach all items
               otherwise only those items of the type rtti.
   \param autoDelete If true, delete all detached items
*/
void QwtPlotDict::detachItems(int rtti, bool autoDelete)
{
    PrivateData::ItemList list = d_data->itemList;
    QwtPlotItemIterator it = list.begin();

}

//! \brief A QwtPlotItemList of all attached plot items.
/// Use caution when iterating these lists, as removing/detaching an item will
/// invalidate the iterator. Instead you can place pointers to objects to be
/// removed in a removal list, and traverse that list later.
//! \return List of all attached plot items.
const QwtPlotItemList &QwtPlotDict::itemList() const
{
    return d_data->itemList;
}


#include "Runtime/World/CScriptSpiderBallWaypoint.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/World/CActorParameters.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace metaforce {
CScriptSpiderBallWaypoint::CScriptSpiderBallWaypoint(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                                     const zeus::CTransform& xf, bool active, u32 w1)
: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(EMaterialTypes::NoStepLogic),
         CActorParameters::None(), kInvalidUniqueId)
, xe8_(w1) {}

void CScriptSpiderBallWaypoint::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptSpiderBallWaypoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
  CActor::AcceptScriptMsg(msg, uid, mgr);

  if (msg == EScriptObjectMessage::InitializedInArea) {
    BuildWaypointListAndBounds(mgr);
  } else if (msg == EScriptObjectMessage::Arrived) {
    SendScriptMsgs(EScriptObjectState::Arrived, mgr, EScriptObjectMessage::None);
  }
}

void CScriptSpiderBallWaypoint::AccumulateBounds(const zeus::CVector3f& v) {
  if (!xfc_aabox) {
    xfc_aabox.emplace(v, v);
  }
  xfc_aabox->accumulateBounds(v);
}

{
  u32 validConnections = 0;
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state == EScriptObjectState::Arrived && conn.x4_msg == EScriptObjectMessage::Next) {
      const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
      if (uid != kInvalidUniqueId) {
        static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid))->AddPreviousWaypoint(GetUniqueId());
        ++validConnections;
      }
    }
  }

  if (validConnections == 0) {
    AccumulateBounds(x34_transform.origin);
  } else {
    CScriptSpiderBallWaypoint* curWaypoint = this;
    TUniqueId uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    while (uid != kInvalidUniqueId) {
      curWaypoint = static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid));
      uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    }

    curWaypoint->AccumulateBounds(x34_transform.origin);
  }
}

void CScriptSpiderBallWaypoint::AddPreviousWaypoint(TUniqueId uid) { xec_waypoints.push_back(uid); }

TUniqueId CScriptSpiderBallWaypoint::PreviousWaypoint(const CStateManager& mgr,
                                                      ECheckActiveWaypoint checkActive) const {
  for (const auto& id : xec_waypoints) {
    if (const CEntity* ent = mgr.GetObjectById(id)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return id;
      }
      if (ent->GetActive()) {
        return id;
      }
    }
  }

  return kInvalidUniqueId;
}

TUniqueId CScriptSpiderBallWaypoint::NextWaypoint(const CStateManager& mgr, ECheckActiveWaypoint checkActive) const {
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state != EScriptObjectState::Arrived || conn.x4_msg != EScriptObjectMessage::Next) {
      continue;
    }
    const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
    if (const CEntity* ent = mgr.GetObjectById(uid)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return ent->GetUniqueId();
      }
      if (ent->GetActive()) {
        return ent->GetUniqueId();
      }
    }
  }

  return kInvalidUniqueId;
}

void CScriptSpiderBallWaypoint::GetClosestPointAlongWaypoints(CStateManager& mgr, const zeus::CVector3f& ballPos,
                                                              float maxPointToBallDist,
                                                              const CScriptSpiderBallWaypoint*& closestWaypoint,
                                                              zeus::CVector3f& closestPoint,
                                                              zeus::CVector3f& deltaBetweenPoints,
                                                              float deltaBetweenInterpDist,
                                                              zeus::CVector3f& interpDeltaBetweenPoints) const {
  const auto* wp = this;
  while (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
  }

  float minPointToBallDistSq = maxPointToBallDist * maxPointToBallDist;
  const float deltaBetweenInterpDistSq = deltaBetweenInterpDist * deltaBetweenInterpDist;
  zeus::CVector3f lastPoint = wp->GetTranslation();
  zeus::CVector3f lastDelta;
  bool computeDelta = wp->GetActive();

  while (true) {
    if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
      if (computeDelta) {
        const auto* prevWp = wp;
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));

        const zeus::CVector3f thisDelta = wp->GetTranslation() - lastPoint;
        const zeus::CVector3f lastPointToBall = ballPos - lastPoint;
        if (prevWp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) == kInvalidUniqueId) {
          lastDelta = thisDelta;
        }

        const float pointToBallDistSq = lastPointToBall.magSquared();
        if (pointToBallDistSq < minPointToBallDistSq) {
          minPointToBallDistSq = pointToBallDistSq;
          closestPoint = lastPoint;
          deltaBetweenPoints = thisDelta;
          interpDeltaBetweenPoints = (thisDelta.normalized() + lastDelta.normalized()) * 0.5f;
          closestWaypoint = wp;
        }

        const float projectedT = lastPointToBall.dot(thisDelta);
        if (projectedT >= 0.f) {
          const float normT = projectedT / thisDelta.magSquared();
          if (normT < 1.f) {
            const zeus::CVector3f projectedPoint = zeus::CVector3f::lerp(lastPoint, wp->GetTranslation(), normT);
            const float projToBallDistSq = (ballPos - projectedPoint).magSquared();
            if (projToBallDistSq < minPointToBallDistSq) {
              minPointToBallDistSq = projToBallDistSq;
              closestPoint = projectedPoint;
              deltaBetweenPoints = thisDelta;
              interpDeltaBetweenPoints = deltaBetweenPoints;
              closestWaypoint = wp;
              float lastToProjDist = (lastPoint - projectedPoint).magnitude();
              if (lastToProjDist < deltaBetweenInterpDistSq) {
                interpDeltaBetweenPoints =
                    zeus::CVector3f::lerp(0.5f * (thisDelta.normalized() + lastDelta.normalized()),
                                          thisDelta.normalized(), lastToProjDist / deltaBetweenInterpDist);
              } else if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
                lastToProjDist = (projectedPoint - wp->GetTranslation()).magnitude();
                if (lastToProjDist < deltaBetweenInterpDist) {
                  const float t = lastToProjDist / deltaBetweenInterpDist;
                  interpDeltaBetweenPoints =
                      zeus::CVector3f::lerp(((static_cast<const CScriptSpiderBallWaypoint*>(
                                                  mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)))
                                                  ->GetTranslation() -
                                              wp->GetTranslation())
                                                 .normalized() +
                                             thisDelta.normalized()) *
                                                0.5f,
                                            thisDelta.normalized(), t);
                }
              }
            }
          }
        }
        lastDelta = thisDelta;
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      } else {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      }
    } else {
      if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
        computeDelta = false;
      } else {
        break;
      }
    }
  }

  if ((ballPos - lastPoint).magSquared() >= minPointToBallDistSq) {
    return;
  }

  closestPoint = lastPoint;
  if (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
    deltaBetweenPoints = lastPoint - wp->GetTranslation();
    interpDeltaBetweenPoints = deltaBetweenPoints;
  }
  closestWaypoint = wp;
}
} // namespace metaforce


{
  CActor::AcceptScriptMsg(msg, uid, mgr);

  if (msg == EScriptObjectMessage::InitializedInArea) {
    BuildWaypointListAndBounds(mgr);
  } else if (msg == EScriptObjectMessage::Arrived) {
    SendScriptMsgs(EScriptObjectState::Arrived, mgr, EScriptObjectMessage::None);
  }
}

#include "Runtime/World/CScriptSpiderBallWaypoint.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/World/CActorParameters.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace metaforce {
CScriptSpiderBallWaypoint::CScriptSpiderBallWaypoint(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                                     const zeus::CTransform& xf, bool active, u32 w1)
: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(EMaterialTypes::NoStepLogic),
         CActorParameters::None(), kInvalidUniqueId)
, xe8_(w1) {}

void CScriptSpiderBallWaypoint::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptSpiderBallWaypoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr)

void CScriptSpiderBallWaypoint::AccumulateBounds(const zeus::CVector3f& v) {
  if (!xfc_aabox) {
    xfc_aabox.emplace(v, v);
  }
  xfc_aabox->accumulateBounds(v);
}

void CScriptSpiderBallWaypoint::BuildWaypointListAndBounds(CStateManager& mgr) {
  u32 validConnections = 0;
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state == EScriptObjectState::Arrived && conn.x4_msg == EScriptObjectMessage::Next) {
      const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
      if (uid != kInvalidUniqueId) {
        static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid))->AddPreviousWaypoint(GetUniqueId());
        ++validConnections;
      }
    }
  }

  if (validConnections == 0) {
    AccumulateBounds(x34_transform.origin);
  } else {
    CScriptSpiderBallWaypoint* curWaypoint = this;
    TUniqueId uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    while (uid != kInvalidUniqueId) {
      curWaypoint = static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid));
      uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    }

    curWaypoint->AccumulateBounds(x34_transform.origin);
  }
}

void CScriptSpiderBallWaypoint::AddPreviousWaypoint(TUniqueId uid) { xec_waypoints.push_back(uid); }

TUniqueId CScriptSpiderBallWaypoint::PreviousWaypoint(const CStateManager& mgr,
                                                      ECheckActiveWaypoint checkActive) const {
  for (const auto& id : xec_waypoints) {
    if (const CEntity* ent = mgr.GetObjectById(id)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return id;
      }
      if (ent->GetActive()) {
        return id;
      }
    }
  }

  return kInvalidUniqueId;
}

TUniqueId CScriptSpiderBallWaypoint::NextWaypoint(const CStateManager& mgr, ECheckActiveWaypoint checkActive) const {
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state != EScriptObjectState::Arrived || conn.x4_msg != EScriptObjectMessage::Next) {
      continue;
    }
    const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
    if (const CEntity* ent = mgr.GetObjectById(uid)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return ent->GetUniqueId();
      }
      if (ent->GetActive()) {
        return ent->GetUniqueId();
      }
    }
  }

  return kInvalidUniqueId;
}

void CScriptSpiderBallWaypoint::GetClosestPointAlongWaypoints(CStateManager& mgr, const zeus::CVector3f& ballPos,
                                                              float maxPointToBallDist,
                                                              const CScriptSpiderBallWaypoint*& closestWaypoint,
                                                              zeus::CVector3f& closestPoint,
                                                              zeus::CVector3f& deltaBetweenPoints,
                                                              float deltaBetweenInterpDist,
                                                              zeus::CVector3f& interpDeltaBetweenPoints) const {
  const auto* wp = this;
  while (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
  }

  float minPointToBallDistSq = maxPointToBallDist * maxPointToBallDist;
  const float deltaBetweenInterpDistSq = deltaBetweenInterpDist * deltaBetweenInterpDist;
  zeus::CVector3f lastPoint = wp->GetTranslation();
  zeus::CVector3f lastDelta;
  bool computeDelta = wp->GetActive();

  while (true) {
    if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
      if (computeDelta) {
        const auto* prevWp = wp;
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));

        const zeus::CVector3f thisDelta = wp->GetTranslation() - lastPoint;
        const zeus::CVector3f lastPointToBall = ballPos - lastPoint;
        if (prevWp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) == kInvalidUniqueId) {
          lastDelta = thisDelta;
        }

        const float pointToBallDistSq = lastPointToBall.magSquared();
        if (pointToBallDistSq < minPointToBallDistSq) {
          minPointToBallDistSq = pointToBallDistSq;
          closestPoint = lastPoint;
          deltaBetweenPoints = thisDelta;
          interpDeltaBetweenPoints = (thisDelta.normalized() + lastDelta.normalized()) * 0.5f;
          closestWaypoint = wp;
        }

        const float projectedT = lastPointToBall.dot(thisDelta);
        if (projectedT >= 0.f) {
          const float normT = projectedT / thisDelta.magSquared();
          if (normT < 1.f) {
            const zeus::CVector3f projectedPoint = zeus::CVector3f::lerp(lastPoint, wp->GetTranslation(), normT);
            const float projToBallDistSq = (ballPos - projectedPoint).magSquared();
            if (projToBallDistSq < minPointToBallDistSq) {
              minPointToBallDistSq = projToBallDistSq;
              closestPoint = projectedPoint;
              deltaBetweenPoints = thisDelta;
              interpDeltaBetweenPoints = deltaBetweenPoints;
              closestWaypoint = wp;
              float lastToProjDist = (lastPoint - projectedPoint).magnitude();
              if (lastToProjDist < deltaBetweenInterpDistSq) {
                interpDeltaBetweenPoints =
                    zeus::CVector3f::lerp(0.5f * (thisDelta.normalized() + lastDelta.normalized()),
                                          thisDelta.normalized(), lastToProjDist / deltaBetweenInterpDist);
              } else if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
                lastToProjDist = (projectedPoint - wp->GetTranslation()).magnitude();
                if (lastToProjDist < deltaBetweenInterpDist) {
                  const float t = lastToProjDist / deltaBetweenInterpDist;
                  interpDeltaBetweenPoints =
                      zeus::CVector3f::lerp(((static_cast<const CScriptSpiderBallWaypoint*>(
                                                  mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)))
                                                  ->GetTranslation() -
                                              wp->GetTranslation())
                                                 .normalized() +
                                             thisDelta.normalized()) *
                                                0.5f,
                                            thisDelta.normalized(), t);
                }
              }
            }
          }
        }
        lastDelta = thisDelta;
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      } else {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      }
    } else {
      if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
        computeDelta = false;
      } else {
        break;
      }
    }
  }

  if ((ballPos - lastPoint).magSquared() >= minPointToBallDistSq) {
    return;
  }

  closestPoint = lastPoint;
  if (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
    deltaBetweenPoints = lastPoint - wp->GetTranslation();
    interpDeltaBetweenPoints = deltaBetweenPoints;
  }
  closestWaypoint = wp;
}
} // namespace metaforce


#include "Runtime/World/CScriptSpiderBallWaypoint.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/World/CActorParameters.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace metaforce {
CScriptSpiderBallWaypoint::CScriptSpiderBallWaypoint(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                                     const zeus::CTransform& xf, bool active, u32 w1)
: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(EMaterialTypes::NoStepLogic),
         CActorParameters::None(), kInvalidUniqueId)
, xe8_(w1) {}

void CScriptSpiderBallWaypoint::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptSpiderBallWaypoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
  CActor::AcceptScriptMsg(msg, uid, mgr);

  if (msg == EScriptObjectMessage::InitializedInArea) {
    BuildWaypointListAndBounds(mgr);
  } else if (msg == EScriptObjectMessage::Arrived) {
    SendScriptMsgs(EScriptObjectState::Arrived, mgr, EScriptObjectMessage::None);
  }
}

void CScriptSpiderBallWaypoint::AccumulateBounds(const zeus::CVector3f& v) {
  if (!xfc_aabox) {
    xfc_aabox.emplace(v, v);
  }
  xfc_aabox->accumulateBounds(v);
}

void CScriptSpiderBallWaypoint::BuildWaypointListAndBounds(CStateManager& mgr) {
  u32 validConnections = 0;
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state == EScriptObjectState::Arrived && conn.x4_msg == EScriptObjectMessage::Next) {
      const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
      if (uid != kInvalidUniqueId) {
        static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid))->AddPreviousWaypoint(GetUniqueId());
        ++validConnections;
      }
    }
  }

  if (validConnections == 0) {
    AccumulateBounds(x34_transform.origin);
  } else {
    CScriptSpiderBallWaypoint* curWaypoint = this;
    TUniqueId uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    while (uid != kInvalidUniqueId) {
      curWaypoint = static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid));
      uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    }

    curWaypoint->AccumulateBounds(x34_transform.origin);
  }
}

void CScriptSpiderBallWaypoint::AddPreviousWaypoint(TUniqueId uid) { xec_waypoints.push_back(uid); }

TUniqueId CScriptSpiderBallWaypoint::PreviousWaypoint(const CStateManager& mgr,
                                                      ECheckActiveWaypoint checkActive) const {
  for (const auto& id : xec_waypoints) {
    if (const CEntity* ent = mgr.GetObjectById(id)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return id;
      }
      if (ent->GetActive()) {
        return id;
      }
    }
  }

  return kInvalidUniqueId;
}

TUniqueId CScriptSpiderBallWaypoint::NextWaypoint(const CStateManager& mgr, ECheckActiveWaypoint checkActive) const {
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state != EScriptObjectState::Arrived || conn.x4_msg != EScriptObjectMessage::Next) {
      continue;
    }
    const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
    if (const CEntity* ent = mgr.GetObjectById(uid)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return ent->GetUniqueId();
      }
      if (ent->GetActive()) {
        return ent->GetUniqueId();
      }
    }
  }

  return kInvalidUniqueId;
}

void CScriptSpiderBallWaypoint::GetClosestPointAlongWaypoints(CStateManager& mgr, const zeus::CVector3f& ballPos,
                                                              float maxPointToBallDist,
                                                              const CScriptSpiderBallWaypoint*& closestWaypoint,
                                                              zeus::CVector3f& closestPoint,
                                                              zeus::CVector3f& deltaBetweenPoints,
                                                              float deltaBetweenInterpDist,
                                                              zeus::CVector3f& interpDeltaBetweenPoints) const {
  const auto* wp = this;
  while (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
  }

  float minPointToBallDistSq = maxPointToBallDist * maxPointToBallDist;
  const float deltaBetweenInterpDistSq = deltaBetweenInterpDist * deltaBetweenInterpDist;
  zeus::CVector3f lastPoint = wp->GetTranslation();
  zeus::CVector3f lastDelta;
  bool computeDelta

  while (true) {
    if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
      if (computeDelta) {
        const auto* prevWp = wp;
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));

        const zeus::CVector3f thisDelta = wp->GetTranslation() - lastPoint;
        const zeus::CVector3f lastPointToBall = ballPos - lastPoint;
        if (prevWp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) == kInvalidUniqueId) {
          lastDelta = thisDelta;
        }

        const float pointToBallDistSq = lastPointToBall.magSquared();
        if (pointToBallDistSq < minPointToBallDistSq) {
          minPointToBallDistSq = pointToBallDistSq;
          closestPoint = lastPoint;
          deltaBetweenPoints = thisDelta;
          interpDeltaBetweenPoints = (thisDelta.normalized() + lastDelta.normalized()) * 0.5f;
          closestWaypoint = wp;
        }

        const float projectedT = lastPointToBall.dot(thisDelta);
        if (projectedT >= 0.f) {
          const float normT = projectedT / thisDelta.magSquared();
          if (normT < 1.f) {
            const zeus::CVector3f projectedPoint = zeus::CVector3f::lerp(lastPoint, wp->GetTranslation(), normT);
            const float projToBallDistSq = (ballPos - projectedPoint).magSquared();
            if (projToBallDistSq < minPointToBallDistSq) {
              minPointToBallDistSq = projToBallDistSq;
              closestPoint = projectedPoint;
              deltaBetweenPoints = thisDelta;
              interpDeltaBetweenPoints = deltaBetweenPoints;
              closestWaypoint = wp;
              float lastToProjDist = (lastPoint - projectedPoint).magnitude();
              if (lastToProjDist < deltaBetweenInterpDistSq) {
                interpDeltaBetweenPoints =
                    zeus::CVector3f::lerp(0.5f * (thisDelta.normalized() + lastDelta.normalized()),
                                          thisDelta.normalized(), lastToProjDist / deltaBetweenInterpDist);
              } else if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
                lastToProjDist = (projectedPoint - wp->GetTranslation()).magnitude();
                if (lastToProjDist < deltaBetweenInterpDist) {
                  const float t = lastToProjDist / deltaBetweenInterpDist;
                  interpDeltaBetweenPoints =
                      zeus::CVector3f::lerp(((static_cast<const CScriptSpiderBallWaypoint*>(
                                                  mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)))
                                                  ->GetTranslation() -
                                              wp->GetTranslation())
                                                 .normalized() +
                                             thisDelta.normalized()) *
                                                0.5f,
                                            thisDelta.normalized(), t);
                }
              }
            }
          }
        }
        lastDelta = thisDelta;
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      } else {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      }
    } else {
      if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
        computeDelta = false;
      } else {
        break;
      }
    }
  }

  if ((ballPos - lastPoint).magSquared() >= minPointToBallDistSq) {
    return;
  }

  closestPoint = lastPoint;
  if (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
    deltaBetweenPoints = lastPoint - wp->GetTranslation();
    interpDeltaBetweenPoints = deltaBetweenPoints;
  }
  closestWaypoint = wp;
}
} // namespace metaforce


while (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
  }

#include "Runtime/World/CScriptSpiderBallWaypoint.hpp"

#include "Runtime/CStateManager.hpp"
#include "Runtime/World/CActorParameters.hpp"

#include "TCastTo.hpp" // Generated file, do not modify include path

namespace metaforce {
CScriptSpiderBallWaypoint::CScriptSpiderBallWaypoint(TUniqueId uid, std::string_view name, const CEntityInfo& info,
                                                     const zeus::CTransform& xf, bool active, u32 w1)
: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(EMaterialTypes::NoStepLogic),
         CActorParameters::None(), kInvalidUniqueId)
, xe8_(w1) {}

void CScriptSpiderBallWaypoint::Accept(IVisitor& visitor) { visitor.Visit(this); }

void CScriptSpiderBallWaypoint::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) {
  CActor::AcceptScriptMsg(msg, uid, mgr);

  if (msg == EScriptObjectMessage::InitializedInArea) {
    BuildWaypointListAndBounds(mgr);
  } else if (msg == EScriptObjectMessage::Arrived) {
    SendScriptMsgs(EScriptObjectState::Arrived, mgr, EScriptObjectMessage::None);
  }
}

void CScriptSpiderBallWaypoint::AccumulateBounds(const zeus::CVector3f& v) {
  if (!xfc_aabox) {
    xfc_aabox.emplace(v, v);
  }
  xfc_aabox->accumulateBounds(v);
}

void CScriptSpiderBallWaypoint::BuildWaypointListAndBounds(CStateManager& mgr) {
  u32 validConnections = 0;
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state == EScriptObjectState::Arrived && conn.x4_msg == EScriptObjectMessage::Next) {
      const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
      if (uid != kInvalidUniqueId) {
        static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid))->AddPreviousWaypoint(GetUniqueId());
        ++validConnections;
      }
    }
  }

  if (validConnections == 0) {
    AccumulateBounds(x34_transform.origin);
  } else {
    CScriptSpiderBallWaypoint* curWaypoint = this;
    TUniqueId uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    while (uid != kInvalidUniqueId) {
      curWaypoint = static_cast<CScriptSpiderBallWaypoint*>(mgr.ObjectById(uid));
      uid = curWaypoint->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck);
    }

    curWaypoint->AccumulateBounds(x34_transform.origin);
  }
}

void CScriptSpiderBallWaypoint::AddPreviousWaypoint(TUniqueId uid) { xec_waypoints.push_back(uid); }

TUniqueId CScriptSpiderBallWaypoint::PreviousWaypoint(const CStateManager& mgr,
                                                      ECheckActiveWaypoint checkActive) const {
  for (const auto& id : xec_waypoints) {
    if (const CEntity* ent = mgr.GetObjectById(id)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return id;
      }
      if (ent->GetActive()) {
        return id;
      }
    }
  }

  return kInvalidUniqueId;
}

TUniqueId CScriptSpiderBallWaypoint::NextWaypoint(const CStateManager& mgr, ECheckActiveWaypoint checkActive) const {
  for (const SConnection& conn : x20_conns) {
    if (conn.x0_state != EScriptObjectState::Arrived || conn.x4_msg != EScriptObjectMessage::Next) {
      continue;
    }
    const TUniqueId uid = mgr.GetIdForScript(conn.x8_objId);
    if (const CEntity* ent = mgr.GetObjectById(uid)) {
      if (checkActive == ECheckActiveWaypoint::SkipCheck) {
        return ent->GetUniqueId();
      }
      if (ent->GetActive()) {
        return ent->GetUniqueId();
      }
    }
  }

  return kInvalidUniqueId;
}

void CScriptSpiderBallWaypoint::GetClosestPointAlongWaypoints(CStateManager& mgr, const zeus::CVector3f& ballPos,
                                                              float maxPointToBallDist,
                                                              const CScriptSpiderBallWaypoint*& closestWaypoint,
                                                              zeus::CVector3f& closestPoint,
                                                              zeus::CVector3f& deltaBetweenPoints,
                                                              float deltaBetweenInterpDist,
                                                              zeus::CVector3f& interpDeltaBetweenPoints) const {
  const auto* wp = this;

  float minPointToBallDistSq = maxPointToBallDist * maxPointToBallDist;
  const float deltaBetweenInterpDistSq = deltaBetweenInterpDist * deltaBetweenInterpDist;
  zeus::CVector3f lastPoint = wp->GetTranslation();
  zeus::CVector3f lastDelta;
  bool computeDelta = wp->GetActive();

  while (true) {
    if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
      if (computeDelta) {
        const auto* prevWp = wp;
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));

        const zeus::CVector3f thisDelta = wp->GetTranslation() - lastPoint;
        const zeus::CVector3f lastPointToBall = ballPos - lastPoint;
        if (prevWp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) == kInvalidUniqueId) {
          lastDelta = thisDelta;
        }

        const float pointToBallDistSq = lastPointToBall.magSquared();
        if (pointToBallDistSq < minPointToBallDistSq) {
          minPointToBallDistSq = pointToBallDistSq;
          closestPoint = lastPoint;
          deltaBetweenPoints = thisDelta;
          interpDeltaBetweenPoints = (thisDelta.normalized() + lastDelta.normalized()) * 0.5f;
          closestWaypoint = wp;
        }

        const float projectedT = lastPointToBall.dot(thisDelta);
        if (projectedT >= 0.f) {
          const float normT = projectedT / thisDelta.magSquared();
          if (normT < 1.f) {
            const zeus::CVector3f projectedPoint = zeus::CVector3f::lerp(lastPoint, wp->GetTranslation(), normT);
            const float projToBallDistSq = (ballPos - projectedPoint).magSquared();
            if (projToBallDistSq < minPointToBallDistSq) {
              minPointToBallDistSq = projToBallDistSq;
              closestPoint = projectedPoint;
              deltaBetweenPoints = thisDelta;
              interpDeltaBetweenPoints = deltaBetweenPoints;
              closestWaypoint = wp;
              float lastToProjDist = (lastPoint - projectedPoint).magnitude();
              if (lastToProjDist < deltaBetweenInterpDistSq) {
                interpDeltaBetweenPoints =
                    zeus::CVector3f::lerp(0.5f * (thisDelta.normalized() + lastDelta.normalized()),
                                          thisDelta.normalized(), lastToProjDist / deltaBetweenInterpDist);
              } else if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
                lastToProjDist = (projectedPoint - wp->GetTranslation()).magnitude();
                if (lastToProjDist < deltaBetweenInterpDist) {
                  const float t = lastToProjDist / deltaBetweenInterpDist;
                  interpDeltaBetweenPoints =
                      zeus::CVector3f::lerp(((static_cast<const CScriptSpiderBallWaypoint*>(
                                                  mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)))
                                                  ->GetTranslation() -
                                              wp->GetTranslation())
                                                 .normalized() +
                                             thisDelta.normalized()) *
                                                0.5f,
                                            thisDelta.normalized(), t);
                }
              }
            }
          }
        }
        lastDelta = thisDelta;
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      } else {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::Check)));
        lastPoint = wp->GetTranslation();
        computeDelta = true;
      }
    } else {
      if (wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck) != kInvalidUniqueId) {
        wp = static_cast<const CScriptSpiderBallWaypoint*>(
            mgr.GetObjectById(wp->NextWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
        computeDelta = false;
      } else {
        break;
      }
    }
  }

  if ((ballPos - lastPoint).magSquared() >= minPointToBallDistSq) {
    return;
  }

  closestPoint = lastPoint;
  if (wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::Check) != kInvalidUniqueId) {
    wp = static_cast<const CScriptSpiderBallWaypoint*>(
        mgr.GetObjectById(wp->PreviousWaypoint(mgr, ECheckActiveWaypoint::SkipCheck)));
    deltaBetweenPoints = lastPoint - wp->GetTranslation();
    interpDeltaBetweenPoints = deltaBetweenPoints;
  }
  closestWaypoint = wp;
}
} // namespace metaforce


//===--- llvm/Support/DataStream.cpp - Lazy streamed data -----------------===//
//                     The LLVM Compiler Infrastructure
// This file implements DataStreamer, which fetches bytes of Data from
// a stream source. It provides support for streaming (lazy reading) of
// bitcode. An example implementation of streaming from a file or stdin
// is included.

#define DEBUG_TYPE "Data-stream"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/DataStream.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/system_error.h"
#include <string>
#include <cerrno>
#include <cstdio>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
using namespace llvm;

// Interface goals:
// * StreamableMemoryObject doesn't care about complexities like using
//   threads/async callbacks to actually overlap download+compile
// * Don't want to duplicate Data in memory
// * Don't need to know total Data len in advance
// StreamableMemoryObject already has random access so this interface only does
// in-order streaming (no arbitrary seeking, else we'd have to buffer all the
// Data here in addition to MemoryObject).  This also means that if we want
// to be able to to free Data, BitstreamBytes/BitcodeReader will implement it

STATISTIC(NumStreamFetches, "Number of calls to Data stream fetch");

namespace llvm {
DataStreamer::~DataStreamer() {}
}

namespace {

// Very simple stream backed by a file. Mostly useful for stdin and debugging;
// actual file access is probably still best done with mmap.
class DataFileStreamer : public DataStreamer {
 int Fd;
public:
  DataFileStreamer() : Fd(0) {}

{
    close(Fd);
  }
  virtual size_t GetBytes(unsigned char *buf, size_t len) {
    NumStreamFetches++;
    return read(Fd, buf, len);
  }

  error_code OpenFile(const std::string &Filename) {
    if (Filename == "-") {
      Fd = 0;
      sys::Program::ChangeStdinToBinary();
      return error_code::success();
    }

    int OpenFlags = O_RDONLY;
#ifdef O_BINARY
    OpenFlags |= O_BINARY;  // Open input file in binary mode on win32.
#endif
    Fd = ::open(Filename.c_str(), OpenFlags);
    if (Fd == -1)
      return error_code(errno, posix_category());
    return error_code::success();
  }
};

}

namespace llvm {
DataStreamer *getDataFileStreamer(const std::string &Filename,
                                  std::string *StrError) {
  DataFileStreamer *s = new DataFileStreamer();
  if (error_code e = s->OpenFile(Filename)) {
    *StrError = std::string("Could not open ") + Filename + ": " +
        e.message() + "\n";
    return NULL;
  }
  return s;
}

}


{
    if (Filename == "-") {
      Fd = 0;
      sys::Program::ChangeStdinToBinary();
      return error_code::success();
    }

    int OpenFlags = O_RDONLY;
#ifdef O_BINARY
    OpenFlags |= O_BINARY;  // Open input file in binary mode on win32.
#endif
    Fd = ::open(Filename.c_str(), OpenFlags);
    if (Fd == -1)
      return error_code(errno, posix_category());
    return error_code::success();
  }

//===--- llvm/Support/DataStream.cpp - Lazy streamed data -----------------===//
//                     The LLVM Compiler Infrastructure
// This file implements DataStreamer, which fetches bytes of Data from
// a stream source. It provides support for streaming (lazy reading) of
// bitcode. An example implementation of streaming from a file or stdin
// is included.

#define DEBUG_TYPE "Data-stream"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/DataStream.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/system_error.h"
#include <string>
#include <cerrno>
#include <cstdio>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
using namespace llvm;

// Interface goals:
// * StreamableMemoryObject doesn't care about complexities like using
//   threads/async callbacks to actually overlap download+compile
// * Don't want to duplicate Data in memory
// * Don't need to know total Data len in advance
// StreamableMemoryObject already has random access so this interface only does
// in-order streaming (no arbitrary seeking, else we'd have to buffer all the
// Data here in addition to MemoryObject).  This also means that if we want
// to be able to to free Data, BitstreamBytes/BitcodeReader will implement it

STATISTIC(NumStreamFetches, "Number of calls to Data stream fetch");

namespace llvm {
DataStreamer::~DataStreamer() {}
}

namespace {

// Very simple stream backed by a file. Mostly useful for stdin and debugging;
// actual file access is probably still best done with mmap.
class DataFileStreamer : public DataStreamer {
 int Fd;
public:
  DataFileStreamer() : Fd(0) {}
  virtual ~DataFileStreamer() {
    close(Fd);
  }
  virtual size_t GetBytes(unsigned char *buf, size_t len) {
    NumStreamFetches++;
    return read(Fd, buf, len);
  }

  error_code OpenFile(const std::string &Filename)

};

}

namespace llvm {
DataStreamer *getDataFileStreamer(const std::string &Filename,
                                  std::string *StrError) {
  DataFileStreamer *s = new DataFileStreamer();
  if (error_code e = s->OpenFile(Filename)) {
    *StrError = std::string("Could not open ") + Filename + ": " +
        e.message() + "\n";
    return NULL;
  }
  return s;
}

}


//===--- llvm/Support/DataStream.cpp - Lazy streamed data -----------------===//
//                     The LLVM Compiler Infrastructure
// This file implements DataStreamer, which fetches bytes of Data from
// a stream source. It provides support for streaming (lazy reading) of
// bitcode. An example implementation of streaming from a file or stdin
// is included.

#define DEBUG_TYPE "Data-stream"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/DataStream.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/system_error.h"
#include <string>
#include <cerrno>
#include <cstdio>
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
using namespace llvm;

// Interface goals:
// * StreamableMemoryObject doesn't care about complexities like using
//   threads/async callbacks to actually overlap download+compile
// * Don't want to duplicate Data in memory
// * Don't need to know total Data len in advance
// StreamableMemoryObject already has random access so this interface only does
// in-order streaming (no arbitrary seeking, else we'd have to buffer all the
// Data here in addition to MemoryObject).  This also means that if we want
// to be able to to free Data, BitstreamBytes/BitcodeReader will implement it

STATISTIC(NumStreamFetches, "Number of calls to Data stream fetch");

namespace llvm {
DataStreamer::~DataStreamer() {}
}

namespace {

// Very simple stream backed by a file. Mostly useful for stdin and debugging;
// actual file access is probably still best done with mmap.
class DataFileStreamer : public DataStreamer {
 int Fd;
public:
  DataFileStreamer() : Fd(0) {}
  virtual ~DataFileStreamer() {
    close(Fd);
  }
  virtual size_t GetBytes(unsigned char *buf, size_t len) {
    NumStreamFetches++;
    return read(Fd, buf, len);
  }

  error_code OpenFile(const std::string &Filename) {
    if (Filename == "-") {
      Fd = 0;
      sys::Program::ChangeStdinToBinary();
      return error_code::success();
    }

    int OpenFlags = O_RDONLY;
#ifdef O_BINARY
    OpenFlags |= O_BINARY;  // Open input file in binary mode on win32.
#endif
    Fd = ::open(Filename.c_str(), OpenFlags);
    if (Fd == -1)
      return error_code(errno, posix_category());
    retur

  }
};

}

namespace llvm {
DataStreamer *getDataFileStreamer(const std::string &Filename,
                                  std::string *StrError) {
  DataFileStreamer *s = new DataFileStreamer();
  if (error_code e = s->OpenFile(Filename)) {
    *StrError = std::string("Could not open ") + Filename + ": " +
        e.message() + "\n";
    return NULL;
  }
  return s;
}

}


#include <cmath>
#include <stdexcept>

#include "cuBERT/common.h"
#include "LayerNorm.h"

namespace cuBERT {

#ifdef HAVE_MKL
    template<>
    void layer_norm_<float>(const float *in,
                            float *inout,
                            const int batch_size,
                            const int channels,
                            float *mean_gpu,
                            float *var_gpu,
                            const float *beta,
                            const float *gamma,
                            void *stream) {
#pragma omp parallel for
        for (int batch_idx = 0; batch_idx < batch_size; ++batch_idx) {
            float mean = 0;
            float var = 0;
#pragma unroll
            for (int i = batch_idx * channels; i < (batch_idx + 1) * channels; ++i) {
                float t = inout[i] + in[i];
                mean += t;
                var += t * t;
            }
            mean = mean / channels;
            var = var / channels - mean * mean;

            var = 1.f / sqrtf(var + 1e-12f);

#pragma unroll
            for (int i = 0; i < channels; ++i) {
                int j = batch_idx * channels + i;
                inout[j] = beta[i] + gamma[i] * var * (inout[j] + in[j] - mean);
            }
        }
    }
#endif

    template <typename T>
    LayerNorm<T>::LayerNorm(size_t max_batch_size, size_t channels, T *beta, T *gamma) {
        this->channels = channels;

        this->mean_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));
        this->var_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));

        this->beta = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        this->gamma = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        cuBERT::memcpy(this->beta, beta, channels * sizeof(T), 1);
        cuBERT::memcpy(this->gamma, gamma, channels * sizeof(T), 1);
    }

    template <typename T>

{
        cuBERT::free(gamma);
        cuBERT::free(beta);

        cuBERT::free(var_gpu);
        cuBERT::free(mean_gpu);
    }

    template <typename T>
    void LayerNorm<T>::compute_(size_t batch_size, T *in, T *inout, void* stream) {
        layer_norm_<T>(in, inout, batch_size, channels, mean_gpu, var_gpu, beta, gamma, stream);
    }

    template class LayerNorm<float>;
#ifdef HAVE_CUDA
    template class LayerNorm<half>;
#endif
}


{
        cuBERT::free(gamma);
        cuBERT::free(beta);

        cuBERT::free(var_gpu);
        cuBERT::free(mean_gpu);
    }

#include <cmath>
#include <stdexcept>

#include "cuBERT/common.h"
#include "LayerNorm.h"

namespace cuBERT {

#ifdef HAVE_MKL
    template<>
    void layer_norm_<float>(const float *in,
                            float *inout,
                            const int batch_size,
                            const int channels,
                            float *mean_gpu,
                            float *var_gpu,
                            const float *beta,
                            const float *gamma,
                            void *stream) {
#pragma omp parallel for
        for (int batch_idx = 0; batch_idx < batch_size; ++batch_idx) {
            float mean = 0;
            float var = 0;
#pragma unroll
            for (int i = batch_idx * channels; i < (batch_idx + 1) * channels; ++i) {
                float t = inout[i] + in[i];
                mean += t;
                var += t * t;
            }
            mean = mean / channels;
            var = var / channels - mean * mean;

            var = 1.f / sqrtf(var + 1e-12f);

#pragma unroll
            for (int i = 0; i < channels; ++i) {
                int j = batch_idx * channels + i;
                inout[j] = beta[i] + gamma[i] * var * (inout[j] + in[j] - mean);
            }
        }
    }
#endif

    template <typename T>
    LayerNorm<T>::LayerNorm(size_t max_batch_size, size_t channels, T *beta, T *gamma) {
        this->channels = channels;

        this->mean_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));
        this->var_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));

        this->beta = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        this->gamma = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        cuBERT::memcpy(this->beta, beta, channels * sizeof(T), 1);
        cuBERT::memcpy(this->gamma, gamma, channels * sizeof(T), 1);
    }

    template <typename T>
    LayerNorm<T>::~LayerNorm()

    template <typename T>
    void LayerNorm<T>::compute_(size_t batch_size, T *in, T *inout, void* stream) {
        layer_norm_<T>(in, inout, batch_size, channels, mean_gpu, var_gpu, beta, gamma, stream);
    }

    template class LayerNorm<float>;
#ifdef HAVE_CUDA
    template class LayerNorm<half>;
#endif
}


#include <cmath>
#include <stdexcept>

#include "cuBERT/common.h"
#include "LayerNorm.h"

namespace cuBERT {

#ifdef HAVE_MKL
    template<>
    void layer_norm_<float>(const float *in,
                            float *inout,
                            const int batch_size,
                            const int channels,
                            float *mean_gpu,
                            float *var_gpu,
                            const float *beta,
                            const float *gamma,
                            void *stream) {
#pragma omp parallel for
        for (int batch_idx = 0; batch_idx < batch_size; ++batch_idx) {
            float mean = 0;
            float var = 0;
#pragma unroll
            for (int i = batch_idx * channels; i < (batch_idx + 1) * channels; ++i) {
                float t = inout[i] + in[i];
                mean += t;
                var += t * t;
            }
            mean = mean / channels;
            var = var / channels - mean * mean;

            var = 1.f / sqrtf(var + 1e-12f);

#pragma unroll
            for (int i = 0; i < channels; ++i) {
                int j = batch_idx * channels + i;
                inout[j] = beta[i] + gamma[i] * var * (inout[j] + in[j] - mean);
            }
        }
    }
#endif

    template <typename T>
    LayerNorm<T>::LayerNorm(size_t max_batch_size, size_t channels, T *beta, T *gamma) {
        this->channels = channels;

        this->mean_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));
        this->var_gpu = static_cast<T *>(cuBERT::malloc(max_batch_size * sizeof(T)));

        this->beta = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        this->gamma = static_cast<T *>(cuBERT::malloc(channels * sizeof(T)));
        cuBERT::memcpy(this->beta, beta, channels * sizeof(T

        cuBERT::memcpy(this->gamma, gamma, channels * sizeof(T), 1);
    }

    template <typename T>
    LayerNorm<T>::~LayerNorm() {
        cuBERT::free(gamma);
        cuBERT::free(beta);

        cuBERT::free(var_gpu);
        cuBERT::free(mean_gpu);
    }

    template <typename T>
    void LayerNorm<T>::compute_(size_t batch_size, T *in, T *inout, void* stream) {
        layer_norm_<T>(in, inout, batch_size, channels, mean_gpu, var_gpu, beta, gamma, stream);
    }

    template class LayerNorm<float>;
#ifdef HAVE_CUDA
    template class LayerNorm<half>;
#endif
}