#include "stdafx.h"
#include "CLashConfig.h"
#include "CApp.h"
#include "comm.h"
#include "FileOperate.h"
#include "SysProxy.h"

CLashConfig::CLashConfig() : m_socks_port(9200), m_http_port(9300), m_c_port(9090), m_process(nullptr), m_Asyntask(1), m_AsyntaskProcessMonitor(1), m_is_qut(false)
{
	SNotifyCenter::getSingleton().addEvent(EVENTID(EventClashPreOceeQut));
	 

}

CLashConfig::~CLashConfig(void)
{
	if (m_process)
	{
		delete m_process;
		m_process = nullptr;
	}
 
}

BOOL CLashConfig::MakeClash()
{
	buildRules();
	YAML::Node root;
	root["port"] = m_http_port;
	root["socks-port"] = m_socks_port;
	root["allow-lan"] = true;
	root["external-controller"] = "127.0.0.1:" + std::to_string(m_c_port);
	if (CApp::getSingletonPtr()->GetRouteMode() == ROUT_MODE::cn_mode)
	{
		root["mode"] = "rule";
	}
	else {
		root["mode"] = "rule";
	}



#ifdef _DEBUG
	root["log-level"] = "debug";
#else
	root["log-level"] = "info";
#endif

	root["dns"] = buildDnsConfig();

	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::tun_mode)
	{
		root["tun"] = builTunConfig();
	}

	//proxys.reserve(0);

	//CList_node::getSingletonPtr()->listnode;
	YAML::Node proxies;
	for each (auto node in CApp::getSingletonPtr()->GetServerList()->vectlistmode)
	{
		if (node.type == "shadowsocks") {
			proxies.push_back(buildShadowsocks(&node));
		}
		else if (node.type == "trojan") {
			proxies.push_back(buildtrojan(&node));
		}
		else if (node.type == "v2ray") {
			proxies.push_back(buildv2ray(&node));
		}
	}


	root["proxies"] = proxies;
	root["proxy-groups"] = buildProxyGroups();

	YAML::Node reject;
	YAML::Node google;
	YAML::Node gfw;
	YAML::Node proxy;
	YAML::Node cncidr;


	reject["type"] = "http";
	reject["behavior"] = "domain";
	reject["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt";
	reject["path"] = "./ruleset/reject.yaml";
	reject["interval"] = 86400;

	google["type"] = "http";
	google["behavior"] = "domain";
	google["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt";
	google["path"] = "./ruleset/google.yaml";
	google["interval"] = 86400;


	gfw["type"] = "http";
	gfw["behavior"] = "domain";
	gfw["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt";
	gfw["path"] = "./ruleset/gfw.yaml";
	gfw["interval"] = 86400;




	proxy["type"] = "http";
	proxy["behavior"] = "domain";
	proxy["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt";
	proxy["path"] = "./ruleset/proxy.yaml";
	proxy["interval"] = 86400;


	cncidr["type"] = "http";
	cncidr["behavior"] = "domain";
	cncidr["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt";
	cncidr["path"] = "./ruleset/cncidr.yaml";
	cncidr["interval"] = 86400;

	if (CApp::getSingletonPtr()->GetRouteMode() == ROUT_MODE::cn_mode)
	{
		root["rule-providers"]["reject"] = reject;
		root["rule-providers"]["cncidr"] = cncidr;
		root["rule-providers"]["proxy"] = proxy;
		root["rule-providers"]["gfw"] = gfw;
		root["rule-providers"]["google"] = google;
	}

	root["rules"] = m_rules;
	auto name_file = DSPROXY_CONFIG_ClASH_NAME;

	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::tun_mode) {
		name_file = DSPROXY_CONFIG_TUN_ClASH_NAME;
	}


	try
	{
		m_run_config = CApp::getSingletonPtr()->GetConfigPath() / name_file;
		std::ofstream ofstream(CApp::getSingletonPtr()->GetConfigPath() /  name_file);
		ofstream << root;
		ofstream.close();
	}
	catch (const std::exception& e) {
		LOG_CAUGHT_EXCEPTION();
		return FALSE;
	}


	return TRUE;
}

BOOL CLashConfig::InitClash()
{
	if (!m_process)
	{
		m_process = new CProcess();
	}
	
	
	m_http_port = CTool::getSingletonPtr()->FindAvailableTcpPort(9300,9500);
	m_socks_port = CTool::getSingletonPtr()->FindAvailableTcpPort(9600,9800);
	m_c_port = CTool::getSingletonPtr()->FindAvailableTcpPort(9900);

	auto name_file = DSPROXY_CONFIG_INIT_ClASH_NAME;

	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::tun_mode) {
		/*name_file = dir + "\\" + CLASHCONFIINIT;*/
		name_file = DSPROXY_CONFIG_TUN_ClASH_NAME;
	}
	YAML::Node root;
	root["port"] = m_http_port;
	root["socks-port"] = m_socks_port;
	root["allow-lan"] = true;
	root["external-controller"] = "127.0.0.1:" + std::to_string(m_c_port);


	try
	{
		
		std::ofstream ofstream(CApp::getSingletonPtr()->GetConfigPath() / name_file);
		ofstream << root << std::endl;
		ofstream.close();
		return TRUE;
	}
	catch (const std::exception& e)
	{
		Logger::getSingletonPtr()->INFO(e.what());
		LOG_CAUGHT_EXCEPTION();
		return FALSE;
	}


    return TRUE;
}

BOOL CLashConfig::StartClash()
{
	auto assetsDir = std::filesystem::current_path() / CLASH_ASSETS_DIR_NAME;
	auto confg_path = CApp::getSingletonPtr()->GetConfigPath() / DSPROXY_CONFIG_INIT_ClASH_NAME;
	CProcessManager::getSingletonPtr()->SetArgs(assetsDir / DSPROXY_EXE_NAME, assetsDir, std::move(confg_path));

	/*ProcessManager::SetConfigFile(confg_path / DSPROXY_CONFIG_INIT_ClASH_NAME);*/
	/* m_Asyntask.AddTask(&CLashConfig::ThreadFun_process_Config, this, (LPARAM)CProcessManager::getSingletonPtr());*/

	if (CProcessManager::getSingletonPtr()->Start())
	{
		CApp::getSingletonPtr()->SetCLashRuning(true);
		m_AsyntaskProcessMonitor.AddTask(&CLashConfig::ThreadFun_ProcessMonitor_Config, this, (LPARAM)CProcessManager::getSingletonPtr());
	}
	
	/*if (m_process)
	{
		m_process->Create(CProcess::ASYNC);
		if (m_process->Execute(path_config.GetBuffer(0)))
		{
			
		}


	}*/
    return 0;
}

BOOL CLashConfig::StopClash()
{
	m_is_qut = true;
	/*SetEvent(_hEvent.get());*/

	//char ch[MAX_PATH];
	//memset(ch, 0, MAX_PATH);
	//sprintf_s(ch, "ok\n");
	//m_process->WriteSome(ch, sizeof(ch));
    return 0;
}

int CLashConfig::GetHttpPort()
{
	return m_http_port;
}

int CLashConfig::GetSocketPort()
{
	return m_socks_port;
}

int CLashConfig::GetClasApiPort()
{
	return m_c_port;
}

std::filesystem::path CLashConfig::GetRunConfig()
{
	return m_run_config;
}

YAML::Node CLashConfig::buildShadowsocks(CServerListMode* node)
{
	YAML::Node t_map;

	if (!node)
	{
		return t_map;
	}
	
	t_map["name"] = node->name;
	t_map["type"] = std::string("ss");
	t_map["server"] = node->host;
	t_map["port"] = std::to_string(node->port);
	t_map["cipher"] = node->method;
	t_map["password"] = node->passwd;
	t_map["udp"] = true;

	return t_map;

    return YAML::Node();
}

YAML::Node CLashConfig::buildtrojan(CServerListMode* node)
{
	YAML::Node t_map;

	if (!node)
	{
		return t_map;
	}
	
	t_map["name"] = node->name;
	t_map["type"] = std::string("trojan");
	t_map["server"] = node->host;
	t_map["port"] = std::to_string(node->port);
	t_map["password"] = node->passwd;
	if (!node->sni.empty()) {
		t_map["sni"] = node->sni;
	}
	t_map["udp"] = true;

	return t_map;
    return YAML::Node();
}

YAML::Node CLashConfig::buildv2ray(CServerListMode* node)
{
	YAML::Node t_map;
	if (!node)
	{
		return t_map;
	}

	std::vector<YAML::Node> p;
	t_map["name"] = node->name;
	t_map["type"] = std::string("vmess");
	t_map["server"] = node->host;
	t_map["port"] = std::to_string(node->port);
	t_map["uuid"] = node->uuid;
	t_map["cipher"] = node->method;
	t_map["alterId"] = node->v2_alter_id;
	t_map["udp"] = node->udp;

	if (node->v2_tls == "tls") {
		t_map["tls"] = true;
	}

	if (node->v2_net == "tcp") {
		t_map["skip-cert-verify"] = false;
		t_map["network"] = node->v2_net;
		if (!node->v2_sni.empty()) {
			t_map["servername"] = node->v2_sni;
		}
		 
	}
	else if (node->v2_net == "ws") {
		YAML::Node t_host;
		t_map["skip-cert-verify"] = false;
		/*
			   max-early-data: 2048
				early-data-header-name: Sec-WebSocket-Protocol
		 *
		 */
		t_host["host"] = node->v2_host;
		t_map["network"] = node->v2_net;
		t_map["ws-opts"]["headers"] = t_host;
		t_map["ws-opts"]["path"] = node->v2_path;
		t_map["ws-opts"]["max-early-data"] = 2048;
		t_map["ws-opts"]["early-data-header-name"] = "Sec-WebSocket-Protocol";

		/*t_map["ws-path"] = node->v2_path;
		t_map["ws-headers"] = t_host;*/
	}
	return t_map;
}

std::vector<YAML::Node> CLashConfig::buildv2rayHost(CServerListMode* node)
{
	std::vector<YAML::Node> p;
	if (node) {
		YAML::Node t_map;
		t_map["host"] = node->v2_host;
		p.push_back(t_map);
	}
	return p;
}

YAML::Node CLashConfig::builTunConfig()
{
	std::vector<std::string> dns;
	dns.push_back("198.18.0.2:53");
	YAML::Node node;
	node["enable"] = true;
	node["stack"] = "gvisor";
	node["dns-hijack"] = dns;
	node["auto-route"] = true;
	node["auto-detect-interface"] = true;
	return node;
}

YAML::Node CLashConfig::buildDnsConfig()
{
	YAML::Node dns;

	std::vector<std::string> dns_arr;
	dns_arr.push_back("114.114.114.114");
	dns_arr.push_back("8.8.8.8");
	dns_arr.push_back("tls://dns.rubyfish.cn:853");
	dns_arr.push_back("https://1.1.1.1/dns-query");
	dns["enable"] = true;
	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::tun_mode)
	{

		dns["enhanced-mode"] = "redir-host";
	}
	else
	{
		//dns["enable"] = false;
	}
	dns["nameserver"] = dns_arr;
	//dns["use-hosts"] = true;


	return dns;
}

std::vector<YAML::Node> CLashConfig::buildProxyGroups()
{
	std::vector<YAML::Node> dns_arr;
	std::vector<std::string> proxy;
	YAML::Node proxies;
	for each (auto node in CApp::getSingletonPtr()->GetServerList()->vectlistmode)
	{
		proxy.push_back(node.name.c_str());
	}
	YAML::Node li;
	li["name"] = "Proxy";
	li["type"] = "select";
	li["proxies"] = proxy;
	dns_arr.push_back(li);

	return dns_arr;
}

std::vector<YAML::Node> CLashConfig::buildRules()
{
	std::vector<YAML::Node> p;

	m_rules.clear();

	if (CApp::getSingletonPtr()->GetRouteMode() == ROUT_MODE::cn_mode)
	{
		m_rules.push_back("RULE-SET,proxy,Proxy");
		m_rules.push_back("RULE-SET,google,Proxy");
		m_rules.push_back("RULE-SET,gfw,Proxy");
		m_rules.push_back("RULE-SET,cncidr,DIRECT");

		m_rules.push_back("GEOIP,CN,DIRECT");
		m_rules.push_back("IP-CIDR,127.0.0.0/8,DIRECT");
		m_rules.push_back("IP-CIDR,172.16.0.0/12,DIRECT");
		m_rules.push_back("IP-CIDR,192.168.0.0/16,DIRECT");
		m_rules.push_back("IP-CIDR,10.0.0.0/8,DIRECT");
		m_rules.push_back("IP-CIDR,100.64.0.0/10,DIRECT");
	}
	else
	{
		// m_rules.push_back("RULE-SET,proxy,Proxy");
		// m_rules.push_back("RULE-SET,google,Proxy");
		// m_rules.push_back("RULE-SET,gfw,Proxy");
		// m_rules.push_back("RULE-SET,cncidr,Proxy");
		m_rules.push_back("GEOIP,CN,Proxy");
		m_rules.push_back("IP-CIDR,127.0.0.0/8,DIRECT");
		m_rules.push_back("IP-CIDR,172.16.0.0/12,DIRECT");
		m_rules.push_back("IP-CIDR,192.168.0.0/16,DIRECT");
		m_rules.push_back("IP-CIDR,10.0.0.0/8,DIRECT");
		m_rules.push_back("IP-CIDR,100.64.0.0/10,DIRECT");
	}

	m_rules.push_back("MATCH,Proxy");

	return p;
}

std::vector<YAML::Node> CLashConfig::buildruleproviders()
{
	std::vector<YAML::Node> pp;
	YAML::Node node;
	YAML::Node reject;
	YAML::Node google;
	YAML::Node gfw;
	YAML::Node proxy;
	YAML::Node cncidr;
	reject["type"] = "http";
	reject["behavior"] = "domain";
	reject["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/reject.txt";
	reject["path"] = "./ruleset/reject.yaml";
	reject["interval"] = 86400;
	node["reject"] = reject;
	pp.push_back(node);

	google["type"] = "http";
	google["behavior"] = "domain";
	google["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/google.txt";
	google["path"] = "./ruleset/google.yaml";
	google["interval"] = 86400;
	node["google"] = google;
	pp.push_back(node);


	gfw["type"] = "http";
	gfw["behavior"] = "domain";
	gfw["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/gfw.txt";
	gfw["path"] = "./ruleset/gfw.yaml";
	gfw["interval"] = 86400;
	node["gfw"] = gfw;
	pp.push_back(node);


	proxy["type"] = "http";
	proxy["behavior"] = "domain";
	proxy["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/proxy.txt";
	proxy["path"] = "./ruleset/proxy.yaml";
	proxy["interval"] = 86400;
	node["proxy"] = proxy;
	pp.push_back(node);

	cncidr["type"] = "http";
	cncidr["behavior"] = "domain";
	cncidr["url"] = "https://cdn.jsdelivr.net/gh/Loyalsoldier/clash-rules@release/cncidr.txt";
	cncidr["path"] = "./ruleset/cncidr.yaml";
	cncidr["interval"] = 86400;
	node["cncidr"] = proxy;
	pp.push_back(node);


	return pp;
}

void CLashConfig::ThreadFun_process_Config(LPARAM lParam)
{
 
}

void CLashConfig::ThreadFun_ProcessMonitor_Config(LPARAM lParam)
{
	CProcessManager* p = (CProcessManager*)lParam;
	if (p)
	{
		HANDLE hClashProcess = p->GetClashProcessInfo().hProcess;
		HANDLE hSubProcess = p->GetSubProcessInfo().hProcess;
 
		WaitForSingleObject(hClashProcess, INFINITE);

		CApp::getSingletonPtr()->SetCLashRuning(false);


		if (p->IsRunning() != State::Running)
		{
			return;
		}

		DWORD exitCode = 0;
		THROW_IF_WIN32_BOOL_FALSE(GetExitCodeProcess(hClashProcess, &exitCode));

		LOG_HR_MSG(E_FAIL, "syscode.exe exited with code: %ul", exitCode);

		FILETIME creationTime = {}, exitTime = {}, kernelTime = {}, userTime = {};
		THROW_IF_WIN32_BOOL_FALSE(GetProcessTimes(hClashProcess, &creationTime, &exitTime, &kernelTime, &userTime));
		auto creation = winrt::clock::from_FILETIME(creationTime);
		auto exit = winrt::clock::from_FILETIME(exitTime);
		if (exit - creation < 5s)
		{
			/*g_balloonClickAction = BalloonClickAction::ShowConsoleWindow;
			ShowBalloon(_(L"Clash process was alive less than 5 seconds\nTap to view console log"), _(L"Error"), NIIF_ERROR);
			co_await winrt::resume_on_signal(hSubProcess);*/
			WaitForSingleObject(hSubProcess, INFINITE);
		}

		if (p->IsRunning() != State::Running)
		{
			return;
		}

		p->Stop();

		EventClashPreOceeQut* pEvt = new EventClashPreOceeQut(nullptr);
		pEvt->status = 200;
		pEvt->msg = L"";
		SNotifyCenter::getSingleton().FireEventAsync(pEvt);
		pEvt->Release();

		
	}
	 
}

void CLashConfig::SetProxy()
{
	if (CApp::getSingletonPtr()->GetSysMode() == PROXY_MODE::sys_mode)
	{
		auto proxy = "127.0.0.1:" + std::to_string(m_http_port);
		SetSystemProxy(S_CA2W(proxy.c_str()), NULL, L"localhost;127.*;10.*;172.16.*;172.17.*;172.18.*;172.19.*;172.20.*;172.21.*;172.22.*;172.23.*;172.24.*;172.25.*;172.26.*;172.27.*;172.28.*;172.29.*;172.30.*;172.31.*;172.32.*;192.168.*");
	}
}

//winrt::fire_and_forget CLashConfig::_StartClash()
//{
//	if (CProcessManager::getSingletonPtr()->Start())
//	{
//		CApp::getSingletonPtr()->SetCLashRuning(true);
//
//
//
//		/*HANDLE hClashProcess = CProcessManager::getSingletonPtr()->GetClashProcessInfo().hProcess;
//
//		for (size_t i = 0; i < 5; ++i) {
//
//		}*/
//	}
//
//
// 
//}
//
//winrt::Windows::Foundation::IAsyncAction CLashConfig::ProcessMonitor()
//{
//	HANDLE hSubProcess = CProcessManager::getSingletonPtr()->GetClashProcessInfo().hProcess,
//		hClashProcess = CProcessManager::getSingletonPtr()->GetClashProcessInfo().hProcess;
//
//	 winrt::resume_on_signal(hClashProcess);
//
//	 /*std::future<void> f = std::async(std::launch::async, []() {
//		 })*/
//
//
//
// 
//}
