text: /*
   * lws abstract display
   *
   * Copyright (C) 2019 - 2022 Andy Green <andy@warmcat.com>
   *
   * 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 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.
   *
   * Secure Streams as DLO transport
   */
  
  #include <private-lib-core.h>
  #include "private-lib-drivers-display-dlo.h"
  
  LWS_SS_USER_TYPEDEF
  	sul_cb_t			on_rx;
  	lhp_ctx_t			*lhp;
  	lws_sorted_usec_list_t		*ssevsul; /* sul to use to resume rz */
  	lws_sorted_usec_list_t		sul; /* used for initial metadata cb */
  	lws_dlo_image_t			u; /* we use the lws_flow_t in here */
  	lws_dll2_t			active_asset_list; /*cx->active_assets*/
  	char				url[96];
  } dloss_t;
  
  /*
   * dlo images call back here when they have their dimensions (or have failed)
   */
  
  void
  lws_lhp_image_dimensions_cb(lws_sorted_usec_list_t *sul)
  {
  	dloss_t *m = lws_container_of(sul, dloss_t, sul);
  	lws_display_render_state_t *rs = lws_container_of(m->ssevsul,
  				lws_display_render_state_t, sul);
  
  	if (m->u.failed) {
  		m->u.u.dlo_png->dlo.box.w.whole = -1;
  		m->u.u.dlo_png->dlo.box.h.whole = -1;
  		lwsl_notice("%s: Failing %s\n", __func__, m->url);
  	} else {
  
  		m->u.u.dlo_png->dlo.box.w.whole = (int32_t)lws_dlo_image_width(&m->u);
  		m->u.u.dlo_png->dlo.box.h.whole = (int32_t)lws_dlo_image_height(&m->u);
  
  		lwsl_notice("%s: setting dlo box %d x %d\n", __func__,
  			(int)m->u.u.dlo_png->dlo.box.w.whole, (int)m->u.u.dlo_png->dlo.box.h.whole);
  	}
  
  	if (rs->html != 1) {
  		lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
  		return;
  	}
  }
  
  /* secure streams payload interface */
  
  static lws_ss_state_return_t
  dloss_rx(void *userobj, const uint8_t *buf, size_t len, int flags)
  {
  	dloss_t *m = (dloss_t *)userobj;
  	lws_stateful_ret_t r;
  
  	lwsl_info("%s: %u\n", __func__, (unsigned int)len);
  
  	/* .flow is at the same offset in both dlo_jpeg and dlo_png */
  
  	if (len &&
  	    lws_buflist_append_segment(&m->u.u.dlo_jpeg->flow.bl, buf, len) < 0) {
  		m->u.failed = 1;
  		lws_sul_schedule(lws_ss_get_context(m->ss), 0,
  				&m->sul, lws_lhp_image_dimensions_cb, 1);
  		return LWSSSSRET_DISCONNECT_ME;
  	}
  
  	// lwsl_notice("%s: buflen size %d\n", __func__,
  	//		(int)lws_buflist_total_len(&m->u.u.dlo_jpeg->flow.bl));
  
  	if (flags & LWSSS_FLAG_EOM) {
  		m->u.u.dlo_jpeg->flow.state = LWSDLOFLOW_STATE_READ_COMPLETED;
  		return LWSSSSRET_DISCONNECT_ME;
  	}
  
  	if (!lws_dlo_image_width(&m->u)) {
  		lws_flow_feed(&m->u.u.dlo_jpeg->flow);
  		r = lws_dlo_image_metadata_scan(&m->u);
  		lws_flow_req(&m->u.u.dlo_jpeg->flow);
  
  		if (r & LWS_SRET_FATAL) {
  			m->u.failed = 1;
  			lws_sul_schedule(lws_ss_get_context(m->ss), 0,
  					&m->sul, lws_lhp_image_dimensions_cb, 1);
  			return LWSSSSRET_DISCONNECT_ME;
  		}
  
  		if (r != LWS_SRET_WANT_INPUT) {
  			lwsl_notice("%s: seen metadata\n", __func__);
  			lws_sul_schedule(lws_ss_get_context(m->ss), 0,
  					&m->sul, lws_lhp_image_dimensions_cb, 1);
  		} //else
  			//lwsl_err("%s: metadata scan no end yet\n", __func__);
  
  		return LWSSSSRET_OK;
  	}
  
  	lws_sul_schedule(lws_ss_get_context(m->ss), 0, m->ssevsul, m->on_rx, 1);
  
  	return LWSSSSRET_OK;
  }
  
  static lws_ss_state_return_t
  dloss_state(void *userobj, void *sh, lws_ss_constate_t state,
  	    lws_ss_tx_ordinal_t ack)
  {
  	dloss_t *m = (dloss_t *)userobj;
  
  	switch (state) {
  	case LWSSSCS_CREATING:
  		break;
  
  	case LWSSSCS_DESTROYING:
  		lws_dll2_remove(&m->active_asset_list);
  		break;
  
  	default:
  		break;
  	}
  
  	return LWSSSSRET_OK;
  }
  
  static LWS_SS_INFO("default", dloss_t)
  	.rx				= dloss_rx,
  	.state				= dloss_state
  };
  
  /*
   * If we have an active image asset from this URL, return a pointer to its
   * dlo image (ie, dlo_jpeg or dlo_png)
   */
  
  int
  lws_dlo_ss_find(struct lws_context *cx, const char *url, lws_dlo_image_t *u)
  {
  	lws_start_foreach_dll(struct lws_dll2 *, d,
  			      lws_dll2_get_head(&cx->active_assets)) {
  		dloss_t *ds = lws_container_of(d, dloss_t, active_asset_list);
  
  		if (!strcmp(url, ds->url)) {
  			*u = ds->u;
  
  			return 0; /* found */
  		}
  
  	} lws_end_foreach_dll(d);
  
  	return 1; /* not found */
  }
  
  lws_dlo_t *
  lws_dlo_ss_create(lws_dlo_ss_create_info_t *i)
  {
  	lws_dlo_jpeg_t *dlo_jpeg = NULL;
  	lws_dlo_png_t *dlo_png = NULL;
  	size_t ul = strlen(i->url);
  	struct lws_ss_handle *h;
  	lws_ss_info_t ssi;
  	dloss_t *dloss;
  	lws_dlo_t *dlo;
  	char type;
  
  	if (ul < 5)
  		return NULL;
  
  	if (!strcmp(i->url + ul - 4, ".png"))
  		type = LWSDLOSS_TYPE_PNG;
  	else
  		if (!strcmp(i->url + ul - 4, ".jpg") ||
  		    !strcmp(i->url + ul - 5, ".jpeg"))
  			type = LWSDLOSS_TYPE_JPEG;
  		else
  			return NULL;
  
  	switch (type) {
  	case LWSDLOSS_TYPE_PNG:
  		dlo_png = lws_display_dlo_png_new(i->dl, i->dlo_parent, i->box);
  		if (!dlo_png)
  			return NULL;
  
  		i->u->u.dlo_png = dlo_png;
  
  		dlo_png->dlo.box.w.whole = (int32_t)
  			lws_upng_get_width(dlo_png->png);
  		dlo_png->dlo.box.w.frac = 0;
  		dlo_png->dlo.box.h.whole = (int32_t)
  			lws_upng_get_height(dlo_png->png);
  		dlo_png->dlo.box.h.frac = 0;
  
  		dlo = &dlo_png->dlo;
  		break;
  
  	case LWSDLOSS_TYPE_JPEG:
  		dlo_jpeg = lws_display_dlo_jpeg_new(i->dl, i->dlo_parent, i->box);
  		if (!dlo_jpeg)
  			return NULL;
  
  		i->u->u.dlo_jpeg = dlo_jpeg;
  
  		dlo_jpeg->dlo.box.w.whole = (int32_t)
  			lws_jpeg_get_width(dlo_jpeg->j);
  		dlo_jpeg->dlo.box.w.frac = 0;
  		dlo_jpeg->dlo.box.h.whole = (int32_t)
  			lws_jpeg_get_height(dlo_jpeg->j);
  		dlo_jpeg->dlo.box.h.frac = 0;
  
  		dlo = &dlo_jpeg->dlo;
  		break;
  	}
  
  	/* we adapt the initial tx credit also to the requested window */
  
  	ssi = ssi_dloss_t;
  	ssi.manual_initial_tx_credit = i->window;
  
  	if (lws_ss_create(i->cx, 0, &ssi, (void *)dlo, &h, NULL, NULL)) {
  		lwsl_notice("%s: unable to create ss\n", __func__);
  		return NULL;
  	}
  
  	dloss = (dloss_t *)lws_ss_to_user_object(h);
  	dloss->u.type = (lws_dlo_image_type_t)type;
  	dloss->on_rx = i->on_rx;
  	dloss->ssevsul = i->on_rx_sul;
  	dloss->lhp = i->lhp;
  	lws_strncpy(dloss->url, i->url, sizeof(dloss->url));
  
  	switch (type) {
  	case LWSDLOSS_TYPE_PNG:
  		dloss->u.u.dlo_png = dlo_png;
  		dlo_png->flow.h = h;
  		dlo_png->flow.window = i->window;
  		break;
  	case LWSDLOSS_TYPE_JPEG:
  		dloss->u.u.dlo_jpeg = dlo_jpeg;
  		dlo_jpeg->flow.h = h;
  		dlo_jpeg->flow.window = i->window;
  		break;
  	}
  
  	if (lws_ss_alloc_set_metadata(h, "endpoint", i->url, ul))
  		goto fail;
  
  	if (lws_ss_client_connect(dloss->ss))
  		goto fail;
  
  	lws_dll2_add_tail(&dloss->active_asset_list, &i->cx->active_assets);
  
  	lwsl_notice("%s: starting %s (dlo %p)\n", __func__, i->url, dlo);
  
  	return dlo;
  
  fail:
  	lws_ss_destroy(&h);
  
  	switch (type) {
  	case LWSDLOSS_TYPE_PNG:
  		lws_display_dlo_png_destroy(&dlo_png->dlo);
  		break;
  	case LWSDLOSS_TYPE_JPEG:
  		lws_display_dlo_jpeg_destroy(&dlo_jpeg->dlo);
  		break;
  	}
  
  	return NULL;
  }
  