#include "http_server.h"

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/reboot.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <string.h>

#include "shttpd/shttpd.h"
#include "storage/StoragePreferences.h"
#include "service/BusinessConfig.h"
#include "manager/ConfigManager.h"
#include "utils/Log.h"
#include "shttpd/multipart_reader.h"
#include "os/MountMonitor.h"
#include "net/NetManager.h"

#define ETHERNETMANAGER		NETMANAGER->getEthernetManager()

namespace srv {

class MyThread: public Thread {
public:
	virtual bool readyToRun() {
		return true;
	}
	virtual bool threadLoop() {
		//检查是否有退出线程的请求，如果有，则返回false，立即退出线程
		if (exitPending()) {
			return false;
		}
		//休眠5s
		usleep(5000 * 1000);
		//重启
		sync();
		reboot(RB_AUTOBOOT);
		//返回真，继续下次线程循环
		return false;
	}
};

static MyThread my_thread;

//地址总长
#define ADDRLEN 15

//定义字符串数组保存IP地址、掩码地址、网关地址、首选DNS服务器地址以及备选DNS服务器地址
static std::string addr[5];
/**
 * 字符长度是否正确
 * */
const bool isLengthCorrect(const std::string addr)
{
	return addr.size() <= ADDRLEN;
}

/**
 * 检查是不是最多只有3个.字符
 * */
const bool noMoreThan3charactor(const std::string addr)
{
	//.字符个数
	int count = 0;
	for(unsigned int level1 = 0; level1 < addr.size(); ++level1)
	{
		if('.' == addr[level1])
		{
			++count;
		}
	}
	return 3 >= count;
}

/*
 * 判断所有字符是不是由0~9和.组成
 * */
const bool allCharacterIsLegal(const std::string addr)
{
	for(unsigned int level1 = 0; level1 < addr.size(); ++level1)
	{
		if(false == (('0' <= addr[level1] && addr[level1] <= '9') || '.' == addr[level1]))
		{
			return false;
		}
	}
	return true;
}

/**
 * 判断开头是否是1`9
 * */
const bool headIsCorrect(std::string addr, unsigned int pos = 0)
{
	//查找到达末尾，直接返回true
	if(addr.size() == pos)
	{
		return true;
	}
	//查找.字符
	if(std::string::npos == addr.find('.', pos))
	{
		if('0' < addr[pos] && addr[pos] <= '9')
		{
			return true;
		}
		//如果开头为字符'0'且其后没有字符，那么返回true
		else if(addr.size()-1 == pos)
		{
			return true;
		}
		return false;
	}
	else
	{
		//开头为0~9
		if('0' > addr[pos] || addr[pos] > '9')
		{
			return false;
		}
		//开头为0，后面没有字符返回false
		if('0' == addr[pos] && addr.size()-1 <= pos)
		{
			return false;
		}
		//跳过.字符继续查找
		return headIsCorrect(addr, addr.find('.', pos)+1);
	}
	return true; //默认返回true
}

/**
 * 检查地址每一小段的长度，最长为3个字符
 * */
const bool partCheractorCorrect(const std::string addr, unsigned int pos = 0)
{
	//查找到达末尾，直接返回true
	if(addr.size() == pos)
	{
		return true;
	}
	//查找.字符
	if(std::string::npos == addr.find('.', pos))
	{
		if(3 >= addr.size()-pos)
		{
			return true;
		}
		return false;
	}
	else
	{
		//由于跳过了.字符进行查找，因此判断条件应该是>3
		if(3 < addr.find('.', pos) - pos)
		{
			return false;
		}
		//跳过.字符继续查找
		return partCheractorCorrect(addr, addr.find('.', pos)+1);
	}
	return true; //默认返回true
}

/**
 * 检测地址是否有误
 * */
const bool checkAddr(std::string addr)
{
	return  (true == headIsCorrect(addr))?			//地址每小段开头是否为0~9
			(true == partCheractorCorrect(addr))?	//每小段字符串最大长度是否为3
			(true == noMoreThan3charactor(addr))?	//最多是否只有3个.字符
			(true == isLengthCorrect(addr))?		//地址最大长度是否为15
			(true == allCharacterIsLegal(addr))?	//所有的字符是否都由0~9和.组成
			true: false: false: false: false: false;
}

struct NetworkConfiguration {
	std::string ip;
	std::string sub_network_mask;
	std::string gateway;
	std::string dns1;
	std::string dns2;
};
NetworkConfiguration GetNetworkConfiguration() {
	char ip[64] = {0};
	char mask[64] = {0};
	char gateway[64] = {0};
	char dns1[64] = {0};
	char dns2[64] = {0};
	if (ETHERNETMANAGER->isAutoMode()) {
		ETHERNETMANAGER->getConfigureInfo(ip, mask, gateway, dns1, dns2);
	} else {
		ETHERNETMANAGER->getStaticConfigureInfo(ip, mask, gateway, dns1, dns2);
	}
	return NetworkConfiguration{ip, mask, gateway, dns1, dns2};
}

std::string read_file(const std::string &file) {
  std::ifstream ifs(file.c_str(), std::ios::binary | std::ios::in);
  if (!ifs.is_open()) {
    return "";
  }
  std::stringstream ss;
  ss << ifs.rdbuf();
  ifs.close();
  std::string content(ss.str());
  return content;
}

//  Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4MxyDJzPWCVPEFFc
std::string get_boundary(shttpd_arg *arg) {
  int len = 0;
  const char* boundary = shttpd_get_boundary(arg, &len);
  if (boundary) {
    return std::string(boundary, len);
  }
  return "";
}

/*
 * This callback function is attached to the "/" and "/abc.html" URIs,
 * thus is acting as "index.html" file. It shows a bunch of links
 * to other URIs, and allows to change the value of program's
 * internal variable. The pointer to that variable is passed to the
 * callback function as arg->user_data.
 */
static void
show_index(struct shttpd_arg *arg)
{
//  int   *p = arg->user_data;  /* integer passed to us */
  char    wifi_name[128] = {0};
  char    wifi_password[128] = {0};
  const char  *request_method, *query_string;

  request_method = shttpd_get_env(arg, "REQUEST_METHOD");
//  request_uri = shttpd_get_env(arg, "REQUEST_URI");
  query_string = shttpd_get_env(arg, "QUERY_STRING");

  /* Change the value of integer variable */
  if (!strcmp(request_method, "POST")) {
    /* If not all data is POSTed, wait for the rest */
    if (arg->flags & SHTTPD_MORE_POST_DATA)
      return;

    shttpd_get_var("wifi_name", arg->in.buf, arg->in.len,
        wifi_name, sizeof(wifi_name));
    shttpd_get_var("wifi_password", arg->in.buf, arg->in.len,
        wifi_password, sizeof(wifi_password));
//    mTextViewSetupNamePtr->setText(wifi_name);
//    mTextViewSetupPasswordPtr->setText(wifi_password);
//    StoragePreferences::putString(PROP_WIFI_NAME, wifi_name);
//    StoragePreferences::putString(PROP_WIFI_PASSWORD, wifi_password);
  } else if (query_string != NULL) { // GET
//    (void) shttpd_get_var("name1", query_string,
//        strlen(query_string), wifi_name, sizeof(wifi_name));
  }

  shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
  shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");

  std::string content = read_file(CONFIGMANAGER->getResFilePath("index.html"));
  std::string serverIp = StoragePreferences::getString(STORE_GATEWAY, serverIP);
  content = srv::format(content.c_str(),
		  NETMANAGER->getEthernetManager()->getMacAddr(),
		  NETMANAGER->getEthernetManager()->getIp(),
		  GetNetworkConfiguration().sub_network_mask.c_str(),
		  GetNetworkConfiguration().gateway.c_str(),
		  GetNetworkConfiguration().dns1.c_str(),
		  GetNetworkConfiguration().dns2.c_str(),
		  serverIp.c_str());
  std::string html(content);
  shttpd_printf(arg, "%s", html.c_str());
  if (html.empty()) {
    shttpd_printf(arg, "%s", "read file failed");
  }

  arg->flags |= SHTTPD_END_OF_OUTPUT;
}

struct state {
  size_t cl; /* Content-Length   */
  size_t nread; /* Number of bytes read */
  FILE *fp;
  size_t file_length;
  multipart_reader *reader;
  HttpServer* server;
  bool is_tf_mounted;
};

static void
on_form_data_receive(const multipart_reader* reader, const char* data, uint64_t data_length) {
//  LOGD("write form data [%s], filename = [%s] length %lld", reader->form_data_name, reader->form_data_filename, data_length);
  if (strcmp(reader->form_data_name, "uploadFile") != 0) {
    return;
  }
  if (data_length == 0) {
    return;
  }
  if (reader->user_data == NULL) {
    return;
  }

  struct state* state = (struct state*)reader->user_data;
  if (state->fp == NULL) {
    if (!state->is_tf_mounted) {
      LOGD("未插入SD卡");
      return;
    }
    state->fp = fopen(srv::format("/mnt/extsd/%s", reader->form_data_filename).c_str(), "wb");
    if (state->fp == NULL) {
      LOGE("创建文件失败");
      return;
    }
  }

  state->file_length += data_length;

  if (data_length != fwrite(data, 1, data_length, state->fp)) {
    LOGE("write form data failed");
    return;
  }
}

static void
upload_file(struct shttpd_arg *arg)
 {
  const char *s;

  const char* request_method = shttpd_get_env(arg, "REQUEST_METHOD");
  if (strcmp(request_method, "POST") != 0) {
    return;
  }

  /* If the connection was broken prematurely, cleanup */
  if ((arg->flags & SHTTPD_CONNECTION_ERROR) && arg->state) {
    struct state* state = (struct state*) arg->state;
    if (state->fp) {
      fclose(state->fp);
    }
    sync();
    multipart_reader_destroy(state->reader);
    free(state);
  } else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
    shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
    arg->flags |= SHTTPD_END_OF_OUTPUT;
  } else if (arg->state == NULL) {
    /* New request. Allocate a state structure, and open a file */
    struct state* state = (struct state*)calloc(1, sizeof(*state));
    arg->state = state;
    state->cl = strtoul(s, NULL, 10);
    state->server = (HttpServer*)arg->user_data;
    state->is_tf_mounted = MOUNTMONITOR->isMount();

    std::string boundary = get_boundary(arg);
    if (!boundary.empty()) {
      LOGD("boundary=%s", boundary.c_str());
    }
    state->reader = multipart_reader_create(boundary.c_str(), on_form_data_receive, state);

    shttpd_printf(arg, "HTTP/1.0 200 OK\n"
        "Content-Type: text/plain\n\n");
  } else {
    struct state* state = (struct state*)arg->state;

    if(0 != multipart_reader_execute(state->reader, arg->in.buf, arg->in.len)) {
      LOGE("multipart_parser failed");
    }

    state->nread += arg->in.len;

    /* Tell SHTTPD we have processed all data */
    arg->in.num_bytes = arg->in.len;

    if (state->server->upload_filename_listener()) {
      state->server->upload_filename_listener()(state->reader->form_data_filename);
    }
    if (state->server->upload_progress_listener()) {
      state->server->upload_progress_listener()(
          (state->nread > state->cl) ? state->cl : state->nread,
              state->cl);
    }

    /* Data stream finished? Close the file, and free the state */
    if (state->nread >= state->cl) {
      shttpd_printf(arg, "Written %d bytes", state->file_length);
      if (!state->is_tf_mounted) {
        shttpd_printf(arg, ", No TF card is mounted!");
      }
      LOGD("file length = %d", state->file_length);
      if (state->fp) {
        fflush(state->fp);
        fclose(state->fp);
      }
      sync();
      free(state);
      arg->flags |= SHTTPD_END_OF_OUTPUT;
    }
  }
}

static void
setting(struct shttpd_arg *arg){
	char dhcp[10] = {0};
	char ip[64] = {0};
	char netmask[64] = {0};
	char gateway[64] = {0};
	char dns1[64] = {0};
	char dns2[64] = {0};
	char serverIp[64] = {0};

	char tcpPort[64] = {0};

	char loraCmd[64] = {0};
	char loraState[64] = {0};
	char loraLearn[64] = {0};
	char loraDelete[64] = {0};
	char loraDeleteMac[64] = {0};

	const char *s;

	const char* request_method = shttpd_get_env(arg, "REQUEST_METHOD");
	//仅接受post
	if (strcmp(request_method, "POST") != 0) {
		return;
	}

	/* If the connection was broken prematurely, cleanup */
	if ((arg->flags & SHTTPD_CONNECTION_ERROR) && arg->state) {
		struct state* state = (struct state*) arg->state;
		if (state->fp) {
			fclose(state->fp);
		}
		sync();
		free(state);
	} else if ((s = shttpd_get_header(arg, "Content-Length")) == NULL) {
		shttpd_printf(arg, "HTTP/1.0 411 Length Required\n\n");
		arg->flags |= SHTTPD_END_OF_OUTPUT;
	} else if (arg->state == NULL) {
		/* New request. Allocate a state structure, and open a file */
		struct state* state = (struct state*)calloc(1, sizeof(*state));
		arg->state = state;
		state->cl = strtoul(s, NULL, 10);
		state->server = (HttpServer*)arg->user_data;

		shttpd_get_var("dhcp", arg->in.buf, arg->in.len,
				dhcp, sizeof(dhcp));
		LOGD(">>>>>>>>>>>>> dhcp %s", dhcp);
		shttpd_get_var("ip", arg->in.buf, arg->in.len,
				ip, sizeof(ip));
		LOGD(">>>>>>>>>>>>> ip %s", ip);
		shttpd_get_var("netmask", arg->in.buf, arg->in.len,
				netmask, sizeof(netmask));
		LOGD(">>>>>>>>>>>>> netmask %s", netmask);
		shttpd_get_var("gateway", arg->in.buf, arg->in.len,
				gateway, sizeof(gateway));
		LOGD(">>>>>>>>>>>>> gateway %s", gateway);
		shttpd_get_var("dns1", arg->in.buf, arg->in.len,
				dns1, sizeof(dns1));
		LOGD(">>>>>>>>>>>>> dns1 %s", dns1);
		shttpd_get_var("dns2", arg->in.buf, arg->in.len,
				dns2, sizeof(dns2));
		LOGD(">>>>>>>>>>>>> dns2 %s", dns2);
		shttpd_get_var("server_ip", arg->in.buf, arg->in.len,
				serverIp, sizeof(serverIp));
		LOGD(">>>>>>>>>>>>> server ip %s", serverIp);


		shttpd_get_var("tcp_port", arg->in.buf, arg->in.len,
				tcpPort, sizeof(tcpPort));
		LOGD(">>>>>>>>>>>>> tcp port %s", tcpPort);

		shttpd_get_var("lora_cmd", arg->in.buf, arg->in.len,
				loraCmd, sizeof(loraCmd));
		LOGD(">>>>>>>>>>>>> lora cmd %s", loraCmd);
		shttpd_get_var("lora_state", arg->in.buf, arg->in.len,
				loraState, sizeof(loraState));
		LOGD(">>>>>>>>>>>>> lora state %s", loraState);
		shttpd_get_var("lora_learn", arg->in.buf, arg->in.len,
				loraLearn, sizeof(loraLearn));
		LOGD(">>>>>>>>>>>>> lora learn %s", loraLearn);
		shttpd_get_var("lora_delete", arg->in.buf, arg->in.len,
				loraDelete, sizeof(loraDelete));
		LOGD(">>>>>>>>>>>>> lora delete %s", loraDelete);
		shttpd_get_var("lora_delete_mac", arg->in.buf, arg->in.len,
				loraDeleteMac, sizeof(loraDeleteMac));
		LOGD(">>>>>>>>>>>>> lora delete mac %s", loraDeleteMac);

		std::string content = read_file(CONFIGMANAGER->getResFilePath("setting_result.html"));

//		std::string _ip = ip;
//		std::string _netmask = netmask;
//		std::string _gateway = gateway;
//		std::string _dns1 = dns1;
//		std::string _dns2 = dns2;
//		std::string _serverIp = serverIp;

		std::string msg = "setting ok. rebooting... pls wait.";
		if (!checkAddr(ip)){
			msg = "ip wrong input";
		} else if (!checkAddr(netmask)){
			msg = "netmask wrong input";
		} else if (!checkAddr(gateway)){
			msg = "gateway wrong input";
		} else if (!checkAddr(serverIp)){
			msg = "server_ip wrong input";
		}
		else {
			std::string _dhcp = dhcp;
			if (_dhcp == "1"){
				ETHERNETMANAGER->setAutoMode(true);
			} else {
				ETHERNETMANAGER->setAutoMode(false);
				ETHERNETMANAGER->configure(ip, netmask, gateway, dns1, dns2);
			}

			StoragePreferences::putString(STORE_GATEWAY, serverIp);
			StoragePreferences::putString(STORE_TCP_LOCAL_IP, serverIp);
			StoragePreferences::putInt(STORE_TCP_PORT, std::stoi(tcpPort));

			std::string _loraCmd = loraCmd;
			if (_loraCmd == "1") {
				std::string _loraState = loraState;
				sendToLora(std::stoi(_loraCmd), std::stoi(_loraState));
			}
			else if (_loraCmd == "0") {
				std::string _loraLearn = loraLearn;
				sendToLora(std::stoi(_loraCmd), std::stoi(_loraLearn));
			}
			else if (_loraCmd == "2") {
				std::string _loraDelete = loraDelete;
				std::string _loraDeleteMac = loraDeleteMac;
				if (_loraDelete == "1" && _loraDeleteMac.length() != 6) {
					msg = "lora delete mac wrong input";
					content = srv::format(content.c_str(), msg.c_str());

					shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
					shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");

					std::string html(content);
					shttpd_printf(arg, "%s", html.c_str());
					if (html.empty()) {
						shttpd_printf(arg, "%s", "read file failed");
					}

					arg->flags |= SHTTPD_END_OF_OUTPUT;
					return;
				}
				sendDeleteToLoraAndRestart(loraDelete, loraDeleteMac);
			}
		}

		content = srv::format(content.c_str(), msg.c_str());

		shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
		shttpd_printf(arg, "%s", "Content-Type: text/html\r\n\r\n");

		std::string html(content);
		shttpd_printf(arg, "%s", html.c_str());
		if (html.empty()) {
			shttpd_printf(arg, "%s", "read file failed");
		}

		arg->flags |= SHTTPD_END_OF_OUTPUT;
		my_thread.run("reboot");
	} else {

	}
}

/*
 * This callback function is used to show how to handle 404 error
 */
static void
show_404(struct shttpd_arg *arg)
{
  shttpd_printf(arg, "%s", "HTTP/1.1 200 OK\r\n");
  shttpd_printf(arg, "%s", "Content-Type: text/plain\r\n\r\n");
  shttpd_printf(arg, "%s", "Oops. File not found! ");
  shttpd_printf(arg, "%s", "This is a custom error handler.");
  arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/*
 * Make sure we have ho zombies from CGIs
 */
static void
signal_handler(int sig_num)
{
  switch (sig_num) {
#ifndef _WIN32
  case SIGCHLD:
    while (waitpid(-1, &sig_num, WNOHANG) > 0) ;
    break;
#endif /* !_WIN32 */
  default:
    break;
  }
}

HttpServer::HttpServer() {
  stop_ = false;
  port_ = 80;
  upload_filename_listener_ = NULL;
  upload_progress_listener_ = NULL;
}

HttpServer::~HttpServer() {
}

bool HttpServer::threadLoop() {
  stop_ = false;
  RunSync(port_);
  return false;
}

void HttpServer::RunAsync(int port) {
  port_ = port;
  stop_ = false;
  run("shttpd");
}

int HttpServer::RunSync(int port) {
  struct shttpd_ctx *ctx;

#ifndef _WIN32
  signal(SIGPIPE, SIG_IGN);
  signal(SIGCHLD, &signal_handler);
#endif /* !_WIN32 */

  /*
   * Initialize SHTTPD context.
   * Start listening on ports 8080 and 8081
   */
  ctx = shttpd_init(0, NULL);
  shttpd_set_option(ctx, "ports", format("%d", port).c_str());

  /* 注册uri和它的回调函数*/
  shttpd_register_uri(ctx, "/", &show_index, NULL);
  shttpd_register_uri(ctx, "/setting", &setting, this);

  shttpd_handle_error(ctx, 404, show_404, NULL);

  /* Serve connections infinitely until someone kills us */
  while (!stop_) {
    shttpd_poll(ctx, 1000);
  }

  /* Probably unreached, because we will be killed by a signal */
  shttpd_fini(ctx);

  return 0;
}

void HttpServer::Stop() {
  stop_ = true;
  requestExitAndWait();
  stop_ = false;
}

void HttpServer::set_upload_filename_listener(UploadFilenameListener listener) {
  upload_filename_listener_ = listener;
}
void HttpServer::set_upload_progress_listener(UploadProgressListener listener) {
  upload_progress_listener_ = listener;
}

HttpServer::UploadFilenameListener HttpServer::upload_filename_listener() {
  return upload_filename_listener_;
}
HttpServer::UploadProgressListener HttpServer::upload_progress_listener() {
  return upload_progress_listener_;
}


} /* namespace srv */
