package tctable;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;

public class TCField
{
	//Типы данных
	static int BD_UINT1 = 0; //1 байт без знаковый
	static int BD_UINT2 = 1; //2 байта без знаковый
	static int BD_UINT4 = 3; //4 байта без знаковый
	static int BD_INT1 = 10; //1 байт со знаковый
	static int BD_INT2 = 11; //2 байта со знаковый
	static int BD_INT4 = 13; //4 байта со знаковый
	static int BD_INT8 = 17; //8 байт со знаковый
	static int BD_FLOAT4 = 20; //4 байта
	static int BD_FLOAT8 = 22; //8 байт double
	static int BD_UTF8_1 = 100; //100 - utf8_1 string 1й байт размер строки в байтах
	static int BD_UTF8_2 = 101; //101 - utf8_2 string 1х 2 байта размер строки в байтах
	static int BD_UTF8_4 = 102; //102 - utf8_4 string 1х 4 байта размер строки в байтах
	static int BD_BLOB_2 = 141;
	static int BD_BLOB_4 = 143; //Двоичные данные uint4 количество байт

	public String name=""; //Название столбца
	public int type=-1; //Тип данных
	public byte[] value=null; //Запакованые данные

	public TCField(String name, int type)
	{
		this.name=name;
		this.type=type;
	}
	
	public TCField(String name, String type)
	{
		this.name=name;
		//this.type=type;
		
		this.type= TCField.BD_UTF8_2; //Defalt type
		//From PostgreSQL and MySQL
		if(type.equals("bool") || type.equals("tinyint")) { this.type= TCField.BD_UINT1; } else
		if(type.equals("int4") || type.equals("int") || type.equals("serial") || type.equals("bigint")) { this.type= TCField.BD_INT4; } else //bigint немного неправильно потому что это 64 бита но для андрод приложения не сделал
		if(type.equals("int8")) { this.type= TCField.BD_INT8; } else
		if(type.equals("float4") || type.equals("float")) { this.type= TCField.BD_FLOAT4; } else
		if(type.equals("float8") || type.equals("NUMBER")) { this.type= TCField.BD_FLOAT8; } else
		if(type.equals("varchar") || type.equals("VARCHAR2")) { this.type= TCField.BD_UTF8_2; } else
		if(type.equals("text")) { this.type= TCField.BD_UTF8_4; } else
		if(type.equals("bytea") || type.equals("longblob")) { this.type= TCField.BD_BLOB_4; } else
		if(type.equals("timestamptz")) { this.type= TCField.BD_UTF8_1; } else
		if(type.equals("timestamp")) { this.type= TCField.BD_UTF8_1; } else
		if(type.equals("date")) { this.type= TCField.BD_UTF8_1; }
	}
	

	private int byteArrayToInt(byte[] b, int start, int length)
	{
		int dt = 0;
		if ((b[start] & 0x80) != 0)
			dt = Integer.MAX_VALUE;
		for (int i = 0; i < length; i++)
			dt = (dt << 8) + (b[start++] & 255);
		return dt;
	}

/*
	private byte[] intToByteArray(int n, int byteCount)
	{
		byte[] res = new byte[byteCount];
		for (int i = 0; i < byteCount; i++)
			res[byteCount - i - 1] = (byte) ((n >> i * 8) & 255);
		return res;
	}
*/

	//Прочитать значение из потока в соответствии с типом
	public void ReadValue(InputStream fileHandle) throws IOException
	{
		if(this.type== TCField.BD_UINT1)
		{
			value=new byte[1];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_UINT2)
		{
			value=new byte[2];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_UINT4)
		{
			value=new byte[4];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_INT1)
		{
			value=new byte[1];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_INT2)
		{
			value=new byte[2];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_INT4)
		{
			value=new byte[4];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_INT8)
		{
			value=new byte[8];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_FLOAT4)
		{
			value=new byte[4];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_FLOAT8)
		{
			value=new byte[8];
			fileHandle.read(value);
		}else
		if(this.type== TCField.BD_UTF8_1)
		{
			byte[] s=new byte[1];
			fileHandle.read(s);
			if(s[0]==0) value=null;
			else
			{
				value=new byte[s[0]];
				fileHandle.read(value);
			}
		}else
		if(this.type== TCField.BD_UTF8_2)
		{
			int s= Tools.readUShort(fileHandle);
			if(s==0) value=null;
			else
			{
				value=new byte[s];
				fileHandle.read(value);
			}
		}else
		if(this.type== TCField.BD_BLOB_4)
		{	
			byte[] ss=new byte[4];
			fileHandle.read(ss);
			int s=byteArrayToInt(ss, 0, 4);
			if(s==0) value=null;
			else
			{
				value=new byte[s];
				fileHandle.read(value);
			}
		}
	}

	public String getStrVal()
	{
		String result=null;
		try
		{
			if(value==null) result = "";
			if(type== TCField.BD_UINT1) result = String.valueOf(getUByteVal());
			if(type== TCField.BD_UINT4) result = String.valueOf(getUIntVal());
			if(type== TCField.BD_INT4) result = String.valueOf(getIntVal());
			if(type== TCField.BD_FLOAT4) result = String.valueOf(getFloatVal());
			if(type== TCField.BD_UTF8_1 || type== TCField.BD_UTF8_2)
			{
				result = new String(value, "UTF8");
			}
		} catch (Exception e) {}
		return result;
	}

	public int getIntVal()
	{
		int i1 = value[0] & 0xff;
		int i2 = value[1] & 0xff;
		int i3 = value[2] & 0xff;
		int i4 = value[3] & 0xff;

		int val = i4 << 24 | i3 << 16 | i2 << 8 | i1;

		return val;
	}

	/** Пока не использую но если буду использовать протестировать с большими числами */
	public long getUIntVal()
	{
		long ch1, ch2, ch3, ch4, count;
		ch1 = value[0] & 0xff;
		ch2 = value[1] & 0xff;
		ch3 = value[2] & 0xff;
		ch4 = value[3] & 0xff;
		count = (ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1;
		return count;
	}

	public short getUByteVal()
	{
		return (short) (value[0] & 0xff);
	}

	public float getFloatVal()
	{
		float f;
		int ch1, ch2, ch3, ch4, count;
		ch1 = value[0] & 0xFF;
		ch2 = value[1] & 0xFF;
		ch3 = value[2] & 0xFF;
		ch4 = value[3] & 0xFF;

		count = (ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1;
		f = Float.intBitsToFloat(count);
		return f;
	}
	
	public boolean setValue(String val)
	{
		boolean result=true;
		if(val==null)
		{	value=null;
		}else if(type== TCField.BD_UINT1)
		{
			if(val.equals("f") || val.equals("false")) val="0";
			else if(val.equals("t") || val.equals("true")) val="1";
				
			int v= Integer.parseInt(val);
			if(v<0 ||  v>255) result=false;
			value=new byte[1];
			value[0]=(byte)v;
		}else if(type== TCField.BD_UINT2)
		{
			int v= Integer.parseInt(val);
			if(v<0 ||  v>65535) result=false;			
			value=new byte[2];
			value[0] = (byte)((v & 0x000000ff));
			value[1] = (byte)((v & 0x0000ff00) >> 8);
		}else if(type== TCField.BD_UINT4)
		{
			long v= Long.parseLong(val);
			value=new byte[4];
			value[0] = (byte)((v & 0x000000ff));
			value[1] = (byte)((v & 0x0000ff00) >> 8);
			value[2] = (byte)((v & 0x00ff0000) >> 16);
			value[3] = (byte)((v & 0xff000000) >> 24);			
		}else if(type== TCField.BD_INT1)
		{
			int v= Integer.parseInt(val);
			value=new byte[1];
			value[0]=(byte)v;
		}else if(type== TCField.BD_INT2)
		{
			int v= Integer.parseInt(val);
			value=new byte[2];
			value[0] = (byte)((v & 0x000000ff));
			value[1] = (byte)((v & 0x0000ff00) >> 8);
		}else if(type== TCField.BD_INT4)
		{
			long v= Long.parseLong(val);
			value=new byte[4];
			value[0] = (byte)((v & 0x000000ff));
			value[1] = (byte)((v & 0x0000ff00) >> 8);
			value[2] = (byte)((v & 0x00ff0000) >> 16);
			value[3] = (byte)((v & 0xff000000) >> 24);			
		}else if(type== TCField.BD_INT8)
		{
			long v= Long.parseLong(val);
			value=new byte[8];
			value[0] = (byte) (v & 0x00000000000000ffl);
			value[1] = (byte)((v & 0x000000000000ff00l) >> 8);
			value[2] = (byte)((v & 0x0000000000ff0000l) >> 16);
			value[3] = (byte)((v & 0x00000000ff000000l) >> 24);
			value[4] = (byte)((v & 0x000000ff00000000l) >> 32);
			value[5] = (byte)((v & 0x0000ff0000000000l) >> 40);
			value[6] = (byte)((v & 0x00ff000000000000l) >> 48);
			value[7] = (byte)((v & 0xff00000000000000l) >> 56);
			
		}else if(type== TCField.BD_FLOAT4)
		{
			Float v= Float.parseFloat(val);
			int iv= Float.floatToIntBits(v);
			
			value=new byte[4];
			value[0] = (byte)((iv & 0x000000ff));
			value[1] = (byte)((iv & 0x0000ff00) >> 8);
			value[2] = (byte)((iv & 0x00ff0000) >> 16);
			value[3] = (byte)((iv & 0xff000000) >> 24);
		}else if(type== TCField.BD_FLOAT8)
		{
			Double v= Double.parseDouble(val);
			long iv= Double.doubleToLongBits(v);

			value=new byte[8];
			value[0] = (byte)((iv & 0x00000000000000ffl));
			value[1] = (byte)((iv & 0x000000000000ff00l) >> 8);
			value[2] = (byte)((iv & 0x0000000000ff0000l) >> 16);
			value[3] = (byte)((iv & 0x00000000ff000000l) >> 24);
			value[4] = (byte)((iv & 0x000000ff00000000l) >> 32);
			value[5] = (byte)((iv & 0x0000ff0000000000l) >> 40);
			value[6] = (byte)((iv & 0x00ff000000000000l) >> 48);
			value[7] = (byte)((iv & 0xff00000000000000l) >> 56);

		}else if(type== TCField.BD_UTF8_1)
		{
			value=null;
			if(val!=null && !val.equals(""))
			{
				byte[] b=null;
				try {
					b = val.getBytes("UTF-8");
				} catch (UnsupportedEncodingException e) {
					result=false;
				}
				if(b!=null)
				{			
					int len=b.length;
					if(len>255) len=255;	
					value=new byte[len+1];
					value[0]=(byte)len;
					for(int i=0;i<len;i++)
					{
						value[i+1]=b[i];
					}
				}
			}			
		}else if(type== TCField.BD_UTF8_2)
		{
			value=null;
			if(val!=null && !val.equals(""))
			{
				byte[] b=null;
				try {
					b = val.getBytes("UTF-8");
				} catch (UnsupportedEncodingException e) {
					result=false;
				}
				if(b!=null)
				{			
					int len=b.length;
					if(len>65535) len=65535;	
					value=new byte[len+2];
					value[0]=(byte) (len & 0x000000ff);
					value[1]=(byte)((len & 0x0000ff00) >> 8);
					for(int i=0;i<len;i++)
					{
						value[i+2]=b[i];
					}
				}
			}			
		}else if(type== TCField.BD_UTF8_4)
		{
			value=null;
			if(val!=null && !val.equals(""))
			{
				byte[] b=null;
				try {
					b = val.getBytes("UTF-8");
				} catch (UnsupportedEncodingException e) {
					result=false;
				}
				if(b!=null)
				{			
					int len=b.length;
					value=new byte[len+4];					
					value[0]=(byte) (len & 0x000000ff);
					value[1]=(byte)((len & 0x0000ff00) >> 8);
					value[2]=(byte)((len & 0x00ff0000) >> 16);
					value[3]=(byte)((len & 0xff000000) >> 24);
					for(int i=0;i<len;i++)
					{
						value[i+4]=b[i];
					}
				}
			}			
		}
	
/*
		if($this->type==TCField::$BD_BLOB_4)
		{	return pack("I",strlen($value)).$value;
		}
*/
		return result;
	}

}
