/*
 * Copyright (c) 2001, Adam Dunkels.
 * All rights reserved. 
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met: 
 * 1. Redistributions of source code must retain the above copyright 
 *    notice, this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright 
 *    notice, this list of conditions and the following disclaimer in the 
 *    documentation and/or other materials provided with the distribution. 
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior
 *    written permission.  
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.  
 *
 * This file is part of the uIP TCP/IP stack.
 *
 * $Id: vnc-server.c,v 1.3 2010/10/19 18:29:04 adamdunkels Exp $
 *
 */

/* A micro implementation of a VNC server. VNC is a protocol for
   remote network displays. See http://www.uk.research.att.com/vnc/
   for information about VNC.

   Initialization states:

   VNC_VERSION (send version string)
   VNC_AUTH    (send auth message)
   VNC_INIT    (send init message)

   Steady state:
   
   VNC_RUNNING (send RFB updates, parse incoming messages)

   What kind of message should be sent:

   SEND_NONE   (No message)
   SEND_BLANK  (Blank screen initially)
   SEND_SCREEN (Send entire screen, initially)
   SEND_UPDATE (Send incremental update)

*/

#include "contiki-net.h"
#include "ctk/vnc-server.h"
#include "ctk/vnc-out.h"

#include <string.h>

/* RFB server initial handshaking string. */
#define RFB_SERVER_VERSION_STRING rfb_server_version_string

/* "RFB 003.003" */
static u8_t rfb_server_version_string[12] = {82,70,66,32,48,48,51,46,48,48,51,10};

/* uVNC */
static u8_t uvnc_name[4] = {117,86,78,67};
#if 1
#define PRINTF(x)
#else
#define PRINTF(x) printf x
#endif

/*-----------------------------------------------------------------------------------*/
u8_t
vnc_server_draw_rect(u8_t *ptr, u16_t x, u16_t y, u16_t w, u16_t h, u8_t c)
{
  register struct rfb_fb_update_rect_hdr *recthdr;
  struct rfb_rre_hdr *rrehdr;

  recthdr = (struct rfb_fb_update_rect_hdr *)ptr;
  rrehdr = (struct rfb_rre_hdr *)(ptr + sizeof(struct rfb_fb_update_rect_hdr));
  
  recthdr->rect.x = x;
  recthdr->rect.y = y;
  recthdr->rect.w = w;
  recthdr->rect.h = h; 
  recthdr->encoding[0] =
    recthdr->encoding[1] =
    recthdr->encoding[2] = 0;
  recthdr->encoding[3] = RFB_ENC_RRE;
      
  rrehdr->subrects[0] =
    rrehdr->subrects[1] = 0;
  rrehdr->bgpixel = c;
      
  return sizeof(struct rfb_fb_update_rect_hdr) + sizeof(struct rfb_rre_hdr);
}
/*-----------------------------------------------------------------------------------*/
void
vnc_server_init(void)
{
  vnc_out_init();
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_send_blank(struct vnc_server_state *vs)
{
  switch(vs->type) {
  case 0:	
    vnc_out_send_blank(vs);
    break;
    /*  case 1:
    vnc_stats_send_blank(vs);
    break;   */
  }
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_send_screen(struct vnc_server_state *vs)
{
  switch(vs->type) {
  case 0:	
    vnc_out_send_screen(vs);
    break;
    /*  case 1:
    vnc_stats_send_screen(vs);
    break;*/
  }
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_send_update(struct vnc_server_state *vs)
{
  switch(vs->type) {
  case 0:	
    vnc_out_send_update(vs);
    break;
    /*  case 1:
    vnc_stats_send_update(vs);
    break;*/
  }
}
/*-----------------------------------------------------------------------------------*/
void
vnc_server_send_data(struct vnc_server_state *vs)
{
  register struct rfb_server_init *initmsg;
  
  switch(vs->state) {
  case VNC_VERSION:
    uip_send(RFB_SERVER_VERSION_STRING, sizeof(RFB_SERVER_VERSION_STRING));
    break;
  case VNC_AUTH:
    ((char *)uip_appdata)[0] = 0;
    ((char *)uip_appdata)[1] = 0;
    ((char *)uip_appdata)[2] = 0;
    ((char *)uip_appdata)[3] = RFB_AUTH_NONE;
    uip_send(uip_appdata, 4);
    break;
  case VNC_INIT:
    initmsg = (struct rfb_server_init *)uip_appdata;
    initmsg->width = uip_htons(vs->width);
    initmsg->height = uip_htons(vs->height);
    /* BGR233 pixel format. */
    initmsg->format.bps = 8;
    initmsg->format.depth = 8;
    initmsg->format.endian = 1;
    initmsg->format.truecolor = 1;
    initmsg->format.red_max = uip_htons(7);
    initmsg->format.green_max = uip_htons(7);
    initmsg->format.blue_max = uip_htons(3);
    initmsg->format.red_shift = 0;
    initmsg->format.green_shift = 3;
    initmsg->format.blue_shift = 6;
    initmsg->namelength[0] = 0;
    initmsg->namelength[1] = 0;
    initmsg->namelength[2] = 0;	    
    initmsg->namelength[3] = 4;
    memcpy(&((char *)uip_appdata)[sizeof(struct rfb_server_init)], uvnc_name, 4);
    /*    ((char *)uip_appdata)[sizeof(struct rfb_server_init)+0] = 'u';
    ((char *)uip_appdata)[sizeof(struct rfb_server_init)+1] = 'V';
    ((char *)uip_appdata)[sizeof(struct rfb_server_init)+2] = 'N';
    ((char *)uip_appdata)[sizeof(struct rfb_server_init)+3] = 'C';*/
    uip_send(uip_appdata, sizeof(struct rfb_server_init) + 4);
    break;
  case VNC_RUNNING:
    switch(vs->sendmsg) {
    case SEND_NONE:
      PRINTF(("Sending none\n"));
      break;
      
    case SEND_BLANK:
    case SENT_BLANK:
      PRINTF(("Sending blank\n"));
      vnc_send_blank(vs);
      break;
      
    case SEND_SCREEN:
      PRINTF(("Sending screen\n"));
      vnc_send_screen(vs);
      break;

    case SEND_UPDATE:
      PRINTF(("Sending update\n"));
      vnc_send_update(vs);
      break;
    }
    break;
    
  default:
    break;
  }

}
/*-----------------------------------------------------------------------------------*/
static void
vnc_key_event(struct vnc_server_state *vs)
{
  switch(vs->type) {
  case 0:	
    vnc_out_key_event(vs);
    break;
    /*  case 1:
    vnc_stats_key_event(vs);
    break;*/
  }
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_pointer_event(struct vnc_server_state *vs)
{
  switch(vs->type) {
  case 0:	
    vnc_out_pointer_event(vs);
    break;
    /*  case 1:
    vnc_stats_pointer_event(vs);
    break;*/
  }
}
/*-----------------------------------------------------------------------------------*/
static u8_t
vnc_read_data(CC_REGISTER_ARG struct vnc_server_state *vs)
{
  u8_t *appdata;
  u16_t len;
  struct rfb_fb_update_request *req;
  /*  u8_t niter;*/
  
  len = uip_datalen();
  appdata = (u8_t *)uip_appdata;
  
  /* First, check if there is data left to discard since last read. */
  if(vs->readlen > 0) {
    appdata += vs->readlen;
    if(len > vs->readlen) {
      len -= vs->readlen;
      vs->readlen = 0;
    } else {
      vs->readlen -= len;
      len = 0;
    }
  }

  if(vs->readlen != 0) {
    return 1;
  }

  /* All data read and ignored, parse next message. */
  /*  for(niter = 32; niter > 0 && len > 0; --niter) {*/
  while(len > 0) {
    switch(vs->state) {
    case VNC_VERSION:
    case VNC_VERSION2:
      PRINTF(("Read in version\n"));
      /* Receive and ignore client version string (12 bytes). */
      vs->state = VNC_AUTH;
      vs->readlen = 12;
      break;
      
    case VNC_AUTH:
    case VNC_AUTH2:
      PRINTF(("Read in auth \n"));
      /* Read and discard initialization from client (1 byte). */
      vs->readlen = 1;
      vs->state = VNC_INIT;
      break;
      
    case VNC_INIT:
    case VNC_INIT2:
      PRINTF(("Read in init \n"));
      vs->readlen = 0;
      vs->state = VNC_RUNNING;
      
    case VNC_RUNNING:
      /* Handle all client events. */
      switch(*appdata) {
      case RFB_SET_PIXEL_FORMAT:
	PRINTF(("Set pixel format\n"));
	vs->readlen = sizeof(struct rfb_set_pixel_format);
	/* Check if client runs with BGR233 format. If not, abort the
	   connection. */
	/* XXX: not implemented yet. */
	break;
	
      case RFB_FIX_COLORMAP_ENTRIES:
	PRINTF(("Fix colormap entries\n"));
	return 0;
	
      case RFB_SET_ENCODINGS:
	PRINTF(("Set encodings\n"));
	vs->readlen = sizeof(struct rfb_set_encoding);
	vs->readlen += uip_htons(((struct rfb_set_encoding *)appdata)->encodings) * 4;
	/* Make sure that client supports the encodings we use. */
	/* XXX: not implemented yet. */
	break;
	
      case RFB_FB_UPDATE_REQ:
	PRINTF(("Update request\n"));
	vs->update_requested = 1;
	vs->readlen = sizeof(struct rfb_fb_update_request);
	/* blank the screen initially */
	req = (struct rfb_fb_update_request *)appdata;
	if(req->incremental == 0) {
	  /*	  vs->sendmsg = SEND_BLANK;*/
	  vnc_out_update_area(vs, 0, 0, vs->w, vs->h);
	}
	break;
	
      case RFB_KEY_EVENT:
	vs->readlen = sizeof(struct rfb_key_event);
	vnc_key_event(vs);
	break;
	
      case RFB_POINTER_EVENT:
	vs->readlen = sizeof(struct rfb_pointer_event);
	vnc_pointer_event(vs);
	break;
	
      case RFB_CLIENT_CUT_TEXT:
	PRINTF(("Client cut text\n"));

	if(((struct rfb_client_cut_text *)appdata)->len[0] != 0 ||
	   ((struct rfb_client_cut_text *)appdata)->len[1] != 0) {
	  return 0;
	  
	}
	vs->readlen = sizeof(struct rfb_client_cut_text) +
	  (((struct rfb_client_cut_text *)appdata)->len[2] << 8) +
	  ((struct rfb_client_cut_text *)appdata)->len[3];
	/*	return 0;*/
	break;
	
      default:
	PRINTF(("Unknown message %d\n", *appdata));
	return 0;
      }
      break;
      
    default:
      return 0;
    }

    if(vs->readlen > 0) {
      if(len > vs->readlen) {
	len -= vs->readlen;
	appdata += vs->readlen;
	vs->readlen = 0;
      } else {
	vs->readlen -= len;
	len = 0;
      }
    } else {
      /* Lost data. */
      break;
    }
    
  }

  /*  if(vs->readlen > 0) {
    printf("More data %d\n", vs->readlen);
    }*/
  
  /*  uip_appdata = appdata;*/

  return 1;
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_new(CC_REGISTER_ARG struct vnc_server_state *vs)
{
  vs->counter = 0;
  vs->readlen = 0;
  vs->sendmsg = SEND_NONE;
  vs->update_requested = 1;
  switch(vs->type) {
  case 0:	
    vnc_out_new(vs);
    break;
    /*  case 1:
    vnc_stats_new(vs);
    break;*/
  }
}
/*-----------------------------------------------------------------------------------*/
static void
vnc_acked(CC_REGISTER_ARG struct vnc_server_state *vs)
{
  switch(vs->state) {
  case VNC_VERSION:
    vs->state = VNC_VERSION2;
    break;
    
  case VNC_AUTH:
    vs->state = VNC_AUTH2;
    break;
    
  case VNC_INIT:
    vs->state = VNC_INIT2;
    break;

  case VNC_RUNNING:
    switch(vs->type) {
    case 0:	
      vnc_out_acked(vs);
      break;
      /*    case 1:
      vnc_stats_acked(vs);
      break;*/
    }
    break;
  }
}
/*-----------------------------------------------------------------------------------*/
void
vnc_server_appcall(struct vnc_server_state *vs)
{
  
  vs->type = uip_htons(uip_conn->lport) - 5900;
  
  if(uip_connected()) {      
    vnc_new(vs);
    vs->state = VNC_VERSION;
    vnc_server_send_data(vs);
    return;
  }
  if(uip_acked()) {
    PRINTF(("Acked\n"));
    vnc_acked(vs);
  }
  
  if(uip_newdata()) {
    PRINTF(("Newdata\n"));
    vs->counter = 0;
    if(vnc_read_data(vs) == 0) {
      uip_abort();
      return;
    }
  }
  
  if(uip_rexmit()) {
    PRINTF(("Rexmit\n"));
  }
  
  
  if(uip_newdata() ||
     uip_rexmit() ||
     uip_acked()) {
    vnc_server_send_data(vs);
  } else if(uip_poll()) {
    ++vs->counter;
    /* Abort connection after about 20 seconds of inactivity. */
    if(vs->counter >= 40) {
      uip_abort();
      return;
    }
    
    vnc_out_poll(vs);
  }
  
}
/*-----------------------------------------------------------------------------------*/
