package net.jueb.util4j.beta.serializable.nmap.type;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import net.jueb.util4j.beta.serializable.nmap.falg.Flag;
import net.jueb.util4j.beta.serializable.nmap.util.NMapConvert;
import net.jueb.util4j.beta.tools.convert.typebytes.TypeBytesInputStream;

/**
 * 内部封装一个hashmap
 * @author Administrator
 *
 */
public class NMap extends NType<Map<NType<?>,NType<?>>> implements Map<NType<?>, NType<?>>{

	public NMap() {
		super(new LinkedHashMap<NType<?>, NType<?>>(), Flag.Head.NMap, Flag.End.NMap);
	}
	
	@Override
	public byte[] getBytes() {
		int size=obj.size();//加入当前map的第一层大小
		return addByteArray(getFlagHead(),tb.getBytes(size),getObjectBytes(),getFlagEnd());
	}
	
	@Override
	public byte[] getObjectBytes() {
		byte[] data=new byte[]{};
		Iterator<Entry<NType<?>, NType<?>>> it=obj.entrySet().iterator();
		//entrySet比keySet快,而且不易取到null的值
		while(it.hasNext())
		{
			Entry<NType<?>, NType<?>> kv=it.next();
			data=addByteArray(data,kv.getKey().getBytes(),kv.getValue().getBytes());
		}
		/**
		for(NType<?> key:obj.keySet())
		{//不管key和value是什么类型,只管取数据,如果类型是NMap,会自动嵌套调用它getBytes
			data=addByteArray(data,key.getBytes(),obj.get(key).getBytes());
		}
		 */
		return data;
	}
	
	@Override
	public String getString() {
		return obj.toString();
	}

	@Override
	public int size() {
		return obj.size();
	}

	@Override
	public boolean isEmpty() {
		return obj.isEmpty();
	}

	@Override
	public boolean containsKey(Object key) {
		return obj.containsKey(key);
	}

	@Override
	public boolean containsValue(Object value) {
		return obj.containsValue(value);
	}

	@Override
	public NType<?> get(Object key) {
		return obj.get(key);
	}

	@Override
	public NType<?> put(NType<?> key, NType<?> value) {
		if(key==this || value==this)
		{
			throw new RuntimeException("不能将map自身作为key或者value");
		}
		return obj.put(key, value);
	}

	@Override
	public NType<?> remove(Object key) {
		return obj.remove(key);
	}

	@Override
	public void putAll(Map<? extends NType<?>, ? extends NType<?>> m) {
		obj.putAll(m);
	}

	@Override
	public void clear() {
		obj.clear();
	}

	@Override
	public Set<NType<?>> keySet() {
		return obj.keySet();
	}

	@Override
	public Collection<NType<?>> values() {
		return obj.values();
	}

	@Override
	public Set<java.util.Map.Entry<NType<?>, NType<?>>> entrySet() {
		return obj.entrySet();
	}

	private NType<?> readKey(TypeBytesInputStream ti) throws Exception
	{
		String index=Integer.toHexString(ti.getReadIndex());
		byte keyHead=ti.readNextByte();
		log(index+"-StartReadkey,flagHead:"+keyHead);
		NType<?> key=readNTypeByHeadsMap(ti, keyHead);
		if(key==null)
		{
			throw new RuntimeException("decoder key null atReadIndex:"+index);
		}
		log(Integer.toHexString(ti.getReadIndex())+"-ReadkeyEnd:"+key);
		return key;
	}
	private NType<?> readValue(TypeBytesInputStream ti) throws Exception
	{
		String index=Integer.toHexString(ti.getReadIndex());
		byte valueHead=ti.readNextByte();
		log(index+"-StartReadValue,flagHead:"+valueHead);
		NType<?> value=readNTypeByHeadsMap(ti, valueHead);
		if(value==null)
		{
			throw new RuntimeException("decoder value null atReadIndex:"+index);
		}
		log(Integer.toHexString(ti.getReadIndex())+"-ReadVauleEnd:"+value);
		return value;
	}
	/**
	 * 解析NType对象到map
	 * @param map
	 * @param ti
	 * @throws Exception 
	 */
	private void readNTypesToNMap(final NMap map,TypeBytesInputStream ti) throws Exception
	{
		int num=ti.readInt();
		for(int i=0;i<num;i++)
		{	
			map.put(readKey(ti), readValue(ti));
		}
	}
	
	/**
	 * 注册的NTYpe类型
	 */
	private final static HashMap<Byte, NType<?>> registHeads=new HashMap<Byte, NType<?>>();
	
	{	registNType(this);//默认注册自身类型
		registNType(new NNull());
		registNType(new NBoolean(true));
		registNType(new NBoolean(false));
		registNType(new NInteger(1111));
		registNType(new NUTF8String("UTF8"));
		registNType(new NUTF16LEString("UTF16L4"));
		registNType(new NByteArray(new byte[]{1,0,1,0}));
		//TODO
	}
	
	public final HashMap<Byte, NType<?>> getRegisgHeads()
	{
		return registHeads;
	}
	
	/**
	 * 注册NType类型 决定了该map能解码出哪些对象类型
	 * @param ntype
	 */
	public final static void registNType(NType<?> ntype)
	{
		if(ntype!=null)
		{
			registHeads.put(ntype.getFlagHead(), ntype);
		}
	}
	
	private NType<?> readNTypeByHeadsMap(TypeBytesInputStream ti,byte head) throws Exception
	{
		if(!registHeads.containsKey(head))
		{
			String index=Integer.toHexString(ti.getReadIndex());
			throw new RuntimeException("Known Head by readIndex:"+index);
		}
		return registHeads.get(head).decoderByStream(ti);
	}
	/**
	 * 根据头得到类型
	 * @param ti 这里的ti必须是没有读过head的
	 * @param head
	 * @return
	 * @throws IOException 
	 */
	protected NType<?> readNTypeByHead(TypeBytesInputStream ti,byte head) throws Exception
	{
		NType<?> type=null;
		switch (head) {
		case Flag.Head.NNull:
			type=new NNull().decoderByStream(ti);
			break;
		case Flag.Head.NTrue:
			type=new NBoolean(true).decoderByStream(ti);
			break;
		case Flag.Head.NFalse:
			type=new NBoolean(false).decoderByStream(ti);
			break;
		case Flag.Head.NInteger:
			type=new NInteger(0).decoderByStream(ti);
			break;
		case Flag.Head.NUTF8String:
			type=new NUTF8String("").decoderByStream(ti);
			break;
		case Flag.Head.NUTF16LEString:
			type=new NUTF16LEString("").decoderByStream(ti);
			break;
		case Flag.Head.NMap:
			type=new NMap().decoderByStream(ti);
			break;
		case Flag.Head.NByteArray:
			type=new NByteArray(new byte[]{}).decoderByStream(ti);
			break;
		default:
			break;
		}
		return type;
	}

	@Override
	protected NType<Map<NType<?>, NType<?>>> decoder(TypeBytesInputStream ti)throws Exception {
		if(checkHead(ti))
		{
			final NMap map=new NMap();
			readNTypesToNMap(map, ti);
			return map;
		}
		return null;
	}
	
	public final void saveTo(File file) throws IOException
	{
		FileOutputStream fos=new FileOutputStream(file);
		fos.write(getBytes());
		fos.flush();
		fos.close();
	}
	/**
	 * 根据文件解码一个新nmap对象
	 * @param file
	 * @return
	 * @throws Exception
	 */
	public final NMap load(File file) throws Exception
	{
		if(file.exists())
		{
			FileInputStream fis=new FileInputStream(file);
			BufferedInputStream bis=new BufferedInputStream(fis);
			ByteArrayOutputStream bos=new ByteArrayOutputStream();//定义一个内存输出流
			int i=-1;
			while(true)
			{
				i=bis.read();
				if(i==-1)
				{
					break;
				}
				bos.write(i);//保存到内存数组
			}
			bos.flush();
			bos.close();
			bis.close();
			return (NMap) decoder(bos.toByteArray());
		}
		return null;
	}
	
	public final NMapConvert mapConvert=new NMapConvert(this);
	
	/**
	 * 解码当前NMap为普通对象map
	 * 但是无法从map转换到nmap,比如string这种同类型多形式的
	 * @return
	 */
	public final TreeMap<Object, Object> toMap()
	{
		TreeMap<Object, Object> map=new TreeMap<Object, Object>();
		mapConvert.toTreeMap(this, map);
		return map;
	}
}