/* Copyright (C) 2002-2005 RealVNC Ltd.  All Rights Reserved.
 *
 * This 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 software 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 software; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 * USA.
 */
#include <stdio.h>
#include <stdint.h>

#define NEED_REPLIES
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#define _VNCEXT_PROTO_
#include "vncExt.h"

static Bool extensionInited = False;
static XExtCodes* codes = 0;

static Bool checkExtension(Display* dpy)
{
  if (!extensionInited) {
    extensionInited = True;
    codes = XInitExtension(dpy, VNCEXTNAME);
    if (!codes) return False;
  }
  return codes != 0;
}

Bool XVncExtQueryExtension(Display* dpy, int* event_basep, int* error_basep)
{
  if (!checkExtension(dpy)) return False;
  *event_basep = codes->first_event;
  *error_basep = codes->first_error;
  return True;
}

Bool XVncExtSetParam(Display* dpy, const char* param)
{
  xVncExtSetParamReq* req;
  xVncExtSetParamReply rep;

  int paramLen = strlen(param);
  if (paramLen > 255) return False;
  if (!checkExtension(dpy)) return False;

  LockDisplay(dpy);
  GetReq(VncExtSetParam, req);
  req->reqType = codes->major_opcode;
  req->vncExtReqType = X_VncExtSetParam;
  req->length += (paramLen + 3) >> 2;
  req->paramLen = paramLen;
  Data(dpy, param, paramLen);
  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
    UnlockDisplay(dpy);
    SyncHandle();
    return False;
  }
  UnlockDisplay(dpy);
  SyncHandle();
  return rep.success;
}

Bool XVncExtGetParam(Display* dpy, const char* param, char** value, int* len)
{
  xVncExtGetParamReq* req;
  xVncExtGetParamReply rep;

  int paramLen = strlen(param);
  *value = 0;
  *len = 0;
  if (paramLen > 255) return False;
  if (!checkExtension(dpy)) return False;

  LockDisplay(dpy);
  GetReq(VncExtGetParam, req);
  req->reqType = codes->major_opcode;
  req->vncExtReqType = X_VncExtGetParam;
  req->length += (paramLen + 3) >> 2;
  req->paramLen = paramLen;
  Data(dpy, param, paramLen);
  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
    UnlockDisplay(dpy);
    SyncHandle();
    return False;
  }
  if (rep.success) {
    *len = rep.valueLen;
    *value = (char*) Xmalloc (*len+1);
    if (!*value) {
      _XEatData(dpy, (*len+1)&~1);
      return False;
    }
    _XReadPad(dpy, *value, *len);
    (*value)[*len] = 0;
  }
  UnlockDisplay(dpy);
  SyncHandle();
  return rep.success;
}

char* XVncExtGetParamDesc(Display* dpy, const char* param)
{
  xVncExtGetParamDescReq* req;
  xVncExtGetParamDescReply rep;
  char* desc = 0;

  int paramLen = strlen(param);
  if (paramLen > 255) return False;
  if (!checkExtension(dpy)) return False;

  LockDisplay(dpy);
  GetReq(VncExtGetParamDesc, req);
  req->reqType = codes->major_opcode;
  req->vncExtReqType = X_VncExtGetParamDesc;
  req->length += (paramLen + 3) >> 2;
  req->paramLen = paramLen;
  Data(dpy, param, paramLen);
  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
    UnlockDisplay(dpy);
    SyncHandle();
    return False;
  }
  if (rep.success) {
    desc = (char*)Xmalloc(rep.descLen+1);
    if (!desc) {
      _XEatData(dpy, (rep.descLen+1)&~1);
      return False;
    }
    _XReadPad(dpy, desc, rep.descLen);
    desc[rep.descLen] = 0;
  }
  UnlockDisplay(dpy);
  SyncHandle();
  return desc;
}

char** XVncExtListParams(Display* dpy, int* nParams)
{
  xVncExtListParamsReq* req;
  xVncExtListParamsReply rep;
  char** list = 0;
  char* ch;
  int rlen, paramLen, i;

  if (!checkExtension(dpy)) return False;

  LockDisplay(dpy);
  GetReq(VncExtListParams, req);
  req->reqType = codes->major_opcode;
  req->vncExtReqType = X_VncExtListParams;
  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
    UnlockDisplay(dpy);
    SyncHandle();
    return False;
  }
  UnlockDisplay(dpy);
  SyncHandle();
  if (rep.nParams) {
    list = (char**)Xmalloc(rep.nParams * sizeof(char*));
    rlen = rep.length << 2;
    ch = (char*)Xmalloc(rlen + 1);
    if (!list || !ch) {
      if (list) Xfree((char*)list);
      if (ch) Xfree(ch);
      _XEatData(dpy, rlen);
      UnlockDisplay(dpy);
      SyncHandle();
      return 0;
    }
    _XReadPad(dpy, ch, rlen);
    paramLen = *ch++;
    for (i = 0; i < rep.nParams; i++) {
      list[i] = ch;
      ch += paramLen;
      paramLen = *ch;
      *ch++ = 0;
    }
  }
  *nParams = rep.nParams;
  UnlockDisplay(dpy);
  SyncHandle();
  return list;
}

void XVncExtFreeParamList(char** list)
{
  if (list) {
    Xfree(list[0]-1);
    Xfree((char*)list);
  }
}

Bool XVncExtConnect(Display* dpy, const char* hostAndPort)
{
  xVncExtConnectReq* req;
  xVncExtConnectReply rep;

  int strLen = strlen(hostAndPort);
  if (strLen > 255) return False;
  if (!checkExtension(dpy)) return False;

  LockDisplay(dpy);
  GetReq(VncExtConnect, req);
  req->reqType = codes->major_opcode;
  req->vncExtReqType = X_VncExtConnect;
  req->length += (strLen + 3) >> 2;
  req->strLen = strLen;
  Data(dpy, hostAndPort, strLen);
  if (!_XReply(dpy, (xReply *)&rep, 0, xFalse)) {
    UnlockDisplay(dpy);
    SyncHandle();
    return False;
  }
  UnlockDisplay(dpy);
  SyncHandle();
  return rep.success;
}
