/*!
 * web-server.cpp
 *
 * Copyright (c) 2015-2019, NADAL Jean-Baptiste. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301 USA
 * 
 * @Author:  NADAL Jean-Baptiste
 * @Date: 13/11/2019
 *
 */

// This is an independent project of an individual developer. Dear PVS-Studio, please check it.
// PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com

/*------------------------------- INCLUDES ----------------------------------*/

#include <string.h>

#include "web-server.h"

//404前回调(未找到页面/文件时回调,此功能便于程序返回自定义功能)；返回0表示没有适合的处理请求，需要发送404错误
char on_request(void* data, uv_stream_t* client, tw_peerAddr* pa, tw_reqHeads* heads)
{
//	struct sockaddr_in serveraddr, peeraddr;
//	char serv_ip[17],peer_ip[17], tmp[1024];
//	int addrlen = sizeof(struct sockaddr);
//	int r;
//
//	//获取clientAddr: http://www.codes51.com/article/detail_113112.html
//	//本地接入地址
//	r = uv_tcp_getsockname((uv_tcp_t*)client, (struct sockaddr*)&serveraddr, &addrlen);
//	//网络字节序转换成主机字符序
//	uv_ip4_name(&serveraddr, (char*)serv_ip, sizeof(serv_ip));
//	//客户端的地址
//	r = uv_tcp_getpeername((uv_tcp_t*)client, (struct sockaddr*)&peeraddr, &addrlen);
//	//网络字节序转换成主机字符序
//	uv_ip4_name(&peeraddr, (char*)peer_ip, sizeof(peer_ip));
//
//	sprintf(tmp, "<h1>Page not found:</h1><url>%s<br>%s<br></url><br><br><br><i>server：%s:%d\t\tpeer：%s:%d</i>\n", heads->path, (heads->query?heads->query:""), serv_ip, ntohs(serveraddr.sin_port), peer_ip, ntohs(peeraddr.sin_port));
//#ifdef _MSC_VER //Windows下需要转换编码
//	size_t ll = strlen(tmp);
//	char *ch = GB2U8(tmp, &ll);
//	tw_send_200_OK(client, "text/html", ch, -1, 0);
//	free(ch);
//#else //linux 下，系统是和源代码文件编码都是是utf8的，就不需要转换
//	tw_send_200_OK(client, "text/html", tmp, -1, 0);
//#endif // _MSC_VER
//
	printf("  sk:%zd Request:\n",pa->sk);
	printf("  Query: %s\n",heads->query);
	printf("  Path: %s\n",heads->path);
	printf("  Host: %s\n",heads->host);
	printf("  Cookie: %s\n", heads->cookie);
	printf("  Range: %lld-%lld\n",heads->Range_frm,heads->Range_to);
	printf("  data(%zd): %s\n", heads->len,heads->data);
	if (!heads->cookie)
	{
		char ck[512];
		tw_make_setcookie(ck, 255,"TINYSSID","FDSAFdfafdsafds", 3600 * 8, NULL, heads->path);
		tw_make_setcookie(ck + strlen(ck),255,"TINYSSID2","faFDSAF45dsafds",  0, heads->host, NULL);
		sprintf(ck + strlen(ck), "WWW-Authenticate: Basic realm=\".\"\r\n");
		size_t len;
		char* rp = tw_format_http_respone(client, "401 Unauthorized", ck, "text/plan", "", -1, &len);
		tw_send_data(client, rp, len, 0, 1);
		return 1;
	}
	return 0;
}

char on_socket_data(void* data, uv_stream_t* client, tw_peerAddr* pa, membuf_t* buf)
{
	if (buf->size < 1)
		return 1;//防止发生数据为空
	if (pa->flag & 0x2) { //WebSocket
		printf((const char*)buf->data, buf->size < 256 ? buf->size : 256);
			printf("-------------------------------------------ws:%zd  dlen=%zd\n%s\n-------------------------------------------\n",pa->sk, buf->size, buf->data);
		ulong len = buf->size;
		char* p = WebSocketMakeFrame(buf->data, &len, 1);//文本帧
		tw_send_data(client, p, len, 0, 1);
	} else { //Socket
		printf("-------------------------------------------sk:%zd  dlen=%zd\n%s\n-------------------------------------------\n",pa->sk, buf->size, buf->data);
		tw_send_data(client, buf->data, buf->size, 1, 0);
	}
	return 1;
}

char on_close(void* data, uv_stream_t* client, tw_peerAddr* pa)
{
	//printf("closed: sk=%zd [%s:%d]  from:%s:%d    cli:%d\n", pa->sk, pa->ip, pa->port, pa->fip, pa->fport, client->loop->active_tcp_streams);
	return 0;
}

char on_connect(void* data, uv_stream_t* client, tw_peerAddr* pa)
{
	//printf("connected: sk=%zd [%s:%d]  from:%s:%d    cli:%d\n",pa->sk,pa->ip,pa->port,pa->fip,pa->fport, client->loop->active_tcp_streams);
	return 0;
}

char on_error(void* data, uv_stream_t* client, tw_peerAddr* pa, int errcode, char* errstr)
{
	//printf("error: sk=%zd [%s:%d]  from:%s:%d    cli:%d   %s\n", pa->sk, pa->ip, pa->port, pa->fip, pa->fport, client->loop->active_tcp_streams,errstr);
	return 0;
}

/*!  ----------------------------------------------------------------------------
 *  @fn WebServer
 *
 *  @brief Constructor of the Web Server Object.
 */
WebServer::WebServer(void)
{
}

/*!  ----------------------------------------------------------------------------
 *  @fn ~WebServer
 *
 *  @brief Destructor of the Web Server Object.
 */

WebServer::~WebServer(void)
{
}

/*!  ----------------------------------------------------------------------------
 *  @fn setup
 *
 *  @brief Setup the Web server
 */
int WebServer::setup(char *a_document_root, int a_port, uv_loop_t *an_evt_loop)
{
	memset(&m_conf, 0, sizeof(m_conf));
	m_conf.dirlist = 1;//目录列表
					 //conf.ip = NULL;// "127.0.0.1";
	m_conf.port = a_port;
	m_conf.doc_dir = a_document_root;
	m_conf.doc_index = NULL;// Default homepage
	//m_conf.doc_dir = NULL;// The directory where the default program files are located

	m_conf.on_request = on_request;
	m_conf.on_data = on_socket_data;
	m_conf.on_close = on_close;
	m_conf.on_connect = on_connect;
	m_conf.on_error = on_error;

	tinyweb_start(an_evt_loop, &m_conf);
    return 0;
}
