// Copyright 2015-2016 by Artem Prilutskiy

#include "PatchCordProxy.h"
#include <stdlib.h>
#include <string.h>

#include <syslog.h>
#include <stdio.h>

#define SERVICE_NAME    "me.burnaway.BrandMeister"
#define OBJECT_PATH     "/me/burnaway/BrandMeister"
#define INTERFACE_NAME  "me.burnaway.BrandMeister"

// From AutoPatch.cpp
#define AUTOPATCH_LINK_NAME  "AutoPatch"

// From PatchCord.h
#define VALUE_CORD_OUTGOING_SOURCE_ID     1
#define VALUE_CORD_INCOMING_SOURCE_ID     4

PatchCordProxy::PatchCordProxy(uint32_t network, uint32_t link)
{
  banner = NULL;
  number = link;
  asprintf(&name, SERVICE_NAME ".network-%d", network);
  connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
}

PatchCordProxy::~PatchCordProxy()
{
  dbus_connection_unref(connection);
  free(banner);
  free(name);
}

void PatchCordProxy::setTalkerID(uint32_t value)
{
  getContextBanner();
  setSpecificValue(VALUE_CORD_OUTGOING_SOURCE_ID, value);
}

uint32_t PatchCordProxy::getTalkerID()
{
  getContextBanner();
  return getSpecificValue(VALUE_CORD_INCOMING_SOURCE_ID);
}

void PatchCordProxy::getContextBanner()
{
  DBusMessage* message = dbus_message_new_method_call(
    name, OBJECT_PATH, INTERFACE_NAME, "getContextList");

  const char* name = AUTOPATCH_LINK_NAME;

  dbus_message_append_args(message,
    DBUS_TYPE_STRING, &name,
    DBUS_TYPE_UINT32, &number,
    DBUS_TYPE_INVALID);

  DBusPendingCall* pending;
  if (dbus_connection_send_with_reply(connection, message, &pending, -1))
  {
    dbus_connection_flush(connection);
    dbus_message_unref(message);
    dbus_pending_call_block(pending);
    message = dbus_pending_call_steal_reply(pending);
    char** array;
    int count;
    if ((dbus_message_get_args(message, NULL,
          DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &array, &count,
          DBUS_TYPE_INVALID)) &&
        (count > 0))
    {
      free(banner);
      banner = strdup(*array);
      dbus_free_string_array(array);
    }
    dbus_pending_call_unref(pending);
  }
  dbus_message_unref(message);
}

void PatchCordProxy::setSpecificValue(uint32_t key, uint32_t value)
{
  DBusMessage* message = dbus_message_new_method_call(
    name, OBJECT_PATH, INTERFACE_NAME, "setSpecificValue");

  dbus_message_append_args(message,
    DBUS_TYPE_STRING, &banner,
    DBUS_TYPE_UINT32, &key,
    DBUS_TYPE_UINT32, &value,
    DBUS_TYPE_INVALID);
  
  DBusPendingCall* pending;
  if (dbus_connection_send_with_reply(connection, message, &pending, -1))
  {
    dbus_connection_flush(connection);
    dbus_message_unref(message);
    dbus_pending_call_block(pending);
    message = dbus_pending_call_steal_reply(pending);
    dbus_pending_call_unref(pending);
  }
  dbus_message_unref(message);
  
  // Each call of dbus_message_unref removes banner from the heap
  banner = NULL;
}

uint32_t PatchCordProxy::getSpecificValue(uint32_t key)
{
  DBusMessage* message = dbus_message_new_method_call(
    name, OBJECT_PATH, INTERFACE_NAME, "getContextData");

  dbus_message_append_args(message,
    DBUS_TYPE_STRING, &banner,
    DBUS_TYPE_INVALID);

  uint32_t value = 0;

  DBusPendingCall* pending;
  if (dbus_connection_send_with_reply(connection, message, &pending, -1))
  {
    dbus_connection_flush(connection);
    dbus_message_unref(message);
    dbus_pending_call_block(pending);
    message = dbus_pending_call_steal_reply(pending);
    const char* name;
    const char* address;
    dbus_uint32_t type;
    dbus_uint32_t state;
    dbus_uint32_t number;
    dbus_uint32_t* values;
    int count;
    if (dbus_message_get_args(message, NULL,
          DBUS_TYPE_STRING, &banner,
          DBUS_TYPE_STRING, &name,
          DBUS_TYPE_UINT32, &type,
          DBUS_TYPE_UINT32, &number,
          DBUS_TYPE_STRING, &address,
          DBUS_TYPE_UINT32, &state,
          DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &values, &count,
          DBUS_TYPE_INVALID))
      value = values[key];
    dbus_pending_call_unref(pending);
  }

  dbus_message_unref(message);

  // Each call of dbus_message_unref removes banner from the heap
  banner = NULL;

  return value;
}