/*
 * Copyright (C) 2017 Advanced Driver Information Technology Joint Venture GmbH
 * Copyright © 2020 Collabora, Ltd.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <linux/input.h>

#include <libweston/libweston.h>
#include <libweston/plugin-registry.h>

#include <weston.h>
#include <ivi-layout-export.h>

#include "plugin.h"
#include "transmitter_api.h"

/* waltham */
#include <errno.h>
#include <sys/epoll.h>
#include <sys/timerfd.h>
#include <waltham-object.h>
#include <waltham-client.h>
#include <waltham-connection.h>

#define MAX_EPOLL_WATCHES 2
#define ESTABLISH_CONNECTION_PERIOD 2000
#define RETRY_CONNECTION_PERIOD 5000

static const struct wthp_seat_listener seat_listener = {
	seat_capabilities,
	NULL
};

/* XXX: all functions and variables with a name, and things marked with a
 * comment, containing the word "fake" are mockups that need to be
 * removed from the final implementation.
 */

/** Send configure event through ivi-shell.
 *
 * \param txs The Transmitter surface.
 * \param width Suggestion for surface width.
 * \param height Suggestion for surface height.
 *
 * When the networking code receives a ivi_surface.configure event, it calls
 * this function to relay it to the application.
 *
 * \c txs cannot be a zombie, because transmitter_surface_zombify() must
 * tear down the network link, so a zombie cannot receive events.
 */
void
transmitter_surface_ivi_resize(struct weston_transmitter_surface *txs,
			       int32_t width, int32_t height)
{
	assert(txs->resize_handler);
	if (!txs->resize_handler)
		return;

	assert(txs->surface);
	if (!txs->surface)
		return;

	txs->resize_handler(txs->resize_handler_data, width, height);
}

static void
transmitter_surface_configure(struct weston_transmitter_surface *txs,
			      int32_t dx, int32_t dy)
{
	assert(txs->surface);
	if (!txs->surface)
		return;

	txs->attach_dx += dx;
	txs->attach_dy += dy;
}

static void
buffer_send_complete(struct wthp_buffer *b, uint32_t serial)
{
	if (b)
		wthp_buffer_destroy(b);
}

static const struct wthp_buffer_listener buffer_listener = {
	buffer_send_complete
};

/*
 * FIXME: note, at this point, no one is using this.
 */
static void
transmitter_surface_gather_state(struct weston_transmitter_surface *txs)
{
	struct weston_transmitter_remote *remote = txs->remote;
	struct waltham_display *dpy = remote->display;

	if (!dpy->running) {
		weston_log("transmitter_surface_gather_state() -- dpy not running\n");
		if (remote->status != WESTON_TRANSMITTER_CONNECTION_DISCONNECTED) {
			weston_log("transmitter_surface_gather_state() -- disconnected\n");

			remote->status =
				WESTON_TRANSMITTER_CONNECTION_DISCONNECTED;

			wth_connection_destroy(remote->display->connection);
			wl_event_source_remove(remote->source);
			wl_event_source_timer_update(remote->retry_timer, 1);
		}
	} else {
		/* TODO: transmit surface state to remote */
		/* The buffer must be transmitted to remote side */

		/* waltham */
		struct weston_surface *surf = txs->surface;
		struct weston_compositor *comp = surf->compositor;
		int32_t stride, data_sz, width, height;
		void *data;

		width = 1;
		height = 1;
		stride = width * (PIXMAN_FORMAT_BPP(comp->read_format) / 8);

		data = zalloc(stride * height);
		data_sz = stride * height;

		/* fake sending buffer */
		txs->wthp_buf = wthp_blob_factory_create_buffer(remote->display->blob_factory,
								data_sz, data, surf->width,
								surf->height, stride,
								PIXMAN_FORMAT_BPP(comp->read_format));

		wthp_buffer_set_listener(txs->wthp_buf, &buffer_listener, txs);

		wthp_surface_attach(txs->wthp_surf, txs->wthp_buf,
				    txs->attach_dx, txs->attach_dy);
		wthp_surface_damage(txs->wthp_surf, txs->attach_dx,
				    txs->attach_dy, surf->width, surf->height);
		wthp_surface_commit(txs->wthp_surf);

		wth_connection_flush(remote->display->connection);
		free(data);
		data = NULL;
		txs->attach_dx = 0;
		txs->attach_dy = 0;
	}
}

/** Mark the weston_transmitter_surface dead.
 *
 * Stop all remoting actions on this surface.
 *
 * Still keeps the pointer stored by a shell valid, so it can be freed later.
 */
static void
transmitter_surface_zombify(struct weston_transmitter_surface *txs)
{
	struct weston_transmitter_remote *remote;
	/* may be called multiple times */
	if (!txs->surface)
		return;

	wl_signal_emit(&txs->destroy_signal, txs);

	wl_list_remove(&txs->surface_destroy_listener.link);
	txs->surface = NULL;

	wl_list_remove(&txs->sync_output_destroy_listener.link);

	remote = txs->remote;

	if (!remote->display->compositor)
		weston_log("remote->compositor is NULL\n");

	if (txs->wthp_surf)
		wthp_surface_destroy(txs->wthp_surf);

	if (txs->wthp_ivi_surface)
		wthp_ivi_surface_destroy(txs->wthp_ivi_surface);

	/* In case called from destroy_transmitter() */
	txs->remote = NULL;
}

static void
transmitter_surface_destroy(struct weston_transmitter_surface *txs)
{
	transmitter_surface_zombify(txs);

	wl_list_remove(&txs->link);
	free(txs);
}

/** weston_surface destroy signal handler */
static void
transmitter_surface_destroyed(struct wl_listener *listener, void *data)
{
	struct weston_transmitter_surface *txs =
		wl_container_of(listener, txs, surface_destroy_listener);

	assert(data == txs->surface);

	transmitter_surface_zombify(txs);
}

void
sync_output_destroy_handler(struct wl_listener *listener, void *data)
{
	struct weston_transmitter_surface *txs;

	txs = wl_container_of(listener, txs, sync_output_destroy_listener);

	wl_list_remove(&txs->sync_output_destroy_listener.link);
	wl_list_init(&txs->sync_output_destroy_listener.link);

	//weston_surface_force_output(txs->surface, NULL);
	weston_surface_unmap(txs->surface);
}

static void
transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs,
			       const char *app_id)
{
        struct weston_transmitter_remote *remote = txs->remote;
	struct waltham_display *dpy = remote->display;

	assert(txs->surface);

	if (!dpy)
		return;

	if (!dpy->compositor)
		return;


	if (!dpy->seat)
		return;

	if (!dpy->application_id)
		return;

	txs->wthp_ivi_surface =
		wthp_ivi_app_id_surface_create(dpy->application_id, app_id, txs->wthp_surf);

	wth_connection_flush(remote->display->connection);
	if (!txs->wthp_ivi_surface) {
		weston_log("Failed to create txs->ivi_surf\n");
		return;
	}

	weston_log("Created wthp_ivi_surface %p\n", txs->wthp_ivi_surface);
}

static struct weston_transmitter_surface *
transmitter_surface_push_to_remote(struct weston_surface *ws, const char *app_id,
				   struct weston_transmitter_remote *remote,
				   struct wl_listener *stream_status)
{
	struct weston_transmitter *txr = remote->transmitter;
	struct weston_transmitter_surface *txs;
	bool found = false;

	if (remote->status != WESTON_TRANSMITTER_CONNECTION_READY) {
		return NULL;
	}

	wl_list_for_each(txs, &remote->surface_list, link) {
		if (txs->surface == ws) {
			found = true;
			break;
		}
	}

	if (!found) {
		txs = NULL;
		txs = zalloc(sizeof(*txs));

		if (!txs) {
			weston_log("No memory to create weston_transmitter_surface\n");
			return NULL;
		}

		txs->remote = remote;
		wl_signal_init(&txs->destroy_signal);
		wl_list_insert(&remote->surface_list, &txs->link);

		txs->status = WESTON_TRANSMITTER_STREAM_INITIALIZING;
		wl_signal_init(&txs->stream_status_signal);
		if (stream_status)
			wl_signal_add(&txs->stream_status_signal, stream_status);

		txs->surface = ws;
		txs->surface_destroy_listener.notify = transmitter_surface_destroyed;
		wl_signal_add(&ws->destroy_signal, &txs->surface_destroy_listener);

		wl_list_init(&txs->sync_output_destroy_listener.link);

		wl_list_init(&txs->frame_callback_list);
		wl_list_init(&txs->feedback_list);

		txs->lyt = weston_plugin_api_get(txr->compositor,
						 IVI_LAYOUT_API_NAME, sizeof(txs->lyt));
	}

	/* TODO: create the content stream connection... */
	if (!remote->display->compositor)
		weston_log("remote->compositor is NULL\n");

	if (!txs->wthp_surf) {
		txs->wthp_surf = wthp_compositor_create_surface(remote->display->compositor);
		wth_connection_flush(remote->display->connection);

		transmitter_surface_set_ivi_id(txs, app_id);
	}

	return txs;
}

static enum weston_transmitter_stream_status
transmitter_surface_get_stream_status(struct weston_transmitter_surface *txs)
{
	return txs->status;
}

/* waltham */

/* The server advertises a global interface.  We can store the ad for later
 * and/or bind to it immediately if we want to.  We also need to keep track of
 * the globals we bind to, so that global_remove can be handled properly (not
 * implemented).
 */
static void
registry_handle_global(struct wthp_registry *registry,
		       uint32_t name,
		       const char *interface,
		       uint32_t version)
{
	struct waltham_display *dpy = wth_object_get_user_data((struct wth_object *)registry);

	if (strcmp(interface, "wthp_compositor") == 0) {
		assert(!dpy->compositor);
		dpy->compositor = (struct wthp_compositor *) 
			wthp_registry_bind(registry, name, interface, 1);
		/* has no events to handle */
	} else if (strcmp(interface, "wthp_blob_factory") == 0) {
		assert(!dpy->blob_factory);
		dpy->blob_factory = (struct wthp_blob_factory *)
			wthp_registry_bind(registry, name, interface, 1);
		/* has no events to handle */
	} else if (strcmp(interface, "wthp_seat") == 0) {
		assert(!dpy->seat);
		dpy->seat = (struct wthp_seat *)
			wthp_registry_bind(registry, name, interface, 1);
		wthp_seat_set_listener(dpy->seat, &seat_listener, dpy);
	} else if (strcmp(interface, "wthp_ivi_application") == 0) {
	        assert(!dpy->application);
		dpy->application = (struct wthp_ivi_application *)
			wthp_registry_bind(registry, name, interface, 1);
	} else if (strcmp(interface, "wthp_ivi_app_id") == 0) {
	        assert(!dpy->application_id);
		dpy->application_id = (struct wthp_ivi_app_id *)
			wthp_registry_bind(registry, name, interface, 1);
	}
}

/* notify connection ready */
static void
conn_ready_notify(struct wl_listener *l, void *data)
{
	struct weston_transmitter_remote *remote =
		wl_container_of(l, remote, establish_listener);

	weston_log("conn_ready_notify()\n");

	/* Outputs and seats are dynamic, do not guarantee they are all
	 * present when signalling connection status.
	 */
	transmitter_remote_create_output_with_name(remote, strdup(remote->model));
	transmitter_remote_create_seat(remote);
}

/* waltham */
/* The server removed a global.
 * We should destroy everything we created through that global,
 * and destroy the objects we created by binding to it.
 * The identification happens by global's name, so we need to keep
 * track what names we bound.
 * (not implemented)
 */
static void
registry_handle_global_remove(struct wthp_registry *wthp_registry,
			      uint32_t name)
{
	if (wthp_registry)
		wthp_registry_free(wthp_registry);
}

static const struct wthp_registry_listener registry_listener = {
	registry_handle_global,
	registry_handle_global_remove
};

static void
connection_handle_data(struct watch *w, uint32_t events)
{
	struct waltham_display *dpy = wl_container_of(w, dpy, conn_watch);
	struct weston_transmitter_remote *remote = dpy->remote;
	int ret;


	if (!dpy->running) {
		weston_log("This server is not running yet. %s:%s\n", remote->addr, remote->port);
		return;
	}

	if (events & EPOLLERR) {
		weston_log("Connection errored out.\n");
		dpy->running = false;
		remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING;
		return;
	}

	if (events & EPOLLOUT) {
		/* Flush out again. If the flush completes, stop
		 * polling for writable as everything has been written.
		 */
		ret = wth_connection_flush(dpy->connection);
	}

	if (events & EPOLLIN) {
		/* Do not ignore EPROTO */
		ret = wth_connection_read(dpy->connection);

		if (ret < 0) {
			weston_log("Connection read error %s:%s\n", remote->addr, remote->port);
			perror("Connection read error\n");
			dpy->running = false;
			remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING;
			perror("EPOLL_CTL_DEL\n");
			return;
		}
	}

	if (events & EPOLLHUP) {
		weston_log("Connection hung up.\n");
		dpy->running = false;
		remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING;

		return;
	}
}

static int
waltham_mainloop(int fd, uint32_t mask, void *data)
{
	struct weston_transmitter_remote *remote = data;
	struct watch *w;
	int ret;
	int running_display;
	running_display = 0;

	struct waltham_display *dpy = remote->display;
	w = &dpy->conn_watch;
	if (!dpy)
		return -1;

	if (!dpy->connection)
		dpy->running = false;

	if (!dpy->running)
		return -1;

	running_display++;
	/* Dispatch queued events. */
	ret = wth_connection_dispatch(dpy->connection);
	if (ret < 0) {
		dpy->running = false;
		remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING;
	}

	if (!dpy->running)
		return -1;

	/* Run any application idle tasks at this point. */
	/* (nothing to run so far) */

	/* Flush out buffered requests. If the Waltham socket is
	 * full, poll it for writable too, and continue flushing then.
	 */
	ret = wth_connection_flush(dpy->connection);

	if (0 < running_display) {
		/* Waltham events only read in the callback, not dispatched,
		 * if the Waltham socket signalled readable. If it signalled
		 * writable, flush more. See connection_handle_data().
		 */
		w->cb(w, mask);
	}

	return 0;
}

static int
waltham_client_init(struct waltham_display *dpy)
{
	if (!dpy)
		return -1;
	/*
	 * get server_address from controller (adrress is set to weston.ini)
	 */
	dpy->connection = wth_connect_to_server(dpy->remote->addr, dpy->remote->port);
	if(!dpy->connection) {
		return -2;
	}
	else {
		dpy->remote->status = WESTON_TRANSMITTER_CONNECTION_READY;
		wl_signal_emit(&dpy->remote->connection_status_signal, dpy->remote);
	}

	dpy->conn_watch.display = dpy;
	dpy->conn_watch.cb = connection_handle_data;
	dpy->conn_watch.fd = wth_connection_get_fd(dpy->connection);
	dpy->remote->source = wl_event_loop_add_fd(dpy->remote->transmitter->loop,
						   dpy->conn_watch.fd,
						   WL_EVENT_READABLE,
						   waltham_mainloop, dpy->remote);

	dpy->display = wth_connection_get_display(dpy->connection);
	/* wth_display_set_listener() is already done by waltham, as
	 * all the events are just control messaging.
	 */

	/* Create a registry so that we will get advertisements of the
	 * interfaces implemented by the server.
	 */
	dpy->registry = wth_display_get_registry(dpy->display);
	wthp_registry_set_listener(dpy->registry, &registry_listener, dpy);

	/* Roundtrip ensures all globals' ads have been received. */
	if (wth_connection_roundtrip(dpy->connection) < 0) {
		weston_log("Roundtrip failed.\n");
		return -1;
	}

	if (!dpy->compositor) {
		weston_log("Did not find wthp_compositor, quitting.\n");
		return -1;
	}

	dpy->running = true;
	weston_log("waltham_client_init()\n");

	return 0;
}

static int
establish_timer_handler(void *data)
{
	struct weston_transmitter_remote *remote = data;
	int ret;

	ret = waltham_client_init(remote->display);
	if (ret == -2) {
		wl_event_source_timer_update(remote->establish_timer,
					     ESTABLISH_CONNECTION_PERIOD);
		return 0;
	}

	weston_log("establish_timer_handler()\n");

	remote->status = WESTON_TRANSMITTER_CONNECTION_READY;
	wl_signal_emit(&remote->connection_status_signal, remote);
	return 0;
}

static void
init_globals(struct waltham_display *dpy)
{
	dpy->compositor = NULL;
	dpy->blob_factory = NULL;
	dpy->seat = NULL;
	dpy->application = NULL;
	dpy->pointer = NULL;
	dpy->keyboard = NULL;
	dpy->touch = NULL;
}

static void
disconnect_surface(struct weston_transmitter_remote *remote)
{
	struct weston_transmitter_surface *txs;

	wl_list_for_each(txs, &remote->surface_list, link) {
		free(txs->wthp_ivi_surface);
		txs->wthp_ivi_surface = NULL;
		free(txs->wthp_surf);
		txs->wthp_surf = NULL;
	}
}

static int
retry_timer_handler(void *data)
{
	struct weston_transmitter_remote *remote = data;
	struct waltham_display *dpy = remote->display;

	weston_log("retry_timer_handler()\n");

	if (!dpy->running) {
		registry_handle_global_remove(dpy->registry, 1);
		init_globals(dpy);
		disconnect_surface(remote);
		wl_event_source_timer_update(remote->establish_timer,
					     ESTABLISH_CONNECTION_PERIOD);
		return 0;
	} else {
		wl_event_source_timer_update(remote->retry_timer,
					     RETRY_CONNECTION_PERIOD);
	}

	return 0;
}

static struct weston_transmitter_remote *
transmitter_connect_to_remote(struct weston_transmitter *txr)
{
	struct weston_transmitter_remote *remote;
	struct wl_event_loop *loop_est, *loop_retry;

	wl_list_for_each_reverse(remote, &txr->remote_list, link) {
		/* XXX: actually start connecting */
		/* waltham */
		remote->display = zalloc(sizeof *remote->display);
		if (!remote->display) {
			weston_log("Failed to allocate remote display\n");
			return NULL;
		}

		remote->display->remote = remote;

		weston_log("transmitter_connect_to_remote() for remote %p\n", remote);

		/* set connection establish timer */
		loop_est = wl_display_get_event_loop(txr->compositor->wl_display);
		remote->establish_timer =
			wl_event_loop_add_timer(loop_est, establish_timer_handler, remote);
		wl_event_source_timer_update(remote->establish_timer, 1);

		/* set connection retry timer */
		loop_retry = wl_display_get_event_loop(txr->compositor->wl_display);
		remote->retry_timer =
			wl_event_loop_add_timer(loop_retry, retry_timer_handler, remote);
		wl_signal_emit(&remote->conn_establish_signal, NULL);
	}

	return remote;
}

static enum weston_transmitter_connection_status
transmitter_remote_get_status(struct weston_transmitter_remote *remote)
{
	return remote->status;
}

static void
transmitter_remote_destroy(struct weston_transmitter_remote *remote)
{
	struct weston_transmitter_surface *txs;
	struct weston_transmitter_output *output, *otmp;
	struct weston_transmitter_seat *seat, *stmp;

	/* Do not emit connection_status_signal. */

	/*
	 *  Must not touch remote->transmitter as it may be stale: the
	 *  destruction order between the shell and Transmitter is undefined.
	 */

	if (!wl_list_empty(&remote->surface_list))
		weston_log("Transmitter warning: surfaces remain in %s.\n", __func__);

	wl_list_for_each(txs, &remote->surface_list, link)
		txs->remote = NULL;

	wl_list_remove(&remote->surface_list);

	wl_list_for_each_safe(seat, stmp, &remote->seat_list, link)
		transmitter_seat_destroy(seat);

	wl_list_for_each_safe(output, otmp, &remote->output_list, link)
		transmitter_output_destroy(output);

	free(remote->addr);
	wl_list_remove(&remote->link);

	wl_event_source_remove(remote->source);

	free(remote);
}

/** Transmitter is destroyed on compositor shutdown. */
static void
transmitter_compositor_destroyed(struct wl_listener *listener, void *data)
{
	struct weston_transmitter_remote *remote;
	struct weston_transmitter_surface *txs;
	struct weston_transmitter *txr =
		wl_container_of(listener, txr, compositor_destroy_listener);

	assert(data == txr->compositor);

	/* may be called before or after shell cleans up */
	wl_list_for_each(remote, &txr->remote_list, link) {
		wl_list_for_each(txs, &remote->surface_list, link) {
			transmitter_surface_zombify(txs);
		}
	}

	/*
	 * Remove the head in case the list is not empty, to avoid
	 * transmitter_remote_destroy() accessing freed memory if the shell
	 * cleans up after Transmitter.
	 */
	wl_list_remove(&txr->remote_list);

	free(txr);
}

static struct weston_transmitter *
transmitter_get(struct weston_compositor *compositor)
{
	struct wl_listener *listener;
	struct weston_transmitter *txr;

	listener = wl_signal_get(&compositor->destroy_signal,
				 transmitter_compositor_destroyed);
	if (!listener)
		return NULL;

	txr = wl_container_of(listener, txr, compositor_destroy_listener);
	assert(compositor == txr->compositor);

	return txr;
}

static void
transmitter_register_connection_status(struct weston_transmitter *txr,
				       struct wl_listener *connected_listener)
{
	wl_signal_add(&txr->connected_signal, connected_listener);
}

static struct weston_surface *
transmitter_get_weston_surface(struct weston_transmitter_surface *txs)
{
	return txs->surface;
}

static struct weston_transmitter_remote *
transmitter_get_transmitter_remote(const char *output_transmitter_name, struct weston_transmitter *transmitter)
{

	struct weston_transmitter_remote *trans_remote;

	if (!output_transmitter_name)
		return NULL;

	wl_list_for_each(trans_remote, &transmitter->remote_list, link) {
		struct weston_transmitter_output *trans_output;
		wl_list_for_each(trans_output, &trans_remote->output_list, link) {
			const char *name = trans_output->base.name;

			if (name && !strcmp(output_transmitter_name, name))
				return trans_remote;

			name = trans_output->name;
			if (name && !strcmp(output_transmitter_name, name))
				return trans_remote;
		}
	}

	return NULL;
}

static const struct weston_transmitter_api transmitter_api_impl = {
	transmitter_get,
	transmitter_connect_to_remote,
	transmitter_remote_get_status,
	transmitter_remote_destroy,
	transmitter_surface_push_to_remote,
	transmitter_surface_get_stream_status,
	transmitter_surface_destroy,
	transmitter_surface_configure,
	transmitter_surface_gather_state,
	transmitter_register_connection_status,
	transmitter_get_weston_surface,
	transmitter_get_transmitter_remote,
};

static void
transmitter_surface_set_resize_callback(
	struct weston_transmitter_surface *txs,
	weston_transmitter_ivi_resize_handler_t cb,
	void *data)
{
	txs->resize_handler = cb;
	txs->resize_handler_data = data;
}

static void
_transmitter_surface_set_ivi_id(struct weston_transmitter_surface *txs, uint32_t ivi_id)
{

}

static const struct weston_transmitter_ivi_api transmitter_ivi_api_impl = {
	_transmitter_surface_set_ivi_id,
	transmitter_surface_set_resize_callback,
};

static int
transmitter_create_remote(struct weston_transmitter *txr,
			  const char *model,
			  const char *addr,
			  const char *port,
	                  const char *width,
	                  const char *height)
{
	struct weston_transmitter_remote *remote;

	remote = zalloc(sizeof (*remote));
	if (!remote)
		return -1;

	remote->transmitter = txr;
	wl_list_insert(&txr->remote_list, &remote->link);

	remote->model = strdup(model);
	remote->addr = strdup(addr);
	remote->port = strdup(port);
	if (remote->width)
		remote->width = atoi(width);
	if (remote->height)
		remote->height = atoi(height);
	remote->status = WESTON_TRANSMITTER_CONNECTION_INITIALIZING;

	weston_log("Creating transmitter remote output model %s, addr %s, "
			"port %s, width %d, height %d, status %d\n",
			remote->model, remote->addr, remote->port, remote->width,
			remote->height, remote->status);

	wl_signal_init(&remote->connection_status_signal);

	wl_list_init(&remote->output_list);
	wl_list_init(&remote->surface_list);
	wl_list_init(&remote->seat_list);

	wl_signal_init(&remote->conn_establish_signal);
	remote->establish_listener.notify = conn_ready_notify;
	wl_signal_add(&remote->conn_establish_signal, &remote->establish_listener);

	return 0;
}

struct wet_compositor {
	struct weston_config *config;
	struct wet_output_config *parsed_options;
	struct wl_listener pending_output_listener;
	bool drm_use_current_mode;
};

static int
load_config(struct weston_config **config, bool no_config,
	    const char *config_file)
{
	const char *file = "agl-compositor.ini";
	const char *full_path;
	char *compositor_env_file = NULL;

	if (config_file)
		file = config_file;

	compositor_env_file = getenv(WESTON_CONFIG_FILE_ENV_VAR);
	if (compositor_env_file)
		file = compositor_env_file;

	if (!no_config)
		*config = weston_config_parse(file);

	if (*config) {
		full_path = weston_config_get_full_path(*config);

		weston_log("Using config file '%s'.\n", full_path);
		setenv(WESTON_CONFIG_FILE_ENV_VAR, full_path, 1);

		return 0;
	}

	if (config_file && !no_config) {
		weston_log("fatal: error opening or reading config file '%s'.\n",
			config_file);

		return -1;
	}

	weston_log("Starting with no config file.\n");
	setenv(WESTON_CONFIG_FILE_ENV_VAR, "", 1);

	return 0;
}


static void
transmitter_get_server_config(struct weston_transmitter *txr)
{
	struct weston_config *config = NULL;
	struct weston_config_section *section;
	const char *name = NULL;
	char *model = NULL;
	char *addr = NULL;
	char *port = NULL;
	char *width = NULL;
	char *height = NULL;
	int ret;

	/* FIXME: this assume as hard-coded agl-compositor */
	if (load_config(&config, 0, "agl-compositor.ini") < 0)
		return;

	section = weston_config_get_section(config, "remote", NULL, NULL);

	while (weston_config_next_section(config, &section, &name)) {
		if (0 == strcmp(name, "transmitter-output")) {
			if (0 != weston_config_section_get_string(section, "name",
								  &model, 0))
				continue;

			if (0 != weston_config_section_get_string(section, "host",
								  &addr, 0))
				continue;

			if (0 != weston_config_section_get_string(section, "port",
								  &port, 0))
				continue;

			ret = transmitter_create_remote(txr, model, addr,
							port, width, height);
			if (ret < 0) {
				weston_log("Fatal: Transmitter create_remote failed.\n");
			}
		}
	}
}

WL_EXPORT int
wet_module_init(struct weston_compositor *compositor, int *argc, char *argv[])
{
	struct weston_transmitter *txr;
	int ret;

	txr = zalloc(sizeof *txr);
	if (!txr){
		weston_log("Transmitter disabled\n");
		return -1;
	}
	wl_list_init(&txr->remote_list);

	txr->compositor = compositor;
	txr->compositor_destroy_listener.notify =
		transmitter_compositor_destroyed;
	wl_signal_add(&compositor->destroy_signal,
		      &txr->compositor_destroy_listener);

	ret = weston_plugin_api_register(compositor,
					 WESTON_TRANSMITTER_API_NAME,
					 &transmitter_api_impl,
					 sizeof(transmitter_api_impl));
	if (ret < 0) {
		weston_log("Fatal: Transmitter API registration failed.\n");
		goto fail;
	}

	ret = weston_plugin_api_register(compositor,
					 WESTON_TRANSMITTER_IVI_API_NAME,
					 &transmitter_ivi_api_impl,
					 sizeof(transmitter_ivi_api_impl));
	if (ret < 0) {
		weston_log("Fatal: Transmitter IVI API registration failed.\n");
		goto fail;
	}

	weston_log("Transmitter initialized.\n");

	txr->loop = wl_display_get_event_loop(compositor->wl_display);
	transmitter_get_server_config(txr);
	transmitter_connect_to_remote(txr);

	return 0;

fail:
	wl_list_remove(&txr->compositor_destroy_listener.link);
	free(txr);

	return -1;
}