#pragma once

/*
 *      Copyright (C) 2005-2013 Team XBMC
 *      http://xbmc.org
 *
 *  This Program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This Program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with XBMC; see the file COPYING.  If not, see
 *  <http://www.gnu.org/licenses/>.
 *
 */

#include <iomanip>
#include <stdexcept>
#include <sstream>

#include "DllWaylandClient.h"

/* These functions are wrappers around using the wayland protocol
 * directly. Unfortunately, since most of the protocol has autogenerated
 * binding code, and that binding code assumes that we're directly
 * linked to the client library (we're not), we can't use it. So
 * we need to use wl_proxy_create, wl_proxy_marshal and wl_proxy_destory
 * directly.
 * 
 * These functions effectively exist to reduce some of the duplication
 * that surrounds using these functions directly. You should look
 * at the autogenerated binding code to determine how wl_proxy_marshal
 * should be used to call a particular method.
 * 
 * Also note that there may be some cases where additional constructor
 * or destructor functions might need to be called before or after
 * CreateWaylandObject and DestroyWaylandObject.
 * 
 * If you need to call a method with more than six arguments,
 * you'll need to add a new overload here */
namespace xbmc
{
namespace wayland
{
namespace protocol
{
/* These functions call a method on a specifed wayland object
 * (you should use the pointer provided by the client library
 *  and not the wrapper) with the specified opcode and
 * arguments.
 * 
 * THERE IS NO TYPE CHECKING so you should be especially sure that
 * you're calling it with the right arguments. Failure to do so
 * will result in very strange behaviour
 */
template <typename Object>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode);
}
template <typename Object,
          typename A1>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode,
                               A1 arg1)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode,
                                      arg1);
}

template <typename Object,
          typename A1,
          typename A2>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode,
                               A1 arg1,
                               A2 arg2)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode,
                                      arg1,
                                      arg2);
}

template <typename Object,
          typename A1,
          typename A2,
          typename A3>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode,
                               A1 arg1,
                               A2 arg2,
                               A3 arg3)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode,
                                      arg1,
                                      arg2,
                                      arg3);
}

template <typename Object,
          typename A1,
          typename A2,
          typename A3,
          typename A4>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode,
                               A1 arg1,
                               A2 arg2,
                               A3 arg3,
                               A4 arg4)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode,
                                      arg1,
                                      arg2,
                                      arg3,
                                      arg4);
}

template <typename Object,
          typename A1,
          typename A2,
          typename A3,
          typename A4,
          typename A5>
void CallMethodOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               uint32_t opcode,
                               A1 arg1,
                               A2 arg2,
                               A3 arg3,
                               A4 arg4,
                               A5 arg5)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_marshaller()(proxy,
                                      opcode,
                                      arg1,
                                      arg2,
                                      arg3,
                                      arg4,
                                      arg5);
}

/* This function template returns a new unmanaged object pointer
 * as specified by Create with the RPC interface as specified
 * as a child in the server ownership hierarchy of factory.
 * 
 * Create must be castable to struct wl_proxy *, which
 * means that struct wl_proxy should be its first member. Generally
 * all wayland library proxy objects satisfy this criteria
 */ 
template <typename Create, typename Factory>
Create CreateWaylandObject(IDllWaylandClient &clientLibrary,
                           Factory factory,
                           struct wl_interface **interface)
{
  struct wl_proxy *pfactory =
    reinterpret_cast<struct wl_proxy *>(factory);
  struct wl_proxy *proxy =
    clientLibrary.wl_proxy_create(pfactory,
                                  reinterpret_cast<struct wl_interface *>(interface));

  if (!proxy)
  {
    std::stringstream ss;
    ss << "Failed to create "
       << typeid(Create).name()
       << " from factory "
       << typeid(Factory).name()
       << " at 0x"
       << std::hex
       << reinterpret_cast<void *>(pfactory)
       << std::dec;
    throw std::runtime_error(ss.str());
  }

  return reinterpret_cast<Create>(proxy);
}

/* This function adds a new "listener" to the object specified.
 * A "listener" is generally a struct of function pointers as specified
 * by the object's RPC interface for each event it can generate. These
 * can usually be found in the protocol header. "data" is passed
 * to each callback to make it a full closure. */
template <typename Object, typename Listener>
int AddListenerOnWaylandObject(IDllWaylandClient &clientLibrary,
                               Object object,
                               Listener listener,
                               void *data)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  
  /* C-style casts are bad, but there is no equavilent to
   * std::remove_const in C++98 and we are reinterpret_cast'ing
   * anyways */
  IDllWaylandClient::wl_proxy_listener_func *listenerFunc =
    (IDllWaylandClient::wl_proxy_listener_func *)((void *)listener);
  return clientLibrary.wl_proxy_add_listener(proxy, listenerFunc, data);
}

/* This function destroys the proxy object and releases its resources
 * on the client side. There may be an additional destroy request to
 * release resources on the server side. That must be called
 * prior to this. */
template <typename Object>
void DestroyWaylandObject(IDllWaylandClient &clientLibrary,
                          Object *object)
{
  struct wl_proxy *proxy =
    reinterpret_cast<struct wl_proxy *>(object);
  clientLibrary.wl_proxy_destroy(proxy);
}
}
}
}
