//
// original code from athena
// SQL conversion by Jioh L. Jung
//

#include <string.h>
#include <stdlib.h>

#include "char.h"
#include "strlib.h"
#include "inter.h"
#include "int_party.h"
#include "int_guild.h"
#include "int_storage.h"
#include "int_pet.h"
#include "lock.h"

#define WISDATA_TTL		(60*1000)	// Wisデータの生存時間(60秒)
#define WISDELLIST_MAX	256			// Wisデータ削除リストの要素数


struct accreg {
	int account_id,reg_num;
	struct global_reg reg[ACCOUNT_REG_NUM];
};

static struct accreg *accreg_pt;


int party_share_level = 10;
MYSQL mysql_handle;
MYSQL_RES* 	sql_res ;
MYSQL_ROW	sql_row ;
int sql_fields, sql_cnt;
char tmp_sql[65535];

MYSQL lmysql_handle;
char tmp_lsql[65535];
MYSQL_RES* 	lsql_res ;
MYSQL_ROW	lsql_row ;

int char_server_port = 3306;
char char_server_ip[32] = "127.0.0.1";
char char_server_id[32] = "ragnarok";
char char_server_pw[32] = "ragnarok";
char char_server_db[32] = "ragnarok";

int login_server_port = 3306;
char login_server_ip[32] = "127.0.0.1";
char login_server_id[32] = "ragnarok";
char login_server_pw[32] = "ragnarok";
char login_server_db[32] = "ragnarok";

// sending packet list
int inter_send_packet_length[]={
	-1,-1,27, 0, -1, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	-1, 7, 0, 0,  0, 0, 0, 0, -1,11, 0, 0,  0, 0,  0, 0,
	35,-1,11,15, 34,29, 7,-1,  0, 0, 0, 0,  0, 0,  0, 0,
	10,-1,15, 0, 79,19, 7,-1,  0,-1,-1,-1, 14,67,186,-1,
	 9, 9,-1, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	11,-1, 7, 3,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
};
// recv. packet list
int inter_recv_packet_length[]={
	-1,-1, 7, 0, -1, 6, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 6,-1, 0, 0,  0, 0, 0, 0, 10,-1, 0, 0,  0, 0,  0, 0,
	72, 6,52,14, 10,29, 6,-1, 34, 0, 0, 0,  0, 0,  0, 0,
	-1, 6,-1, 0, 55,19, 6,-1, 14,-1,-1,-1, 14,19,186,-1,
	 5, 9, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	 0, 0, 0, 0,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
	48,14,-1, 6,  0, 0, 0, 0,  0, 0, 0, 0,  0, 0,  0, 0,
};

struct WisData {
	int id,fd,count,len;
	unsigned long tick;
	unsigned char src[24],dst[24],msg[512];
};
static struct dbt * wis_db = NULL;
static int wis_dellist[WISDELLIST_MAX], wis_delnum;

//--------------------------------------------------------
// Save account_reg to sql (type=2)
int inter_accreg_tosql(int account_id,struct accreg *reg){
	
	int j;
	char temp_str[32];
	if (account_id<=0) return 0;
	reg->account_id=account_id;
	
	//`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
	sprintf(tmp_sql,"DELETE FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, account_id);
	if(mysql_query(&mysql_handle, tmp_sql) ) {
		printf("DB server Error (delete `global_reg_value`)- %s\n", mysql_error(&mysql_handle) );
	}
	
	if (reg->reg_num<=0) return 0;
	
	for(j=0;j<reg->reg_num;j++){
		if(reg->reg[j].str != NULL){
			sprintf(tmp_sql,"INSERT INTO `%s` (`type`, `account_id`, `str`, `value`) VALUES (2,'%d', '%s','%d')",
				reg_db, reg->account_id, jstrescapecpy(temp_str,reg->reg[j].str), reg->reg[j].value);
			if(mysql_query(&mysql_handle, tmp_sql) ) {
				printf("DB server Error (insert `global_reg_value`)- %s\n", mysql_error(&mysql_handle) );
			}
		}
	}
	return 0;
}

// Load account_reg from sql (type=2)
int inter_accreg_fromsql(int account_id,struct accreg *reg)
{
	int j=0;
	if (reg==NULL) return 0;
	memset(reg, 0, sizeof(struct accreg));
	reg->account_id=account_id;
	
	//`global_reg_value` (`type`, `account_id`, `char_id`, `str`, `value`)
	sprintf (tmp_sql, "SELECT `str`, `value` FROM `%s` WHERE `type`=2 AND `account_id`='%d'",reg_db, reg->account_id);
	if(mysql_query(&mysql_handle, tmp_sql) ) {
		printf("DB server Error (select `global_reg_value`)- %s\n", mysql_error(&mysql_handle) );
	}
	sql_res = mysql_store_result(&mysql_handle);

	if (sql_res) {
		for(j=0;(sql_row = mysql_fetch_row(sql_res));j++){	
			memcpy(reg->reg[j].str, sql_row[0],32);
			reg->reg[j].value = atoi(sql_row[1]);
		}
		mysql_free_result(sql_res);
	}
	reg->reg_num=j;
	return 0;
}

// Initialize
int inter_accreg_sql_init()
{
	CREATE(accreg_pt, struct accreg, 1);
	return 0;
	
}

/*==========================================
 * read config file
 *------------------------------------------
 */
int inter_config_read(const char *cfgName) {
	int i;
	char line[1024], w1[1024], w2[1024];
	FILE *fp;

	printf ("start reading interserver configuration: %s\n",cfgName);

	fp=fopen(cfgName,"r");
	if(fp==NULL){
		printf("file not found: %s\n", cfgName);
		return 1;
	}
	while(fgets(line, 1020, fp)){
		i=sscanf(line,"%[^:]: %[^\r\n]",w1,w2);
		if(i!=2)
			continue;

		if(strcmpi(w1,"char_server_ip")==0){
			strcpy(char_server_ip, w2);
			printf ("set char_server_ip : %s\n",w2);
		}
		else if(strcmpi(w1,"char_server_port")==0){
			char_server_port=atoi(w2);
			printf ("set char_server_port : %s\n",w2);
		}
		else if(strcmpi(w1,"char_server_id")==0){
			strcpy(char_server_id, w2);
			printf ("set char_server_id : %s\n",w2);
		}
		else if(strcmpi(w1,"char_server_pw")==0){
			strcpy(char_server_pw, w2);
			printf ("set char_server_pw : %s\n",w2);
		}
		else if(strcmpi(w1,"char_server_db")==0){
			strcpy(char_server_db, w2);
			printf ("set char_server_db : %s\n",w2);
		}
		//Logins information to be read from the inter_athena.conf
		//for character deletion (checks email in the loginDB)
		
		else if(strcmpi(w1,"login_server_ip")==0){
			strcpy(login_server_ip, w2);
			printf ("set login_server_ip : %s\n",w2);
		}
		else if(strcmpi(w1,"login_server_port")==0){
			login_server_port=atoi(w2);
			printf ("set login_server_port : %s\n",w2);
		}
		else if(strcmpi(w1,"login_server_id")==0){
			strcpy(login_server_id, w2);
			printf ("set login_server_id : %s\n",w2);
		}
		else if(strcmpi(w1,"login_server_pw")==0){
			strcpy(login_server_pw, w2);
			printf ("set login_server_pw : %s\n",w2);
		}
		else if(strcmpi(w1,"login_server_db")==0){
			strcpy(login_server_db, w2);
			printf ("set login_server_db : %s\n",w2);
		}
		else if(strcmpi(w1,"party_share_level")==0){
			party_share_level=atoi(w2);
			if(party_share_level < 0) party_share_level = 0;
		}else if(strcmpi(w1,"import")==0){
			inter_config_read(w2);
		}
		else if(strcmpi(w1,"log_inter")==0){
			log_inter = atoi(w2);
		}
		else if(strcmpi(w1,"login_server_db")==0){
			strcpy(login_server_db, w2);
			printf ("set login_server_db : %s\n",w2);
		}
	}
	fclose(fp);
	
	printf ("success reading interserver configuration\n");

	return 0;
}

// Save interlog into sql
int inter_log(char *fmt,...)
{
	char str[255];
	char temp_str[255];
	va_list ap;
	va_start(ap,fmt);

	vsprintf(str,fmt,ap);
	sprintf(tmp_sql,"INSERT INTO `%s` (`time`, `log`) VALUES (NOW(),  '%s')",interlog_db, jstrescapecpy(temp_str,str));
	if(mysql_query(&mysql_handle, tmp_sql) ) {
		printf("DB server Error (insert `interlog`)- %s\n", mysql_error(&mysql_handle) );
	}	

	va_end(ap);
	return 0;
}


// initialize
int inter_init(const char *file)
{
	//int i;
	
	printf ("interserver initialize...\n");
	inter_config_read(file);
	
	//DB connection initialized
	mysql_init(&mysql_handle);
	printf("Connect Character DB server.... (Character Server)\n");
	if(!mysql_real_connect(&mysql_handle, char_server_ip, char_server_id, char_server_pw,
		char_server_db ,char_server_port, (char *)NULL, 0)) {
			//pointer check
			printf("%s\n",mysql_error(&mysql_handle));
			exit(1);
	}
	else {
		printf ("Connect Success! (Character Server)\n");
	}
	
	mysql_init(&lmysql_handle);
	printf("Connect Character DB server.... (login server)\n");
	if(!mysql_real_connect(&lmysql_handle, login_server_ip, login_server_id, login_server_pw,
		login_server_db ,login_server_port, (char *)NULL, 0)) {
			//pointer check
			printf("%s\n",mysql_error(&lmysql_handle));
			exit(1);
	}else {
		printf ("Connect Success! (Login Server)");
	}
	wis_db = numdb_init();
	inter_guild_sql_init();
	inter_storage_sql_init();
	inter_party_sql_init();
	
	inter_pet_sql_init();
	inter_accreg_sql_init();

	//printf ("interserver timer initializing : %d sec...\n",autosave_interval);
	//i=add_timer_interval(gettick()+autosave_interval,inter_save_timer,0,0,autosave_interval);

	return 0;
}

int inter_mapif_init(int fd) {
	inter_guild_mapif_init(fd);

	return 0;
}


//--------------------------------------------------------

// GM message sending
int mapif_GMmessage(unsigned char *mes, int len) {
	unsigned char buf[len];

	WBUFW(buf, 0) = 0x3800;
	WBUFW(buf, 2) = len;
	memcpy(WBUFP(buf, 4), mes, len-4);
	mapif_sendall(buf, len);
	printf("\033[1;34m inter server: GM[len:%d] - '%s' \033[0m\n", len, mes);
	return 0;
}

// Wis sending
int mapif_wis_message(struct WisData *wd) {
	unsigned char buf[56 + wd->len];

	WBUFW(buf, 0) = 0x3801;
	WBUFW(buf, 2) = 56 +wd->len;
	WBUFL(buf, 4) = wd->id;
	memcpy(WBUFP(buf, 8), wd->src, 24);
	memcpy(WBUFP(buf,32), wd->dst, 24);
	memcpy(WBUFP(buf,56), wd->msg, wd->len);
	wd->count = mapif_sendall(buf,WBUFW(buf,2));

	return 0;
}
// Wis sending result
int mapif_wis_end(struct WisData *wd,int flag)
{
	unsigned char buf[27];
	
	WBUFW(buf, 0)=0x3802;
	memcpy(WBUFP(buf, 2),wd->src,24);
	WBUFB(buf,26)=flag;
	mapif_send(wd->fd,buf,27);
//	printf("inter server wis_end %d\n",flag);
	return 0;
}

int mapif_account_reg(int fd,unsigned char *src)
{
	unsigned char buf[WBUFW(src,2)];
	memcpy(WBUFP(buf,0),src,WBUFW(src,2));
	WBUFW(buf, 0)=0x3804;
	mapif_sendallwos(fd,buf,WBUFW(buf,2));
	return 0;
}

// Send the requested account_reg
int mapif_account_reg_reply(int fd,int account_id)
{
	struct accreg *reg=accreg_pt;
	inter_accreg_fromsql(account_id,reg);
	
	WFIFOW(fd,0)=0x3804;
	WFIFOL(fd,4)=account_id;
	if(reg->reg_num==0){
		WFIFOW(fd,2)=8;
	}else{
		int j,p;
		for(j=0,p=8;j<reg->reg_num;j++,p+=36){
			memcpy(WFIFOP(fd,p),reg->reg[j].str,32);
			WFIFOL(fd,p+32)=reg->reg[j].value;
		}
		WFIFOW(fd,2)=p;
	}
	WFIFOSET(fd,WFIFOW(fd,2));
	return 0;
}

//--------------------------------------------------------

// Existence check of WISP data
int check_ttl_wisdata_sub(void *key, void *data, va_list ap) {
	unsigned long tick;
	struct WisData *wd = (struct WisData *)data;
	tick = va_arg(ap, unsigned long);

	if (DIFF_TICK(tick, wd->tick) > WISDATA_TTL && wis_delnum < WISDELLIST_MAX)
		wis_dellist[wis_delnum++] = wd->id;

	return 0;
}

int check_ttl_wisdata() {
	unsigned long tick = gettick();
	int i;

	do {
		wis_delnum = 0;
		numdb_foreach(wis_db, check_ttl_wisdata_sub, tick);
		for(i = 0; i < wis_delnum; i++) {
			struct WisData *wd = numdb_search(wis_db, wis_dellist[i]);
			printf("inter: wis data id=%d time out : from %s to %s\n", wd->id, wd->src, wd->dst);
			// removed. not send information after a timeout. Just no answer for the player
			//mapif_wis_end(wd, 1); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
			numdb_erase(wis_db, wd->id);
			free(wd);
		}
	} while(wis_delnum >= WISDELLIST_MAX);

	return 0;
}

//--------------------------------------------------------

// GM message sending
int mapif_parse_GMmessage(int fd)
{
	mapif_GMmessage(RFIFOP(fd, 4), RFIFOW(fd, 2));
	return 0;
}


// Wisp/page request to send
int mapif_parse_WisRequest(int fd) {
	struct WisData* wd;
	static int wisid = 0;

	if (RFIFOW(fd,2)-52 >= sizeof(wd->msg)) {
		printf("inter: Wis message size too long.\n");
		return 0;
	} else if (RFIFOW(fd,2)-52 <= 0) { // normaly, impossible, but who knows...
		printf("inter: Wis message doesn't exist.\n");
		return 0;
	}
	sprintf (tmp_sql, "SELECT `name` FROM `%s` WHERE `char_id`='%d'",char_db, (int) RFIFOP(fd,28));
	if(mysql_query(&mysql_handle, tmp_sql) ) {
		printf("DB server Error - %s\n", mysql_error(&mysql_handle) );
	}
	sql_res = mysql_store_result(&mysql_handle);

	// search if character exists before to ask all map-servers
	if (!(sql_row = mysql_fetch_row(sql_res))) {
		unsigned char buf[27];
		WBUFW(buf, 0) = 0x3802;
		memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), 24);
		WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
		mapif_send(fd, buf, 27);
	// Character exists. So, ask all map-servers
	} else {
		// to be sure of the correct name, rewrite it
		memset(RFIFOP(fd,28), 0, 24);
		strncpy(RFIFOP(fd,28), sql_row[0], 24);
		// if source is destination, don't ask other servers.
		if (strcmp(RFIFOP(fd,4),RFIFOP(fd,28)) == 0) {
			unsigned char buf[27];
			WBUFW(buf, 0) = 0x3802;
			memcpy(WBUFP(buf, 2), RFIFOP(fd, 4), 24);
			WBUFB(buf,26) = 1; // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
			mapif_send(fd, buf, 27);
		} else {

	CREATE(wd, struct WisData, 1);

			// Whether the failure of previous wisp/page transmission (timeout)
			check_ttl_wisdata();

			wd->id = ++wisid;
			wd->fd = fd;
			wd->len= RFIFOW(fd,2)-52;
			memcpy(wd->src, RFIFOP(fd, 4), 24);
			memcpy(wd->dst, RFIFOP(fd,28), 24);
			memcpy(wd->msg, RFIFOP(fd,52), wd->len);
			wd->tick = gettick();
			numdb_insert(wis_db, wd->id, wd);
			mapif_wis_message(wd);
		}
	}

	return 0;
}


// Wisp/page transmission result
int mapif_parse_WisReply(int fd) {
	int id = RFIFOL(fd,2), flag = RFIFOB(fd,6);
	struct WisData *wd = numdb_search(wis_db, id);

	if (wd == NULL)
		return 0;	// This wisp was probably suppress before, because it was timeout of because of target was found on another map-server

	if ((--wd->count) <= 0 || flag != 1) {
		mapif_wis_end(wd, flag); // flag: 0: success to send wisper, 1: target character is not loged in?, 2: ignored by target
		numdb_erase(wis_db, id);
		free(wd);
	}

	return 0;
}


// Save account_reg into sql (type=2)
int mapif_parse_AccReg(int fd)
{
	int j,p;
	struct accreg *reg=accreg_pt;
	int account_id = RFIFOL(fd,4);
	memset(accreg_pt,0,sizeof(struct accreg));
	
	for(j=0,p=8;j<ACCOUNT_REG_NUM && p<RFIFOW(fd,2);j++,p+=36){
		memcpy(reg->reg[j].str,RFIFOP(fd,p),32);
		reg->reg[j].value=RFIFOL(fd,p+32);
	}
	reg->reg_num=j;
	
	inter_accreg_tosql(account_id,reg);
	mapif_account_reg(fd,RFIFOP(fd,0));	// Send confirm message to map
	return 0;
}

// Request the value of account_reg
int mapif_parse_AccRegRequest(int fd)
{
//	printf("mapif: accreg request\n");
	return mapif_account_reg_reply(fd,RFIFOL(fd,2));
}



//--------------------------------------------------------
int inter_parse_frommap(int fd)
{
	int cmd=RFIFOW(fd,0);
	int len=0;

	// inter鯖管轄かを調べる
	if(cmd<0x3000 || cmd>=0x3000+( sizeof(inter_recv_packet_length)/
		sizeof(inter_recv_packet_length[0]) ) )
		return 0;

	// パケット長を調べる
	if(	(len=inter_check_length(fd,inter_recv_packet_length[cmd-0x3000]))==0 )
		return 2;
	
	switch(cmd){
	case 0x3000: mapif_parse_GMmessage(fd); break;
	case 0x3001: mapif_parse_WisRequest(fd); break;
	case 0x3002: mapif_parse_WisReply(fd); break;
	case 0x3004: mapif_parse_AccReg(fd); break;
	case 0x3005: mapif_parse_AccRegRequest(fd); break;
	default:
		if( inter_party_parse_frommap(fd) )
			break;
		if( inter_guild_parse_frommap(fd) )
			break;
		if( inter_storage_parse_frommap(fd) )
			break;
		if( inter_pet_parse_frommap(fd) )
			break;
		return 0;
	}
	RFIFOSKIP(fd, len );
	return 1;
}

// RFIFO check
int inter_check_length(int fd, int length)
{
	if(length==-1){	// v-len packet
		if(RFIFOREST(fd)<4)	// packet not yet
			return 0;
		length = RFIFOW(fd, 2);
	}
	
	if(RFIFOREST(fd)<length)	// packet not yet
		return 0;
	
	return length;
}
