package com.vpnmore.shadowsocks.tcpip;

public class IPHeader {

	public static final short IP = 0x0800;
	public static final byte ICMP = 1;
	public static final byte TCP = 6;
	public static final byte UDP = 17;

	static final byte offset_ver_ihl = 0; // 0: Version (4 bits) + Internet header length (4// bits)
	static final byte offset_tos = 1; // 1: Type of service
	static final short offset_tlen = 2; // 2: Total length
	static final short offset_identification = 4; // :4 Identification
	static final short offset_flags_fo = 6; // 6: Flags (3 bits) + Fragment offset (13 bits)
	static final byte offset_ttl = 8; // 8: Time to live
	public static final byte offset_proto = 9; // 9: Protocol
	static final short offset_crc = 10; // 10: Header checksum
	public static final int offset_src_ip = 12; // 12: Source address
	public static final int offset_dest_ip = 16; // 16: Destination address
	static final int offset_op_pad = 20; // 20: Option + Padding

	public byte[] m_Data;
	public int m_Offset;

	public IPHeader(byte[] data, int offset) {
		this.m_Data = data;
		this.m_Offset = offset;
	}
	
	public void Default() {
		setHeaderLength(20);
		setTos((byte)0);
		setTotalLength(0);
		setIdentification(0);
		setFlagsAndOffset((short)0);
		setTTL((byte)64);
	}

	public int getDataLength() {
		return this.getTotalLength()-this.getHeaderLength();
	}

	public int getHeaderLength() {
		return (m_Data[m_Offset + offset_ver_ihl] & 0x0F) * 4;
	}
	
	public void setHeaderLength(int value) {
		m_Data[m_Offset+offset_ver_ihl]= (byte)((4<<4)|(value/4));
	}

	public byte getTos() {
		return m_Data[m_Offset + offset_tos];
	}

	public void setTos(byte value) {
		m_Data[m_Offset + offset_tos] = value;
	}
	
	public int getTotalLength() {
		return CommonMethods.readShort(m_Data, m_Offset + offset_tlen) & 0xFFFF;
	}

	public void setTotalLength(int value) {
		CommonMethods.writeShort(m_Data, m_Offset + offset_tlen, (short) value);
	}
	
	public int getIdentification() {
		return CommonMethods.readShort(m_Data, m_Offset + offset_identification) & 0xFFFF;
	}

	public void setIdentification(int value) {
		CommonMethods.writeShort(m_Data, m_Offset + offset_identification, (short) value);
	}

	public short getFlagsAndOffset() {
		return CommonMethods.readShort(m_Data, m_Offset + offset_flags_fo);
	}

	public void setFlagsAndOffset(short value) {
		CommonMethods.writeShort(m_Data, m_Offset + offset_flags_fo, value);
	}
	
	public byte getTTL() {
		return m_Data[m_Offset + offset_ttl];
	}

	public void setTTL(byte value) {
		m_Data[m_Offset + offset_ttl] = value;
	}
	
	public byte getProtocol() {
		return m_Data[m_Offset + offset_proto];
	}

	public void setProtocol(byte value) {
		m_Data[m_Offset + offset_proto] = value;
	}

	public short getCrc() {
		return CommonMethods.readShort(m_Data, m_Offset + offset_crc);
	}

	public void setCrc(short value) {
		CommonMethods.writeShort(m_Data, m_Offset + offset_crc, value);
	}

	public int getSourceIP() {
		return CommonMethods.readInt(m_Data, m_Offset + offset_src_ip);
	}

	public void setSourceIP(int value) {
		CommonMethods.writeInt(m_Data, m_Offset + offset_src_ip, value);
	}

	public int getDestinationIP() {
		return CommonMethods.readInt(m_Data, m_Offset + offset_dest_ip);
	}

	public void setDestinationIP(int value) {
		CommonMethods.writeInt(m_Data, m_Offset + offset_dest_ip, value);
	}
	
	@Override
	public String toString() {
		return String.format("%s->%s Pro=%s,HLen=%d", CommonMethods.ipIntToString(getSourceIP()),CommonMethods.ipIntToString(getDestinationIP()),getProtocol(),getHeaderLength());
	}

}
