#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>

typedef enum
{
	MIPL_DEBUG_LEVEL_NONE = 0,
	MIPL_DEBUG_LEVEL_RXL,
	MIPL_DEBUG_LEVEL_RXL_RXD,
	MIPL_DEBUG_LEVEL_TXL_TXD,
} MIPL_DEBUG_LEVEL_E;

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned uint32;

typedef struct
{
	uint8 boot;
	uint8 encrypt;
	MIPL_DEBUG_LEVEL_E debug;
	uint16 port;
	uint32 keep_alive;
	size_t uri_len;
	char* uri;
	size_t ep_len;
	char* ep;
	uint8 block1;	//COAP option BLOCK1(PUT or POST),0-6. 2^(4+n)  bytes
	uint8 block2;	//COAP option BLOCK2(GET),0-6. 2^(4+n)  bytes
	uint8 block2th;	//max size to trigger block-wise operation,0-2. 2^(8+n) bytes
} MIPL_T;

void usage( const char *name )
{
	printf( "Usage: %s [OPTION]\r\n", name );
	printf( "Launch a client.\r\n" );
	printf( "version: v1.2\r\n" );
	printf( "Options:\r\n" );
	printf( "-b BOOT\t\tSet the bootstrap mode of the client.Default: 0\r\n" );
	printf( "-d DEBUG\tSet the debug mode of the client.Default: 0\r\n" );
	printf( "-e ENCRYPT\tSet the encrypt of the client.\r\n" );
	printf( "-i URI\t\tSet the coap uri of the server to connect to. For example: coap://localhost:5683\r\n" );
	printf( "-n NAME\t\tSet the endpoint name[imei;imsi] of the client.\r\n" );
	printf( "-p PORT\t\tSet the local port of the client to bind to. Default: srand\r\n" );
	printf( "-t TIME\t\tSet the lifetime of the client. Default: 300\r\n" );
	printf( "-u BLOCK1\tSet COAP option BLOCK1(PUT or POST),0-6. Default 5(512B),2^(4+n)\r\n");
	printf( "-g BLOCK2\tSet COAP option BLOCK2(GET),0-6. Default 5(512B),2^(4+n)\r\n");
	printf( "-x BLOCK2TH\tSet max size to trigger block-wise operation,0-2. Default 2(1024B),2^(8+n)\r\n");
	printf( "\r\n" );
}

void output_buffer(unsigned char *buffer, int length,int index, int flag)
{
	int i = 0;
	while ( i < length )
	{
		printf( "%02X", buffer[i++] );
	}
	printf(",%d,%d\r\n",index,flag);
}
/*****************************************************************
       0               1               2               3 
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| boot | encrypt|     debug     |          local_port         |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|                          life_time                          |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|           host_len            |             host            |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|          ep_name_len          |      ep_name(imei;imsi)     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  coap_block1  |  coap_block2  |    block2th   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*****************************************************************/
void mipl_generate(char *buf, size_t buflen, MIPL_T *mipl)
{
	uint32 offset = 0;
	int mipl_num = 0;
	memset(buf, 0, buflen);

	char mode = ((mipl->boot & 0x1) << 4) | (mipl->encrypt & 0x1);
	memcpy(buf + offset, &mode, 1);
	offset++;
	memcpy(buf + offset, &mipl->debug, 1);
	offset++;
	memcpy(buf + offset, &mipl->port, 2);
	offset += 2;
	memcpy(buf+offset, &mipl->keep_alive, 4);
	offset += 4;

	memcpy(buf + offset, &mipl->uri_len, 2);
	offset += 2;
	memcpy(buf + offset, mipl->uri, mipl->uri_len);
	offset += mipl->uri_len;
	memcpy(buf + offset, &mipl->ep_len, 2);
	offset += 2;
	memcpy(buf + offset, mipl->ep, mipl->ep_len);
	offset += mipl->ep_len;

	*(buf + offset) = mipl->block1;
	offset++;
	*(buf + offset) = mipl->block2;
	offset++;
	*(buf + offset) = mipl->block2th;
	offset++;
	
	mipl_num = 0;
	while(offset > 1024)//每条+MIPLCONF命令最多输入1024字节配置数据
	{
		printf("AT+MIPLCONF=%d,",1024);
		output_buffer( (unsigned char *)(buf + (mipl_num >> 10)),1024,mipl_num + 1,0);//index从1起始
		mipl_num++;
		offset -= 1024;
	}
	printf("AT+MIPLCONF=%d,",offset);
	output_buffer( (unsigned char *)(buf + (mipl_num >> 10)),offset,mipl_num + 1,1);//结束
}

int main(int argc, char *argv[])
{
	char conf[1024];
	MIPL_T mipl = 
	{
		.boot = 0,
		.encrypt = 0,
		.debug = 0,
		.port = 0,
		.keep_alive  = 300,
		.uri_len = 0,
		.uri = "",
		.ep_len = 0,
		.ep = "",
		.block1 = 5,	//COAP option BLOCK1(PUT or POST),0-6. 2^(4+n)  bytes
		.block2 = 5,	//COAP option BLOCK2(GET),0-6. 2^(4+n)  bytes
		.block2th = 2,
	};
	
	char* const short_options = "i:p:t:n:d:b:e:hu:g:x:";
	char c;
	struct option long_options[] =
	{
		{"uri", required_argument, NULL, 'i'},
		{"port", required_argument, NULL, 'p'},
		{"keep_alive", required_argument, NULL, 't'},
		{"ep", required_argument, NULL, 'n'},
		{"debug", required_argument, NULL, 'd'},
		{"boot", required_argument, NULL, 'b'},
		{"encrypt", required_argument, NULL, 'e'},
		{"block1",optional_argument,NULL,'u'},
		{"block2",optional_argument,NULL,'g'},
		{"block2th",optional_argument,NULL,'x'},
		{"help", 0, NULL, 'h'},
		{0, 0, 0, 0},
	};

	while((c = getopt_long (argc, argv, short_options, long_options, NULL)) != -1)
	{
		switch (c)
		{
		case 'u':
		{
			mipl.block1 = atoi(optarg); //0-6
			if(mipl.block1 > 6)
			{
				printf("BLOCKSIZE exceed.");
				return -1;
			}
		}
		break;
		case 'g':
		{
			mipl.block2 = atoi(optarg);//0-6
			if(mipl.block2 > 6)
			{
				printf("BLOCKSIZE exceed.");
				return -1;
			}
		}
		break;
		case 'x':
		{
			mipl.block2th = atoi(optarg);//0-2
			if(mipl.block2th > 2)
			{
				printf("BLOCKSIZE exceed.");
				return -1;
			}
		}
		break;
		case 'i':
		{
			mipl.uri = optarg;
			mipl.uri_len = strlen(optarg);
		}
		break;

		case 'p':
		{
			mipl.port = atoi(optarg);
		}
		break;

		case 't':
		{
			mipl.keep_alive = atoi(optarg);
		}
		break;

		case 'n':
		{
			mipl.ep = optarg;
			mipl.ep_len = strlen(optarg);
		}
		break;

		case 'd':
		{
			mipl.debug = atoi(optarg);
		}
		break;

		case 'b':
		{
			mipl.boot = atoi(optarg);
			if(mipl.boot > 1)
			{
				printf("BOOTMODE exceed.");
				return -1;
			}
		}
		break;

		case 'e':
		{
			mipl.encrypt = atoi(optarg);
			if(mipl.encrypt > 1)
			{
				printf("ENCRYPTMODE exceed.");
				return -1;
			}
		}
		break;
		case 'h':
			usage(argv[0]);
			return 0;
		default:
			printf("unsupported option.\r\n");
			return 0;
		}
	}

	mipl_generate(conf,sizeof(conf),&mipl);
	return 0;
}
