/*
 * DISTRHO Plugin Framework (DPF)
 * Copyright (C) 2012-2016 Filipe Coelho <falktx@falktx.com>
 *
 * Permission to use, copy, modify, and/or distribute this software for any purpose with
 * or without fee is hereby granted, provided that the above copyright notice and this
 * permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
 * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
 * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED
#define DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED

#include "String.hpp"

#ifdef DISTRHO_OS_WINDOWS
# error Unsupported platform!
#else
# include <cerrno>
# include <signal.h>
# include <sys/wait.h>
# include <unistd.h>
#endif

START_NAMESPACE_DISTRHO

// -----------------------------------------------------------------------
// ExternalWindow class

class ExternalWindow
{
public:
    ExternalWindow(const uint w = 1, const uint h = 1, const char* const t = "")
        : width(w),
          height(h),
          title(t),
          pid(0) {}

    virtual ~ExternalWindow()
    {
        terminateAndWaitForProcess();
    }

    uint getWidth() const noexcept
    {
        return width;
    }

    uint getHeight() const noexcept
    {
        return height;
    }

    const char* getTitle() const noexcept
    {
        return title;
    }

    void setTitle(const char* const t) noexcept
    {
        title = t;
    }

    bool isRunning() noexcept
    {
        if (pid <= 0)
            return false;

        const pid_t p = ::waitpid(pid, nullptr, WNOHANG);

        if (p == pid || (p == -1 && errno == ECHILD))
        {
            printf("NOTICE: Child process exited while idle\n");
            pid = 0;
            return false;
        }

        return true;
    }

protected:
    bool startExternalProcess(const char* args[])
    {
        terminateAndWaitForProcess();

        pid = vfork();

        switch (pid)
        {
        case 0:
            execvp(args[0], (char**)args);
            _exit(1);
            return false;

        case -1:
            printf("Could not start external ui\n");
            return false;

        default:
            return true;
        }
    }

private:
    uint width;
    uint height;
    String title;
    pid_t pid;

    friend class UIExporter;

    void terminateAndWaitForProcess()
    {
        if (pid <= 0)
            return;

        printf("Waiting for previous process to stop,,,\n");

        bool sendTerm = true;

        for (pid_t p;;)
        {
            p = ::waitpid(pid, nullptr, WNOHANG);

            switch (p)
            {
            case 0:
                if (sendTerm)
                {
                    sendTerm = false;
                    ::kill(pid, SIGTERM);
                }
                break;

            case -1:
                if (errno == ECHILD)
                {
                    printf("Done! (no such process)\n");
                    pid = 0;
                    return;
                }
                break;

            default:
                if (p == pid)
                {
                    printf("Done! (clean wait)\n");
                    pid = 0;
                    return;
                }
                break;
            }

            // 5 msec
            usleep(5*1000);
        }
    }

    DISTRHO_DECLARE_NON_COPY_CLASS(ExternalWindow)
};

// -----------------------------------------------------------------------

END_NAMESPACE_DISTRHO

#endif // DISTRHO_EXTERNAL_WINDOW_HPP_INCLUDED
