#include <stdio.h>
#include <signal.h>

#include "ithread.h"
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "upnp.h"
#include <stdlib.h>
#include "upnptools.h"
#include "ixml.h"

#define DEFAULT_WEB_DIR "./web"

#define DEVICE_DESC_XML "devicedesc.xml"

#define DEFAULT_ADVR_EXPIRE  100

#define MAX_VAL_LEN 5

#ifndef NAME_SIZE
#define NAME_SIZE 256
#endif

#ifndef URL_SIZE
#define URL_SIZE 512
#endif

static int
DeviceSetServiceTableVar( unsigned int service,
                          unsigned int variable,
                          const unsigned char* value );

static unsigned char *
SampleUtil_GetFirstDocumentItem( IXML_Document * doc,
                                 const unsigned char *item );



#define SERVICE_TVCONTROL 0

#define TVCONTROL_VARCOUNT 3

#define TVCONTROL_CHANNEL 0

#define TVCONTROL_CHANNEL_MIN 1

#define TVCONTROL_CHANNEL_MAX 100

#define TVCONTROL_CHANNEL_STEP 1

#define TVCONTROL_VOLUME 1

#define TVCONTROL_VOLUME_MIN 0

#define TVCONTROL_VOLUME_MAX 10

#define TVCONTROL_VOLUME_STEP 1

#define TVCONTROL_POWER 2

#define SERVICE_TVPICTURE 1

#define TVPICTURE_VARCOUNT 4

#define TVPICTURE_COLOR 0

#define TVPICTURE_COLOR_MIN 1

#define TVPICTURE_COLOR_MAX 10

#define TVPICTURE_COLOR_STEP 1

#define TVPICTURE_TINT 1

#define TVPICTURE_TINT_MIN 1

#define TVPICTURE_TINT_MAX 10

#define TVPICTURE_TINT_STEP 1

#define TVPICTURE_CONTRAST 2

#define TVPICTURE_CONTRAST_MIN 1

#define TVPICTURE_CONTRAST_MAX 10

#define TVPICTURE_CONTRAST_STEP 1

#define TVPICTURE_BRIGHTNESS 3

#define TVPICTURE_BRIGHTNESS_MIN 1

#define TVPICTURE_BRIGHTNESS_MAX 10

#define TVPICTURE_BRIGHTNESS_STEP 1

#define DEVICE_UDN "uuid:Upnp-TVEmulator-1_0-1234567890002"

#define MAX_VARS 4

#define MAX_ACTIONS 12

typedef int (*upnp_action) (IXML_Document *request, IXML_Document **out, 
 			    char **errorString);


//typedef struct {
//    unsigned char name[NAME_SIZE];
//    unsigned char type[NAME_SIZE];
//    int int_value;
    /*  unsigned char*  str_value */
//} state_variable;


typedef struct {
    unsigned char* name;
    upnp_action action;
}action;


typedef struct  {
    unsigned char serviceType[NAME_SIZE];
    unsigned char serviceId[NAME_SIZE];
    unsigned char** variableName;
    unsigned char variableStrValue[MAX_VARS][MAX_VAL_LEN];
    unsigned int  variableCount;
    action actions[MAX_ACTIONS];

} Service;


#define SERVICE_COUNT 2

static Service serviceTable[SERVICE_COUNT];

static unsigned char* ServiceType[]  = {"urn:schemas-upnp-org:service:tvcontrol:1", "urn:schemas-upnp-org:service:tvpicture:1", };

static unsigned char* tvcontrol_varname[] = {"Channel", "Volume", "Power", };
static unsigned char* tvcontrol_varval_def[] = {"1", "5", "0", };

static unsigned char* tvpicture_varname[] = {"Color", "Tint", "Contrast", "Brightness", };
static unsigned char* tvpicture_varval_def[] = {"5", "5", "5", "5", };


static UpnpDevice_Handle deviceHandle = -1;

static ithread_mutex_t DeviceMutex;
static int
tvcontrol_Set_Channel
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 1 || intValue > 100){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVCONTROL,
				    TVCONTROL_CHANNEL, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvcontrol_Set_Volume
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 0 || intValue > 10){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVCONTROL,
				    TVCONTROL_VOLUME, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvcontrol_Set_Power
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue != 0 && intValue != 1){
      return 0;
    }else{
        return DeviceSetServiceTableVar(SERVICE_TVCONTROL,
				    TVCONTROL_POWER, 
                                    newValue);
    }

    return UPNP_E_SUCCESS;
}

static int
tvpicture_Set_Color
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 1 || intValue > 10){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVPICTURE,
				    TVPICTURE_COLOR, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvpicture_Set_Tint
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 1 || intValue > 10){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVPICTURE,
				    TVPICTURE_TINT, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvpicture_Set_Contrast
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 1 || intValue > 10){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVPICTURE,
				    TVPICTURE_CONTRAST, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvpicture_Set_Brightness
(
  const unsigned char* newValue
) {
    int intValue = strtol(newValue, (char **)NULL, 10);

    if (intValue < 1 || intValue > 10){
        return 0;
    }
    return DeviceSetServiceTableVar(SERVICE_TVPICTURE,
				    TVPICTURE_BRIGHTNESS, 
                                    newValue);


    return UPNP_E_SUCCESS;
}

static int
tvcontrol_PowerOn
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "PowerOn",
				    ServiceType[SERVICE_TVCONTROL],
				      "Power",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_PowerOff
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "PowerOff",
				    ServiceType[SERVICE_TVCONTROL],
				      "Power",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_SetChannel
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Channel;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Channel = SampleUtil_GetFirstDocumentItem( in, "Channel" ) ) ) {
         ( *errorString ) = "Invalid Channel";

        free(Channel);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetChannel",
				    ServiceType[SERVICE_TVCONTROL],
				      "NewChannel",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Channel);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Channel);
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_IncreaseChannel
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseChannel",
				    ServiceType[SERVICE_TVCONTROL],
				      "Channel",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_DecreaseChannel
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseChannel",
				    ServiceType[SERVICE_TVCONTROL],
				      "Channel",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_SetVolume
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Volume;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Volume = SampleUtil_GetFirstDocumentItem( in, "Volume" ) ) ) {
         ( *errorString ) = "Invalid Volume";

        free(Volume);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetVolume",
				    ServiceType[SERVICE_TVCONTROL],
				      "NewVolume",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Volume);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Volume);
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_IncreaseVolume
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseVolume",
				    ServiceType[SERVICE_TVCONTROL],
				      "Volume",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvcontrol_DecreaseVolume
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseVolume",
				    ServiceType[SERVICE_TVCONTROL],
				      "Volume",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_SetColor
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Color;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Color = SampleUtil_GetFirstDocumentItem( in, "Color" ) ) ) {
         ( *errorString ) = "Invalid Color";

        free(Color);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetColor",
				    ServiceType[SERVICE_TVPICTURE],
				      "NewColor",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Color);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Color);
    return UPNP_E_SUCCESS;
}
static int
tvpicture_IncreaseColor
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseColor",
				    ServiceType[SERVICE_TVPICTURE],
				      "Color",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_DecreaseColor
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseColor",
				    ServiceType[SERVICE_TVPICTURE],
				      "Color",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_SetTint
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Tint;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Tint = SampleUtil_GetFirstDocumentItem( in, "Tint" ) ) ) {
         ( *errorString ) = "Invalid Tint";

        free(Tint);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetTint",
				    ServiceType[SERVICE_TVPICTURE],
				      "NewTint",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Tint);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Tint);
    return UPNP_E_SUCCESS;
}
static int
tvpicture_IncreaseTint
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseTint",
				    ServiceType[SERVICE_TVPICTURE],
				      "Tint",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_DecreaseTint
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseTint",
				    ServiceType[SERVICE_TVPICTURE],
				      "Tint",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_SetContrast
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Contrast;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Contrast = SampleUtil_GetFirstDocumentItem( in, "Contrast" ) ) ) {
         ( *errorString ) = "Invalid Contrast";

        free(Contrast);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetContrast",
				    ServiceType[SERVICE_TVPICTURE],
				      "NewContrast",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Contrast);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Contrast);
    return UPNP_E_SUCCESS;
}
static int
tvpicture_IncreaseContrast
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseContrast",
				    ServiceType[SERVICE_TVPICTURE],
				      "Contrast",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_DecreaseContrast
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseContrast",
				    ServiceType[SERVICE_TVPICTURE],
				      "Contrast",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_SetBrightness
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    unsigned char* Brightness;
    ( *out ) = NULL;
    ( *errorString ) = NULL;

    if( !( Brightness = SampleUtil_GetFirstDocumentItem( in, "Brightness" ) ) ) {
         ( *errorString ) = "Invalid Brightness";

        free(Brightness);
        return UPNP_E_INVALID_PARAM;
    }
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "SetBrightness",
				    ServiceType[SERVICE_TVPICTURE],
				      "NewBrightness",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
        free(Brightness);
            return UPNP_E_INTERNAL_ERROR;
        }
        free(Brightness);
    return UPNP_E_SUCCESS;
}
static int
tvpicture_IncreaseBrightness
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "IncreaseBrightness",
				    ServiceType[SERVICE_TVPICTURE],
				      "Brightness",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
tvpicture_DecreaseBrightness
(   IXML_Document * in,
    IXML_Document ** out,
    char **errorString )
{
    ( *out ) = NULL;
    ( *errorString ) = NULL;
//TODO: 実際の処理を書く
//      エラーで抜ける際には freeを忘れないように




//TODO: UpnpAddToActionResponseの最後の引数として実際の返り値を入れること
        if( UpnpAddToActionResponse( out, "DecreaseBrightness",
				    ServiceType[SERVICE_TVPICTURE],
				      "Brightness",
                                     "0"  ) != UPNP_E_SUCCESS ) {
            ( *out ) = NULL;
            ( *errorString ) = "Internal Error";
            return UPNP_E_INTERNAL_ERROR;
        }
    return UPNP_E_SUCCESS;
}
static int
InitializeService(Service* service, 
                  const unsigned char* serviceType,
		  const unsigned char* serviceId,
		  unsigned int count,
		  unsigned char** varname,
		  unsigned char** varval_def)
		  
{
    int i;  

    strcpy( service->serviceType, serviceType );
    strcpy( service->serviceId, serviceId );
    service->variableCount = count;
    service->variableName = varname;
    for(i = 0; i < count ; ++i)
      strcpy(service->variableStrValue[i], varval_def[i]);
    return UPNP_E_SUCCESS;
}
static int
InitializeServices(void)
{
    action* actions;
    InitializeService(&serviceTable[SERVICE_TVCONTROL],
    "urn:schemas-upnp-org:service:tvcontrol:1", "urn:upnp-org:serviceId:tvcontrol1",
    TVCONTROL_VARCOUNT,
    tvcontrol_varname,
    tvcontrol_varval_def 
    );

    actions = serviceTable[SERVICE_TVCONTROL].actions;

    actions[0].name = "PowerOn";
    actions[0].action = tvcontrol_PowerOn;
    actions[1].name = "PowerOff";
    actions[1].action = tvcontrol_PowerOff;
    actions[2].name = "SetChannel";
    actions[2].action = tvcontrol_SetChannel;
    actions[3].name = "IncreaseChannel";
    actions[3].action = tvcontrol_IncreaseChannel;
    actions[4].name = "DecreaseChannel";
    actions[4].action = tvcontrol_DecreaseChannel;
    actions[5].name = "SetVolume";
    actions[5].action = tvcontrol_SetVolume;
    actions[6].name = "IncreaseVolume";
    actions[6].action = tvcontrol_IncreaseVolume;
    actions[7].name = "DecreaseVolume";
    actions[7].action = tvcontrol_DecreaseVolume;
    actions[8].name = NULL;

    InitializeService(&serviceTable[SERVICE_TVPICTURE],
    "urn:schemas-upnp-org:service:tvpicture:1", "urn:upnp-org:serviceId:tvpicture1",
    TVPICTURE_VARCOUNT,
    tvpicture_varname,
    tvpicture_varval_def 
    );

    actions = serviceTable[SERVICE_TVPICTURE].actions;

    actions[0].name = "SetColor";
    actions[0].action = tvpicture_SetColor;
    actions[1].name = "IncreaseColor";
    actions[1].action = tvpicture_IncreaseColor;
    actions[2].name = "DecreaseColor";
    actions[2].action = tvpicture_DecreaseColor;
    actions[3].name = "SetTint";
    actions[3].action = tvpicture_SetTint;
    actions[4].name = "IncreaseTint";
    actions[4].action = tvpicture_IncreaseTint;
    actions[5].name = "DecreaseTint";
    actions[5].action = tvpicture_DecreaseTint;
    actions[6].name = "SetContrast";
    actions[6].action = tvpicture_SetContrast;
    actions[7].name = "IncreaseContrast";
    actions[7].action = tvpicture_IncreaseContrast;
    actions[8].name = "DecreaseContrast";
    actions[8].action = tvpicture_DecreaseContrast;
    actions[9].name = "SetBrightness";
    actions[9].action = tvpicture_SetBrightness;
    actions[10].name = "IncreaseBrightness";
    actions[10].action = tvpicture_IncreaseBrightness;
    actions[11].name = "DecreaseBrightness";
    actions[11].action = tvpicture_DecreaseBrightness;

	return UPNP_E_SUCCESS;
}
