#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import optparse
import time
import re 
import os 
import struct 
reload(sys)
sys.setdefaultencoding('utf-8')

def get_chr( hex_re ):
	hex=hex_re.group();
	value=eval ("0x%s" % (hex)) ;
	return chr(value); 

def hex2bin(hexlist):
	phex=re.compile(r'(\w\w)')
	return phex.sub(get_chr , hexlist );
def print_err(msg):
	print "\033[1m\033[41m%s\033[0m"%msg

def print_succ(msg):
	print "\033[1m\033[42m%s\033[0m"%msg

def main(argv):
	p = optparse.OptionParser(
			description='show proto pkg ',
			prog='cat_proto',
			version='%prog 0.1',
			usage='%prog 16 00 00 00 C9 00 0C 00 E9 03 00 00 00 00 58 C3 00 00 00 00 00 00 ' )
	p.add_option('-p', '--py_code_file', default="test_online_proto.py", help='生成的proto文件名:如mole_proto.py 必须和cat_proto放在同一个目录下'  );
	p.add_option('-l', '--header_len', default="18", help='报头长度'  );
	p.add_option('-c', '--cmd_start_pos', default="8", help='命令号所在的位置'  );
	p.add_option('-H', '--cmd_hex_show', action ='store_true',  help='命令号是否要十六进制显示'  );
	p.add_option('-B', '--hex_show_buf', action ='store_true',  help='是否将buf打印成16进制'  );
	p.add_option('-t', '--cmd_type', default="uint16", help='命令号类型:uint16 or int' );
	p.add_option('-i', '--is_in_pkg', action ='store_true',   help='是否是输入报文,如果不是则为输出报文' );
	p.add_option('-f', '--header_fmt_list', default="userid,L,14,4|seqid,L,4,4|result,l,10,4",  help='头部数据解析列表,\n名字,解析方式,开始位置,长度.\n 如userid,L,14,4|seqid,L,4,4|result,l,10,4 ' );
	p.add_option('-b', '--big_endian', action ='store_true',  help='是不是大端协议'  );

	options, arguments = p.parse_args()	


	if options.big_endian: 
		bigend_str=">";
	else:
		bigend_str="<";


	file_name=options.py_code_file	
	dirname=os.path.dirname(file_name );
	sys.path.append(dirname);
	basename=os.path.basename(file_name);

	#得到模块名,并导入
	moudle_name=basename.split(".")[0];

	exec ("import %s"%moudle_name );

	exec ("create_time=%s.create_time"%moudle_name );


	header_len=int(options.header_len);

	ll=""
	for v in arguments :
		ll+=v 

	if len(ll)<header_len*2:
		p.print_help()
		if (len(ll)!=0 ):
			print_err( "长度还不够报文头部")
		sys.exit(0);


	#输入命令号和长度		
	buf=hex2bin(ll);
	pri_buf=buf[header_len:]

	pkg_len=struct.unpack(bigend_str+"L" ,buf[0:4])[0];
	if (pkg_len<>len(buf) ):
		print_err(   "出错：报文头部标志的长度(%d) != 命令行传入的报文长度(%d) " %(pkg_len,len(buf) ))

	cmdid=0;
	cmd_start_pos=int( options.cmd_start_pos);
	if  options.cmd_type=="uint16" :
		cmdid=struct.unpack(bigend_str+"H" ,buf[cmd_start_pos:cmd_start_pos+2])[0];
	else:
		cmdid=struct.unpack(bigend_str+"L" ,buf[cmd_start_pos:cmd_start_pos+4])[0];


	print "\n\nXML生成时间:\t", create_time 

	if options.cmd_hex_show:
		cmdstr="0x%x"%(cmdid);
	else:
		cmdstr="%d"%(cmdid);


	print "报文总长:\t[%d]"%(pkg_len)
	print "命令号:\t\t[%s]"%(cmdstr)
	fmt_arr=options.header_fmt_list.split("|");
	for item in fmt_arr:
		if (item!=""):
			field_arr=item.split(",")
			field_name=field_arr[0]		
			field_type=field_arr[1]		
			field_start=int(field_arr[2])
			field_end=field_start+int(field_arr[3])
			value=struct.unpack(bigend_str+field_type, buf[field_start:field_end] )[0];
			print field_name+":\t\t["+str(value)+"]";

	import	proto_base
	exec ("%s.init_cmd_map()"%moudle_name );
	try:
		exec ("struct_item=%s.cmd_map[%d]"%(moudle_name,cmdid));
	except:
		print_err( "出错：没有找到命令为[%s]的信息"%(cmdstr))
		sys.exit(1)
		pass	
	print "协议名称:\t",struct_item[1]	
	#结构体所在位置
	if options.is_in_pkg: 
		struct_index=2;
	else: 
		struct_index=3;

	struct_class=struct_item[struct_index]
	if struct_class ==None:
		struct_class=proto_base.Cmessage
	print "私有结构:\t",str(struct_class).split(".")[-1]

	ba=proto_base.Cbyte_array()
	ba.init_read_mode(pri_buf);
	ba.set_is_big_endian(options.big_endian);

	pkg_obj=struct_class()

	ret=pkg_obj.read_from_buf(ba);
	if not ret :
		try:
			pkg_obj.echo(True ,"    ")
		except:
			pass;
		print "---------------------------------------\n"
		print_err( "没有解析成功,以上是解释中得到的数据,None对应的字段为出错字段")
	else:
		if ba.is_end():
			pkg_obj.echo(True,"    ")
			print "---------------------------------------\n"
			print_succ("解析成功");
		else:
			pkg_obj.echo(True ,"    ")
			print "---------------------------------------\n"
			left_len=ba.get_left_len();
			print_err( "出错：有剩余数据长度：%d,[%s]"%(left_len, ll[-left_len*2:] ))
	print "---------------------------------------\n"
	
if __name__ == "__main__":
	main(sys.argv)

