/** @file
	Parser: @b inet parser class.

	Copyright (c) 2001-2012 Art. Lebedev Studio (http://www.artlebedev.com)
	Author: Alexandr Petrosian <paf@design.ru>(http://paf.design.ru)
*/

#include "pa_vmethod_frame.h"
#include "pa_request.h"

volatile const char * IDENT_INET_C="$Id: inet.C,v 1.6 2012-04-19 19:41:29 moko Exp $";

class MInet: public Methoded {
public:
	MInet();
};

// global variables

DECLARE_CLASS_VAR(inet, new MInet, 0);


static void _ntoa(Request& r, MethodParams& params){
	unsigned long l=(unsigned long)trunc(params.as_double(0, "parameter must be expression", r));
	static const int ip_cstr_bufsize=3*4+3+1+1;
	char* ip_cstr=new(PointerFreeGC) char[ip_cstr_bufsize];

	snprintf(ip_cstr, ip_cstr_bufsize, "%u.%u.%u.%u",
				(l>>24) & 0xFF,
				(l>>16) & 0xFF,
				(l>>8) & 0xFF,
				l & 0xFF);

	r.write_no_lang(*new String(ip_cstr));
}

static void _aton(Request& r, MethodParams& params){
	const String ip=params.as_string(0, PARAMETER_MUST_BE_STRING);
	if(ip.is_empty())
		throw Exception(PARSER_RUNTIME,
			0,
			"IP address must not be empty.");

	const char* ip_cstr=ip.cstr();
	ulong result=0;
	uint byte_value=0;
	uint dot_cnt=0;
	bool byte_start=true;
	bool err=false;
	const char* p=ip_cstr;
	while(char c=*p++){
		int digit=(int)(c-'0');	// assume ascii
		if(digit>=0 && digit<=9){
			byte_start=false;
			if((byte_value=byte_value*10+(uint)digit) > 255){
				err=true;
				break;
			}
		} else if(c=='.'){
			if(byte_start){ // two dots in row or IP started with dot
				err=true;
				break;
			} else {
				byte_start=true;
				dot_cnt++;
				result=(result << 8)+(ulong)byte_value;
				byte_value=0;
			}
		} else { // invalid char
			err=true;
			break;
		}
	}

	if(err || dot_cnt!=3 || byte_start){
		throw Exception(PARSER_RUNTIME,
			0,
			"Invalid IP address '%s' specified.", ip_cstr);
	} else {
		result=(result << 8)+(ulong)byte_value;
		r.write_no_lang(*new VDouble(result));
	}
}

// constructor
MInet::MInet(): Methoded("inet") {
	add_native_method("ntoa", Method::CT_STATIC, _ntoa, 1, 1);
	add_native_method("aton", Method::CT_STATIC, _aton, 1, 1);

	// @todo: gethostbyname, gethostbyaddr
}
