package org.kne.cloud.network.ipv6;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Arrays;

import org.kne.cloud.network.NetworkPacket;
import org.kne.cloud.network.ipv6.IPv6Packet;
import org.kne.cloud.network.ipv6.IPv6Packet.IPv6Payload;
import org.kne.io.KNEChannels;

public class ICMPv6Packet extends IPv6Payload {
    public static final int ICMPv6_PROTOCOL_NUMBER = 58;
    public static final int ICMPv6_HEADER_LENGTH = 8; // ICMPv6基础头部固定8字节
    
    // ICMPv6类型常量
    public static final int TYPE_DESTINATION_UNREACHABLE = 1;
    public static final int TYPE_PACKET_TOO_BIG = 2;
    public static final int TYPE_TIME_EXCEEDED = 3;
    public static final int TYPE_PARAMETER_PROBLEM = 4;
    public static final int TYPE_ECHO_REQUEST = 128;
    public static final int TYPE_ECHO_REPLY = 129;
    public static final int TYPE_POSTCARD = 253;
    
    private ByteBuffer header;
    private ByteBuffer data;
	public void setData(ByteBuffer data) {
		this.data = data;
	}

	private boolean isDefault;

    protected ICMPv6Packet(ByteBuffer header,boolean isDefault) {
        super(ICMPv6_PROTOCOL_NUMBER);
        this.header=header;
        this.isDefault=isDefault;
        
    }

    public ICMPv6Packet(int type, int code) {
    	this(type,code,true);
    }
    
    protected ICMPv6Packet(int type, int code,boolean isDefault) {
        super(ICMPv6_PROTOCOL_NUMBER);
        header = NetworkPacket.bufferAllocator.allocate(ICMPv6_HEADER_LENGTH);
        getHeader().put((byte) type);
        getHeader().put((byte) code);
        // 校验和字段初始为0，后续计算
        getHeader().putChar((char) 0);
        this.isDefault=isDefault;
        if(isDefault) {
        data = NetworkPacket.bufferAllocator.allocate(65535);
        }
    }
    
    @Override
    public long getTotalLength() {
        return data != null ? data.limit() + ICMPv6_HEADER_LENGTH : ICMPv6_HEADER_LENGTH;
    }

  

    @Override
    public void writeToChannel(WritableByteChannel dto) throws IOException {
        // 写入头部和数据
        dto.write(getHeader().slice(0, ICMPv6_HEADER_LENGTH));
        if ( isDefault) {
            dto.write(data.slice(0, data.limit()));
        }
    }

    @Override
    public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
        // 读取ICMPv6头部
        getHeader().limit(ICMPv6_HEADER_LENGTH);
        KNEChannels.readFully(din, getHeader());
        getHeader().flip();
        // 读取数据部分（总长度减去头部长度）
        long dataLength = length - ICMPv6_HEADER_LENGTH;
        
        if (dataLength < 0) {
            throw new IOException("Invalid ICMPv6 packet: total length < header length");
        }
        
        if (isDefault) {
            data = NetworkPacket.bufferAllocator.allocate((int) dataLength);
            KNEChannels.readFully(din, data);
            data.flip();
        } 
    }

    @Override
    protected boolean needEndPosition() {
        return true;
    }

    /**
     * 获取ICMPv6类型
     */
    public int getType() {
        return getHeader().get(0) & 0xFF;
    }

    /**
     * 设置ICMPv6类型
     */
    public void setType(int type) {
        getHeader().put(0, (byte) type);
    }

    /**
     * 获取ICMPv6代码
     */
    public int getCode() {
        return getHeader().get(1) & 0xFF;
    }

    /**
     * 设置ICMPv6代码
     */
    public void setCode(int code) {
        getHeader().put(1, (byte) code);
    }

    /**
     * 获取校验和
     */
    public int getChecksum() {
        return getHeader().getChar(2);
    }

    /**
     * 设置校验和
     */
    public void setChecksum(int checksum) {
        getHeader().putChar(2, (char) checksum);
    }

    /**
     * 获取消息体（不同类型ICMPv6消息的特定字段）
     * 对于Echo请求/回复，这是标识符和序列号
     * 对于错误消息，这是未使用的字段和原始数据包片段
     */
    public int getMessageBody() {
        return getHeader().getInt(4);
    }

    /**
     * 设置消息体
     */
    public void setMessageBody(int messageBody) {
        getHeader().putInt(4, messageBody);
    }

    /**
     * 获取数据部分长度
     */
    public int getDataLength() {
        return data != null ? data.limit() : 0;
    }

    public ByteBuffer getData() {
        return data;
    }

    /**
     * 计算ICMPv6校验和
     * ICMPv6校验和计算包括IPv6伪首部和整个ICMPv6报文
     */
    public int calculateChecksum() {
        if (getParent() == null) {
            throw new IllegalStateException("Parent IPv6 packet required for checksum calculation");
        }

        // 保存原始校验和值
        int originalChecksum = getChecksum();
        
        try {
            // 临时将校验和字段设为0
            setChecksum(0); 


            // IPv6伪首部
            long sum = getParent().calculateAddressChecksum();
         

            // ICMPv6报文长度
            int totalLength = (int) getTotalLength();
            sum += (totalLength >>> 16) + totalLength;
            sum += ICMPv6_PROTOCOL_NUMBER;

            // ICMPv6头部
            ByteBuffer headerCopy = getHeader().duplicate();
            headerCopy.position(0).limit(ICMPv6_HEADER_LENGTH);
            while (headerCopy.remaining() >= 2) {
                sum += headerCopy.getChar();
            }

            // ICMPv6数据
            if (data != null && data.limit() > 0) {
                ByteBuffer dataCopy = data.duplicate();
                dataCopy.position(0);
                
                while (dataCopy.remaining() >= 2) {
                    sum += dataCopy.getChar();
                }
                
                if (dataCopy.remaining() == 1) {
                    sum += (dataCopy.get() & 0xFF) << 8;
                }
            }

            // 折叠进位并取反
            while ((sum >> 16) != 0) {
                sum = (sum & 0xFFFF) + (sum >> 16);
            }

            int checksum = (int) (~sum & 0xFFFF);
            return checksum == 0 ? 0xFFFF : checksum;
            
        } finally {
            // 恢复原始校验和值
            setChecksum(originalChecksum);
        }
    }

    /**
     * 更新校验和
     */
    public void updateChecksum() {
        setChecksum(calculateChecksum());
    }

    /**
     * 验证校验和
     */
    public boolean verifyChecksum() {
        int storedChecksum = getChecksum();
        return storedChecksum == 0 || storedChecksum == calculateChecksum();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ICMPv6 Type:").append(getType())
          .append(" Code:").append(getCode())
          .append(" [Total:").append(getTotalLength())
          .append(", Data:").append(getDataLength()).append("]");
        
        if (data != null && data.limit() > 0) {
            byte[] b = new byte[Math.min(data.limit(), 10)];
            data.get(0, b);
            sb.append(" ").append(Arrays.toString(b));
        }
        
        return sb.toString();
    }


    // 测试方法
    public static void main(String[] args) {
        // 创建一个Echo Request类型的ICMPv6包
        ICMPv6Packet icmpv6 = new ICMPv6Packet(TYPE_ECHO_REQUEST, 0);
        icmpv6.setMessageBody(0x12345678); // 设置标识符和序列号
        
        // 添加一些测试数据
        ByteBuffer testData = NetworkPacket.bufferAllocator.allocate(4);
        testData.put("test".getBytes());
        testData.flip();
        icmpv6.data = testData;
        
        // 创建父IPv6包（用于校验和计算）
        IPv6Packet ipv6 = new IPv6Packet();
        // 这里需要设置源和目的地址，但为了示例简化
        
        icmpv6.setParent(ipv6);
        
        System.out.println("Original checksum: " + icmpv6.calculateChecksum());
        icmpv6.setChecksum(1);
        System.out.println("Checksum verification (should be false): " + icmpv6.verifyChecksum());
        icmpv6.updateChecksum();
        System.out.println("Checksum verification (should be true): " + icmpv6.verifyChecksum());
        System.out.println(icmpv6);
    }

	public ByteBuffer getHeader() {
		return header;
	}

	public static ICMPv6Packet readICMPv6PacketFromChannel(ReadableByteChannel din, int payloadlength) throws EOFException, IOException {
		ByteBuffer bb=NetworkPacket.bufferAllocator.allocate(ICMPv6_HEADER_LENGTH);
		bb.position(0);
		bb.limit(ICMPv6_HEADER_LENGTH);
		KNEChannels.readFully(din, bb);
		int type=bb.get(0)&0xff;
		switch(type) {
			case ICMPv6TimeExceededPacket.TYPE_TIME_EXCEEDED:
				ICMPv6TimeExceededPacket timeExceed=new ICMPv6TimeExceededPacket(bb);
				timeExceed.readFromChannel(din, payloadlength);
			return  timeExceed;
			case ICMPv6PostcardPacket.TYPE_POSTCARD:
				ICMPv6PostcardPacket postcards=new ICMPv6PostcardPacket(bb);
				postcards.readFromChannel(din, payloadlength);
			return  postcards;
			default:
				ICMPv6Packet icmp=new ICMPv6Packet(bb, true);
				icmp.readFromChannel(din, payloadlength);
				return icmp;
		}
	}
}