/*****************************************************************
 * gmerlin - a general purpose multimedia framework and applications
 *
 * Copyright (c) 2001 - 2012 Members of the Gmerlin project
 * gmerlin-general@lists.sourceforge.net
 * http://gmerlin.sourceforge.net
 *
 * 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 of the License, 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 * *****************************************************************/

#include <string.h>

#include "server.h"

#define LOG_DOMAIN "deviceproxy"

#define MAX_IDLE_TIME (10*GAVL_TIME_SCALE)

typedef struct
  {
  bg_websocket_context_t * ws;
  bg_remote_device_handle_t * h;
  gavl_timer_t * timer;
  } device_proxy_t;

static void free_data(void * d)
  {
  device_proxy_t * p = d;
  if(p->h)
    bg_remote_device_handle_destroy(p->h);

  if(p->ws)
    bg_websocket_context_destroy(p->ws);
  if(p->timer)
    gavl_timer_destroy(p->timer);
  free(p);
  }

static void device_proxy_func(client_t * cl)
  {
  gavl_time_t delay_time = GAVL_TIME_SCALE / 50; // 20 ms granularity
  device_proxy_t * priv = cl->data;

  client_set_status(cl, CLIENT_STATUS_RUNNING);
  
  while(1)
    {
    if(client_get_status(cl) == CLIENT_STATUS_STOP)
      return;
    
    if(!bg_remote_device_handle_ping(priv->h) &&
       !bg_websocket_context_iteration(priv->ws))
      gavl_time_delay(&delay_time);

    if(!bg_websocket_context_num_clients(priv->ws))
      {
      if(gavl_timer_is_running(priv->timer))
        {
        if(gavl_timer_get(priv->timer) > MAX_IDLE_TIME)
          {
          gavl_log(GAVL_LOG_INFO, LOG_DOMAIN,
                 "Device was idle for 10 secs, closing down control handle");
          return; 
          }
        }
      else
        {
        gavl_timer_set(priv->timer, 0);
        gavl_timer_start(priv->timer);
        }
      }
    else
      {
      if(gavl_timer_is_running(priv->timer))
        gavl_timer_stop(priv->timer);
      }
    
    }
  
  }


static device_proxy_t *
device_proxy_create(server_t * s,
                    const bg_remote_device_info_t * info)
  {
  int result = 0;
  char * url = NULL;
  char id[BG_REMOTE_DEVICE_ID_LEN+1];
  device_proxy_t * ret = calloc(1, sizeof(*ret));

  bg_remote_device_info_get_id(info, id);
  
  url = bg_sprintf("%s%s%s", s->root_url, BG_REMOTE_DEVICE_PATH, id);
  
  if(!(ret->h = bg_remote_device_handle_create(info, url)))
    goto fail;

  if(!(ret->ws = bg_websocket_context_create(info->type, NULL, NULL,
                                             bg_remote_device_handle_get_controllable(ret->h))))
    goto fail;

  ret->timer = gavl_timer_create();
  
  return ret;
  
  result = 1;
  fail:

  if(!result)
    {
    free_data(ret);
    ret = NULL;
    }

  if(url)
    free(url);
  
  return ret;
  
  }




/*
 * Path:
 * /remotedev/<MD5>      Websocket
 * /remotedev/<MD5>/etc  Backend defined
 */

int device_proxy_handle(bg_http_connection_t * conn, void * data)
  {
  int result = 0;
  server_t * s = data;
  client_t * dev = NULL;
  char * pos;
  int i;
  int name_len;
  bg_remote_device_info_t * infos = NULL;
  device_proxy_t * priv = NULL;
  
  /* Create device if necessary */
  
  for(i = 0; i < s->num_clients; i++)
    {
    if(!s->clients[i]->name ||
       (s->clients[i]->type != CLIENT_TYPE_DEVICEPROXY))
      continue;

    name_len = strlen(s->clients[i]->name);
    
    if(!strncmp(s->clients[i]->name, conn->path, name_len) &&
       ((conn->path[name_len] == '\0') ||
        (conn->path[name_len] == '/')))
      {
      dev = s->clients[i];
      break;
      }
    }
  if(!dev && !strchr(conn->path, '/'))
    {
    char id[BG_REMOTE_DEVICE_ID_LEN+1];
    int idx = 0;
    bg_remote_device_info_t * info = NULL;
    
    infos = bg_remote_device_registry_get(s->remote_reg);
    
    /* Create device instance */

    while(infos[idx].protocol)
      {
      bg_remote_device_info_get_id(&infos[idx], id);
      if(!strcmp(conn->path, id))
        {
        info = &infos[idx];
        break;
        }
      idx++;
      }

    if(!info)
      goto fail; // 404

    gavl_log(GAVL_LOG_INFO, LOG_DOMAIN,
           "Creating device proxy %s [%s]", info->url, info->protocol);

    if(!(priv = device_proxy_create(s, info)))
      goto fail;
    
    dev = client_create(s,
                        CLIENT_TYPE_DEVICEPROXY, -1, NULL, priv,
                        free_data, device_proxy_func);
    dev->name = malloc(BG_REMOTE_DEVICE_ID_LEN+1);
    bg_remote_device_info_get_id(info, dev->name);
    server_attach_client(s, dev);
    }

  if(!dev)
    goto fail; // 404

  priv = dev->data;
  
  /* Check for websocket call */
  if(!(pos = strchr(conn->path, '/')))
    {
    result = bg_websocket_context_handle_request(conn, priv->ws);
    }
  else
    {
    /* Else it's handled by the backend */
    conn->path = pos;
    result = bg_remote_device_handle_handle(conn, priv->h);
    }
  
  fail:

  if(infos)
    bg_remote_device_info_destroy_array(infos);
  
  return result;
  }

