/*
 * Copyright © Canonical Ltd.
 *
 * This program is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 or 3,
 * as published by the Free Software Foundation.
 *
 * 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.
 *
 * 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 <mir/default_server_configuration.h>
#include <mir/fatal.h>
#include <mir/options/default_configuration.h>
#include <mir/glib_main_loop.h>
#include <mir/default_server_status_listener.h>
#include <mir/emergency_cleanup.h>
#include <mir/frontend/wayland.h>

#include <mir/logging/dumb_console_logger.h>
#include <mir/options/option.h>
#include <mir/options/program_option.h>
#include <mir/frontend/session_credentials.h>
#include <mir/frontend/session_authorizer.h>
#include <mir/graphics/cursor.h>
#include <mir/scene/null_session_listener.h>
#include <mir/graphics/display.h>
#include <mir/input/cursor_observer.h>
#include <mir/input/vt_filter.h>
#include <mir/input/input_manager.h>
#include <mir/time/steady_clock.h>
#include <mir/geometry/rectangles.h>
#include "default_emergency_cleanup.h"
#include <mir/graphics/platform.h>
#include <mir/console_services.h>
#include <mir/decoration_strategy.h>
#include <mir/scene/session.h>

namespace mc = mir::compositor;
namespace geom = mir::geometry;
namespace mf = mir::frontend;
namespace mg = mir::graphics;
namespace ml = mir::logging;
namespace mo = mir::options;
namespace ms = mir::scene;
namespace msh = mir::shell;
namespace mi = mir::input;

namespace
{
std::map<std::string, mir::ServerConfiguration::WaylandProtocolExtensionFilter> get_default_wayland_extension_policy_map()
{
    std::map<std::string, mir::ServerConfiguration::WaylandProtocolExtensionFilter> result;
    for (auto const& extension : mir::frontend::get_supported_extensions())
        result[extension] = [](auto const&, const char*) { return false; };
    for (auto const& extension: mir::frontend::get_standard_extensions())
        result[extension] = [](auto const&, const char*) { return true; };
    return result;
}
}

mir::DefaultServerConfiguration::DefaultServerConfiguration(int argc, char const* argv[]) :
        DefaultServerConfiguration(std::make_shared<mo::DefaultConfiguration>(argc, argv))
{
}

mir::DefaultServerConfiguration::DefaultServerConfiguration(std::shared_ptr<mo::Configuration> const& configuration_options) :
    configuration_options(configuration_options),
    wayland_extension_policy_map(get_default_wayland_extension_policy_map()),
    wayland_extension_filter([this](auto const& session, auto const& interface_name)
    {
        auto const it = wayland_extension_policy_map.find(interface_name);
        if (it != wayland_extension_policy_map.end())
            return it->second(session, interface_name)
                || session->process_id() == getpid();

        return true;
    })
{
}

auto mir::DefaultServerConfiguration::the_options() const
->std::shared_ptr<options::Option>
{
    return configuration_options->global_options();
}

auto mir::DefaultServerConfiguration::the_options_provider() const
    -> std::shared_ptr<mo::Configuration>
{
    return configuration_options;
}

std::shared_ptr<ms::SessionListener>
mir::DefaultServerConfiguration::the_session_listener()
{
    return session_listener(
        []
        {
            return std::make_shared<ms::NullSessionListener>();
        });
}

std::shared_ptr<mf::SessionAuthorizer>
mir::DefaultServerConfiguration::the_session_authorizer()
{
    struct DefaultSessionAuthorizer : public mf::SessionAuthorizer
    {
        bool connection_is_allowed(mf::SessionCredentials const& /* creds */) override
        {
            return true;
        }
    };
    return session_authorizer(
        [&]()
        {
            return std::make_shared<DefaultSessionAuthorizer>();
        });
}

std::shared_ptr<mir::time::Clock> mir::DefaultServerConfiguration::the_clock()
{
    return clock(
        []()
        {
            return std::make_shared<mir::time::SteadyClock>();
        });
}

std::shared_ptr<mir::MainLoop> mir::DefaultServerConfiguration::the_main_loop()
{
    return main_loop(
        [this]() -> std::shared_ptr<mir::MainLoop>
        {
            return std::make_shared<mir::GLibMainLoop>(the_clock());
        });
}

std::shared_ptr<mir::ServerActionQueue> mir::DefaultServerConfiguration::the_server_action_queue()
{
    return the_main_loop();
}

std::shared_ptr<mir::ServerStatusListener> mir::DefaultServerConfiguration::the_server_status_listener()
{
    return server_status_listener(
        []()
        {
            return std::make_shared<mir::DefaultServerStatusListener>();
        });
}

std::shared_ptr<mir::EmergencyCleanup> mir::DefaultServerConfiguration::the_emergency_cleanup()
{
    return emergency_cleanup(
        []()
        {
            return std::make_shared<DefaultEmergencyCleanup>();
        });
}

std::function<void()> mir::DefaultServerConfiguration::the_stop_callback()
{
    return []{};
}

auto mir::DefaultServerConfiguration::the_logger()
    -> std::shared_ptr<ml::Logger>
{
    return logger(
        []() -> std::shared_ptr<ml::Logger>
        {
            return std::make_shared<ml::DumbConsoleLogger>();
        });
}

auto mir::DefaultServerConfiguration::the_decoration_strategy() -> std::shared_ptr<mir::DecorationStrategy>
{
    if (!decoration_strategy)
    {
        class DefaultDecorationStrategy: public mir::DecorationStrategy
        {
            DecorationsType default_style() const override { return DecorationsType::csd; }
            DecorationsType request_style(DecorationsType type) const override { return type; }
        };

        decoration_strategy = std::make_shared<DefaultDecorationStrategy>();
    }

    return decoration_strategy;
}

void mir::DefaultServerConfiguration::set_the_decoration_strategy(std::shared_ptr<mir::DecorationStrategy> strategy)
{
    decoration_strategy = strategy;
}
