/*
 *  ADP (Another Data Processor) www.adp.la
 *  Copyright (C) 2010 Katsuhisa Ohfuji <katsuhisa@ohfuji.name>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 2 as
 *  published by the Free Software Foundation.
 *
 *  This program 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *  MA 02110-1301, USA.
 */

#ifndef ADP_BUILTIN_NET_H
#define ADP_BUILTIN_NET_H

struct ExecContext_Get_Html : public ExecContextRoot {
	ExecContext_Get_Html(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() < 9 ) return RERR_argment_number(excp, 9);
		const PString *ohost = get<PString>(0);
		const PObject *oport = get<PObject>(1);
		const PString *ourl = get<PString>(2);
		const PString *omethod = get<PString>(3);
		const PList   *ovalues = get<PList>(4);
		const PList   *oincookies = get<PList>(5);
		const PVeriable *oheader = dynamic_cast<const PVeriable*>((*pred)[6]);
		const PVeriable *ooutcookies = dynamic_cast<const PVeriable*>((*pred)[7]);
		const PVeriable *obody = dynamic_cast<const PVeriable*>((*pred)[8]);

		if ( !ohost ) return RERR_argment_type(excp, 0);
		if ( !oport ) return RERR_argment_type(excp, 1);
		if ( !ourl  ) return RERR_argment_type(excp, 2);
		if ( !omethod ) return RERR_argment_type(excp, 3);

		double port;
		if ( !oport->cnv_double(port) ) return RERR_argment_type(excp, 1);

		ssmap	values;
		list2ssmap( ovalues, values);
		ssmap	cookies;
		list2ssmap( oincookies, cookies);		

		string header;
		string body;
		
		if ( html_request( ohost->c_str(), (int)port, ourl->c_str(), omethod->c_str(), values, header, body, cookies, pred->opt.debug ) != 0 ) {
			return RERR( excp, "network error ");
		}

		if ( oheader ) {
			PString *ph = pmm.newPString(pobjs,header);
			(*gl)[oheader->idx] = ph;
		}
		if ( obody ) {
			PString *pb = pmm.newPString(pobjs,body);
			(*gl)[obody->idx] = pb;
		}
		if ( ooutcookies ) {
			PList *outcookies = ssmap2list( cookies, this);
			(*gl)[ooutcookies->idx] = outcookies;
		}
		return true;
	}
};

// 引き数が 1個のとき $a = query@ （CGI変数の全取得）
// 引き数が 2個のとき $a = query('name') （特定のCGI変数の取得、見つからない場合はfalse）
// 引き数が 2個のとき $a = query@('name') nameで指定されたハッシュ形式のCGI変数(name[〜])の取得、〜の部分がハッシュキーになる
// 引き数が 3個のとき $a = query('name', $obj) （特定のクエリー変数の取得、見つからないときは第2引数で指定した値を取得）
// 引き数が 3個のとき $a = query@('name', $defvalue, $obj) nameで指定されたハッシュ形式のCGI変数(name[〜])の取得、〜の部分がハッシュキーになる、見つからないときは第2引数で指定した値を取得
struct ExecContext_Query : public ExecContextRoot {
	ExecContext_Query(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
        if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);

		if ( pred->size() < 1 || pred->size() > 3 ) return RERR_argment_number(excp, 1);
		string s1;
		const char *name = cnv_charp(0,s1);
		const int len = (name != 0) ? strlen(name) : 0;
		const PObject *value = 0; (pred->size() > 1) ? get<PObject>(pred->size()-1) : 0;

		if ( this->pred->opt.result_array ) {
			const PObject *value = get<PObject>(pred->size()-1);
			bool findflg = false;
			bool allflg = pred->size() == 1;
			PArray *a = pmm.newPArray(pobjs);
			a->brace = true;
			int	rbrace;
			for ( ssmap::iterator i = query.begin(); i != query.end(); i++ ) {
				if ( allflg ) {
					PString *o = pmm.newPString(pobjs,i->second);
					a->push_back( o, i->first);
					findflg = true;
				} else if ( memcmp( i->first.c_str(), name, len) == 0 && i->first[len] == '[' && (rbrace = i->first.find(']')) > len ) {
					string	key = string(i->first.begin() + len + 1, i->first.begin() + rbrace); 
					PString *o = pmm.newPString(pobjs, i->second);
					a->push_back( o, key);
					findflg = true;
				}
			}
			if ( findflg ) {
				return value->unify(*a, this);
			} else {
				if ( allflg == true ) { 
					value->unify(pnil, this);
					return false; 
				}
				if ( pred->size() != 3 ) return RERR_argment_number(excp, 3);
				const PObject *o = get<PObject>(1);
				return value->unify(*o, this);		
			}
		} else {
			const PObject *value = (pred->size() > 1) ? get<PObject>(pred->size()-1) : 0;
			if ( !name ) return RERR_argment_type(excp, 0);
			if ( !value ) return RERR_argment_type(excp, pred->size()-1);
			ssmap::iterator i = query.find(name);
			if ( i != query.end() ) {
				PString *o = pmm.newPString(pobjs,i->second);
				return value->unify(*o, this);	
			} else {
				if ( pred->size() == 2 ) {
					value->unify(pnil, this);
					return false;
				}
				const PObject *o = get<PObject>(1);
				return value->unify(*o, this);	
			}
		}
	}
};

struct ExecContext_Cookie : public ExecContextRoot {
	ExecContext_Cookie(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
        if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() != 2 ) return RERR_argment_number(excp, 2);
		string s1;
		const char *name = cnv_charp(0,s1);
		const PObject *value = get<PObject>(1);
		if ( !name ) return RERR_argment_type(excp, 0);
		if ( !value ) return RERR_argment_type(excp, 1);
	
		ssmap::iterator i = cookies.find(name);
		if ( i != cookies.end() ) {
			PString *o = pmm.newPString(pobjs,i->second);
			return value->unify(*o, this);
		} else {
			return false;	
		}
		return true;
	}
};

struct ExecContext_Response_Header : public ExecContextRoot {
	ExecContext_Response_Header(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
		if ( !first_print ) return RERR_Alrady_output_response_header(excp);

		const PString *header = get<PString>(0);

		if ( !header ) return RERR_argment_type(excp, 0);
		header_string = header->value;
		if ( strcspn( header_string.c_str(), "\r\n") == 0 ) header_string += "\r\n";
		return true;
	}
};

struct ExecContext_Add_Response_Header : public ExecContextRoot {
	ExecContext_Add_Response_Header(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() != 1 ) return RERR_argment_number(excp, 1);
		if ( !first_print ) return RERR_Alrady_output_response_header(excp);
		const PString *header = get<PString>(0);

		if ( !header ) return RERR_argment_type(excp, 0);
		string add_string = header->value;
		if ( strcspn( add_string.c_str(), "\r\n") == 0 ) add_string += "\r\n";
		header_string += add_string;
		return true;
	}
};

struct ExecContext_Set_Cookie_Row : public ExecContextRoot {
	ExecContext_Set_Cookie_Row(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		string s1,s2,s3,s4,s5,s6;
		const char *name = cnv_charp(0,s1);
		const char *value = cnv_charp(1,s2);
		const char *expires = cnv_charp(2,s3);
		const char *domain = cnv_charp(3,s4);
		const char *path = cnv_charp(4,s5);
		const char *secure = cnv_charp(5,s6);
		if ( !name ) return RERR_argment_type(excp, 0);
		if ( !value ) return RERR_argment_type(excp, 1);
		string v(value);
		string ev;
		urlencode( v.begin(), v.end(), ev);
		string cookie = "Set-Cookie: ";
		cookie += name;
		cookie += "=";
		cookie += ev;
		if ( expires ) {
		    cookie += ";";
			cookie += " expires=";
			cookie += expires;
		}
		if ( domain ) {
			cookie += ";";
			cookie += " domain=";
			cookie += domain;
		}
		if ( path ) {
			cookie += ";";
			cookie += " path=";
			cookie += path;
		}
		if ( secure ) {
			cookie += ";";
			cookie += " ";
			cookie += secure;
		}
		cookie += ";\r\n";
		header_string = cookie + header_string;	// set-cookieはcontent-typeの前に出力
		return true;
	}
};

struct ExecContext_HTMLescape : public ExecContextRoot {
	ExecContext_HTMLescape(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		size_t last = pred->size() - 1;
		string result;
		for ( size_t i = 0; i < last; i++ ) {
			const PObject *item = get<PObject>(i);
			string str,estr;
			if ( !item->cnv_string(str) ) return RERR_argment_type(excp, i);
			htmlescape(str, false);
			result += str;
		}
		const PString *p = pmm.newPString( pobjs,result);
		return unify<PString>( last, p, excp);
	}
};

struct ExecContext_AWPescape : public ExecContextRoot {
	ExecContext_AWPescape(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		size_t last = pred->size() - 1;
		string result;
		for ( size_t i = 0; i < last; i++ ) {
			const PObject *item = get<PObject>(i);
			string str,estr;
			if ( !item->cnv_string(str) ) return RERR_argment_type(excp, i);
			htmlescape(str, true);
			result += str;
		}
		const PString *p = pmm.newPString( pobjs,result);
		return unify<PString>( last, p, excp);
	}
};

struct ExecContext_URLencode : public ExecContextRoot {
	ExecContext_URLencode(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		size_t last = pred->size() - 1;
		string result;
		for ( size_t i = 0; i < last; i++ ) {
			const PObject *item = get<PObject>(i);
			string str,estr;
			if ( !item->cnv_string(str) ) return RERR_argment_type(excp, i);
			urlencode( str.begin(), str.end(), estr);
			result += estr;
		}
		const PString *p = pmm.newPString( pobjs,result);
		return unify<PString>( last, p, excp);
	}
};

struct ExecContext_URLdecode : public ExecContextRoot {
	ExecContext_URLdecode(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( pred->size() < 2 ) return RERR_argment_number(excp, 2);
		size_t last = pred->size() - 1;
		string result;
		for ( size_t i = 0; i < last; i++ ) {
			const PObject *item = get<PObject>(i);
			string str,estr;
			if ( !item->cnv_string(str) ) return RERR_argment_type(excp, i);
			urldecode( str.begin(), str.end(), estr);
			result += estr;
		}
		const PString *p = pmm.newPString( pobjs,result);
		return unify<PString>( last, p, excp);
	}
};

struct ExecContext_WOL : public ExecContextRoot {
	ExecContext_WOL(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() < 1 ) return RERR_argment_number(excp, 1);
		const PObject *item = get<PObject>(0);
		int mac[6];
		string mac_str;
		if ( !item->cnv_string(mac_str) ) return RERR_argment_type(excp, 0);
		if ( sscanf( mac_str.c_str(), "%2x:%2x:%2x:%2x:%2x:%2x", mac, mac+1, mac+2, mac+3, mac+4, mac+5) != 6 ) {
			if ( sscanf( mac_str.c_str(), "%2x-%2x-%2x-%2x-%2x-%2x", mac, mac+1, mac+2, mac+3, mac+4, mac+5) != 6 ) {
				return RERR_argment_type(excp, 1);
			}
		}
		// magic パケットの作成	
		char buf[6+16*6];
		for ( int i = 0; i < 6; i++ ) {
			buf[i] = 0xff;
		}
		for ( int i = 0; i < 16*6; i++ ) {
			buf[i+6] = (unsigned char)mac[i % 6];
		}
		const char *baddr = "255.255.255.255";
		if ( pred->size() >= 2 ) {
			const PString *item = get<PString>(1);
			baddr = item->c_str();
		}
		PINTEGER port  = 10;
		if ( pred->size() >= 3 ) {
			const PObject *item = get<PString>(2);
			item->cnv_integer(port);
		}
		if ( udp_send( baddr, static_cast<int>(port), buf, sizeof(buf)) < 0 ) {
			return RERR_Communication(excp);
		}

		return true;
	}
};

struct ExecContext_SendMail : public ExecContextRoot {
	ExecContext_SendMail(ExecContext *p_, void *f_, const PPredicate *pred_, VLocal *l) : ExecContextRoot(p_, f_, pred_, l) {}
	virtual bool first(PException &excp) { 
		if ( get_gc()->issandbox() ) return RERR_Sandbox(excp);
		if ( pred->size() < 7 ) return RERR_argment_number(excp, 7);
		const PString *smtpserver = get<PString>(0);
		if ( !smtpserver ) return RERR_argment_type( excp, 0);
		const PString *to = get<PString>(1);
		if ( !to ) return RERR_argment_type( excp, 1);
		const PString *cc = get<PString>(2);
		if ( !cc ) return RERR_argment_type( excp, 2);
		const PString *bcc = get<PString>(3);
		if ( !bcc ) return RERR_argment_type( excp, 3);
		const PString *from = get<PString>(4);
		if ( !from ) return RERR_argment_type( excp, 4);
		const PString *subject = get<PString>(5);
		if ( !subject ) return RERR_argment_type( excp, 5);
		const PString *text = get<PString>(6);
		if ( !text ) return RERR_argment_type( excp, 6);
		string charset("utf-8");
		const PString *cset = 0;
		if ( pred->size() > 7 ) cset = get<PString>(7);
		if ( cset ) {
			charset = cset->value;
		}

		if ( !sendmail( smtpserver->value, to->value, cc->value, bcc->value, from->value, subject->value, text->value, charset ) ) {
			return RERR_Communication(excp);
		}
		return true;
	}
};


#endif
