
#include "stratum.h"

static int g_extraonce1_counter = 0;

void get_next_extraonce1(char *extraonce1)
{
	CommonLock(&g_nonce1_mutex);

	g_extraonce1_counter++;
	sprintf(extraonce1, "%08x", g_extraonce1_counter|0x81000000);

	CommonUnlock(&g_nonce1_mutex);
}

void get_random_key(char *key)
{
	int i1 = rand();
	int i2 = rand();
	int i3 = rand();
	int i4 = rand();
	sprintf(key, "%08x%08x%08x%08x", i1, i2, i3, i4);
}

VEXON_CLIENT *client_find_notify_id(const char *notify_id, bool reconnecting)
{
	if (!notify_id || !strlen(notify_id))
		return NULL;

	g_list_client.Enter();
	for(CLI li = g_list_client.first; li; li = li->next)
	{
		VEXON_CLIENT *client = (VEXON_CLIENT *)li->data;
		if(client->reconnecting == reconnecting && !strcmp(client->notify_id, notify_id))
		{
			g_list_client.Leave();
			return client;
		}
	}

	g_list_client.Leave();
	return NULL;
}

void client_sort()
{
	for(CLI li = g_list_client.first; li && li->next; li = li->next)
	{
		VEXON_CLIENT *client1 = (VEXON_CLIENT *)li->data;
		VEXON_CLIENT *client2 = (VEXON_CLIENT *)li->next->data;

//		if(client2->difficulty_actual > client1->difficulty_actual)
		if(client2->speed > client1->speed*1.5)
		{
			g_list_client.Swap(li, li->next);
			client_sort();

			return;
		}
	}
}

int client_send_error(VEXON_CLIENT *client, int error, const char *string)
{
	char buffer3[1024];

	if(client->id_str)
		sprintf(buffer3, "\"%s\"", client->id_str);
	else
		sprintf(buffer3, "%d", client->id_int);

	return socket_send(client->sock, "{\"id\":%s,\"result\":false,\"error\":[%d,\"%s\",null]}\n", buffer3, error, string);
}

int client_send_result(VEXON_CLIENT *client, const char *format, ...)
{
	char buffer[VEXON_SMALLBUFSIZE];
	va_list args;

	va_start(args, format);
	vsprintf(buffer, format, args);
	va_end(args);

	char buffer3[1024];

	if(client->id_str)
		sprintf(buffer3, "\"%s\"", client->id_str);
	else
		sprintf(buffer3, "%d", client->id_int);

	return socket_send(client->sock, "{\"id\":%s,\"result\":%s,\"error\":null}\n", buffer3, buffer);
}

int client_call(VEXON_CLIENT *client, const char *method, const char *format, ...)
{
	char buffer[VEXON_SMALLBUFSIZE];
	va_list args;

	va_start(args, format);
	vsprintf(buffer, format, args);
	va_end(args);

	return socket_send(client->sock, "{\"id\":null,\"method\":\"%s\",\"params\":%s}\n", method, buffer);
}

int client_ask(VEXON_CLIENT *client, const char *method, const char *format, ...)
{
	char buffer[VEXON_SMALLBUFSIZE];
	va_list args;
	int64_t id = client->shares;

	va_start(args, format);
	vsprintf(buffer, format, args);
	va_end(args);

	int ret = socket_send(client->sock, "{\"id\":%d,\"method\":\"%s\",\"params\":%s}\n", id, method, buffer);
	if (ret == -1) {
		debuglog("unable to ask %s\n", method);
		return 0; // -errno
	}
	client->reqid = id;
	return id;
}

void client_block_ip(VEXON_CLIENT *client, const char *reason)
{
	char buffer[1024];
	sprintf(buffer, "iptables -A INPUT -s %s -p tcp --dport %d -j REJECT", client->sock->ip, g_tcp_port);
	if(strcmp("0.0.0.0", client->sock->ip) == 0) return;
	if(strstr(client->sock->ip, "192.168.")) return;
	if(strstr(client->sock->ip, "127.0.0.")) return;

	int s = system(buffer);
	stratumlog("%s: %s blocked (%s)\n", g_stratum_algo, client->sock->ip, reason);
}

void client_block_ipset(VEXON_CLIENT *client, const char *ipset_name)
{
	char buffer[1024];
	sprintf(buffer, "ipset -q -A %s %s", ipset_name, client->sock->ip);
	if(strcmp("0.0.0.0", client->sock->ip) == 0) return;
	if(strstr(client->sock->ip, "192.168.")) return;
	if(strstr(client->sock->ip, "127.0.0.")) return;

	int s = system(buffer);
	stratumlog("%s: %s blocked via ipset %s %s\n", g_stratum_algo, client->sock->ip, ipset_name, client->username);
}

bool client_reset_multialgo(VEXON_CLIENT *client, bool first)
{
//	return false;
	if(!client->algos_subscribed[0].algo) return false;
//	debuglog("client_reset_multialgo\n");

	VEXON_CLIENT_ALGO *best = NULL;
	VEXON_CLIENT_ALGO *current = NULL;

	for(int i=0; g_algos[i].name[0]; i++)
	{
		VEXON_ALGO *algo = &g_algos[i];
		for(int j=0; client->algos_subscribed[j].algo; j++)
		{
			VEXON_CLIENT_ALGO *candidate = &client->algos_subscribed[j];
			if(candidate->algo == algo)
			{
				if(!best || algo->profit*candidate->factor > best->algo->profit*best->factor)
					best = candidate;
			}

			if(!current && candidate->algo == g_current_algo)
				current = candidate;
		}
	}

	if(!best || !current || best == current)
	{
		client->last_best = time(NULL);
		return false;
	}

	if(!first)
	{
		int e = time(NULL) - client->last_best;
		double d = best->algo->profit*best->factor - current->algo->profit*current->factor;
		double p = d/best->algo->profit/best->factor;
#ifdef DEBUG_BEST_MULTI
		debuglog("current %s %f\n", current->algo->name, current->algo->profit*current->factor);
		debuglog("best    %s %f\n", best->algo->name, best->algo->profit*best->factor);
		debuglog(" %d * %f = %f --- percent %f %f\n", e, d, e*d, p, e*p);
#endif
		if(p < 0.02) return false;
		if(e*p < 100) return false;
	}

	shutdown(client->sock->sock, SHUT_RDWR);
	return true;
}

bool client_initialize_multialgo(VEXON_CLIENT *client)
{
	char *p = strstr(client->password, "p=");
	if(p)
	{
		double profit = atof(p+2);
		if(profit > g_current_algo->profit)
			return true;
	}

	char tmp[1024];
	memset(tmp, 0, 1024);
	strncpy(tmp, client->password, 1023);

	p = tmp;
	while(p)
	{
		double value = 0;

		char *p1 = strchr(p, ',');
		if(p1) *p1 = 0;

		char *p2 = strchr(p, '=');
		if(p2)
		{
			*p2 = 0;
			value = atof(p2+1);
		}

		for(int i=0; g_algos[i].name[0]; i++)
		{
			VEXON_ALGO *algo = &g_algos[i];
			if(!strcmp(algo->name, p))
			{
				int i=0;
				for(; i<VEXON_MAXALGOS-1 && client->algos_subscribed[i].algo; i++);

				client->algos_subscribed[i].algo = algo;
				client->algos_subscribed[i].factor = value? value: algo->factor;
			}
		}

		p = p1? p1+1: p1;
	}

	bool reset = client_reset_multialgo(client, true);
	return reset;
}

void client_add_job_history(VEXON_CLIENT *client, int jobid)
{
	if(!jobid)
	{
		debuglog("trying to add jobid 0\n");
		return;
	}

	bool b = client_find_job_history(client, jobid, 0);
	if(b)
	{
//		debuglog("ERROR history already added job %x\n", jobid);
		return;
	}

	for(int i=VEXON_JOB_MAXHISTORY-1; i>0; i--)
		client->job_history[i] = client->job_history[i-1];

	client->job_history[0] = jobid;
}

bool client_find_job_history(VEXON_CLIENT *client, int jobid, int startat)
{
	for(int i=startat; i<VEXON_JOB_MAXHISTORY; i++)
	{
		if(client->job_history[i] == jobid)
		{
//			if(!startat)
//				debuglog("job %x already sent, index %d\n", jobid, i);

			return true;
		}
	}

	return false;
}

int hostname_to_ip(const char *hostname , char* ip)
{
    struct hostent *he;
    struct in_addr **addr_list;
    int i;

    if(hostname[0]>='0' && hostname[0]<='9')
    {
    	strcpy(ip, hostname);
    	return 0;
    }

    if ( (he = gethostbyname( hostname ) ) == NULL)
    {
        // get the host info
        herror("gethostbyname");
        return 1;
    }

    addr_list = (struct in_addr **) he->h_addr_list;

    for(i = 0; addr_list[i] != NULL; i++)
    {
        //Return the first one;
        strcpy(ip, inet_ntoa(*addr_list[i]));
        return 0;
    }

    return 1;
}

bool client_find_my_ip(const char *name)
{
//	return false;
	char ip[1024] = "";

	hostname_to_ip(name, ip);
	if(!ip[0]) return false;

	char host[NI_MAXHOST];
	for(struct ifaddrs *ifa = g_ifaddr; ifa != NULL; ifa = ifa->ifa_next)
	{
		if(ifa->ifa_addr == NULL) continue;
		host[0] = 0;

		getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
		if(!host[0]) continue;

		if(!strcmp(host, ip))
		{
			debuglog("found my ip %s\n", ip);
			return true;
		}
	}

	return false;
}







