package org.kne.cloud.network.ntp;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import java.util.ArrayList;
import java.util.List;

import org.kne.cloud.clock.AdjustedNanoClock;
import org.kne.cloud.clock.HighAccuracyClock;
import org.kne.cloud.clock.NTPTimestamps;
import org.kne.cloud.network.NetworkPacket;
import org.kne.io.KNEChannels;
import org.kne.math.Long128;

public class NTPv4Packet extends NetworkPacket {
	public static final int NTP_VERSION_NUMBER = 4;
    public static final int NTP_DEFAULT_PORT = 123;
    
    public static final int NTP_SYMMETRIC_ACTIVE = 1;

    public static final int NTP_SYMMETRIC_PASSIVE = 2;

    public static final int NTP_CLIENT = 3;

    public static final int NTP_SERVER = 4;
    
    
    public static final int BASE_HEADER_LENGTH = 48; // 基本头部长度
    public static final int AUTH_FIELD_LENGTH = 20;  // 认证字段长度
	private static final Long128 TWO_POW_64 = Long128.ONE.shiftLeft(64);
    
    private ByteBuffer header = NetworkPacket.bufferAllocator.allocate(BASE_HEADER_LENGTH);
    private List<ExtensionField> extensionFields = new ArrayList<>();
    private AuthenticationField authField;
    
    
    private Long128 destinationTimeStamp;
	private HighAccuracyClock clock;
    
    public NTPv4Packet(HighAccuracyClock clock) {
        initializeHeader();
        this.clock=clock;
    }
    
    private void initializeHeader() {
        // 设置默认值：版本4，模式3（客户端）
        header.put((byte) (0x23)); // LI=0, VN=4, Mode=3
        header.put((byte) 0);      // Stratum
        header.put((byte) 0);      // Poll
        header.put((byte) 0);      // Precision
        
        // 根延迟、根分散、参考标识符初始为0
        for (int i = 4; i < 16; i++) {
            header.put((byte) 0);
        }
        
        // 时间戳字段初始为0
        for (int i = 16; i < BASE_HEADER_LENGTH; i++) {
            header.put((byte) 0);
        }
        
        header.flip();
    }

    @Override
    public long getTotalLength() {
        long length = BASE_HEADER_LENGTH;
        
        // 添加扩展字段长度
        for (ExtensionField ext : extensionFields) {
            length += ext.getLength();
        }
        
        // 添加认证字段长度
        if (authField != null) {
            length += AUTH_FIELD_LENGTH;
        }
        
        return length;
    }

    @Override
    public void writeToChannel(WritableByteChannel dto) throws IOException {
        // 写入基本头部
        dto.write(header.slice(0, header.limit()));
        
        // 写入扩展字段
        for (ExtensionField ext : extensionFields) {
            ext.writeToChannel(dto);
        }
        
        // 写入认证字段
        if (authField != null) {
            authField.writeToChannel(dto);
        }
    }

    @Override
    public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
    	destinationTimeStamp=clock.getCurrentTimeNTP128();
        // 读取基本头部
        header.clear().limit(BASE_HEADER_LENGTH);
        KNEChannels.readFully(din, header);
        header.flip();
        
        // 计算剩余长度
        long remaining = length - BASE_HEADER_LENGTH;
        
        // 读取扩展字段（如果有）
        extensionFields.clear();
        while (remaining > 0) {
            // 检查是否是认证字段（认证字段有固定格式）
            if (remaining >= AUTH_FIELD_LENGTH) {
                // 这里简化处理，实际应该根据协议判断
                break;
            }
            
            ExtensionField ext = new ExtensionField();
            ext.readFromChannel(din, remaining);
            extensionFields.add(ext);
            remaining -= ext.getLength();
        }
        
        // 读取认证字段（如果有）
        if (remaining >= AUTH_FIELD_LENGTH) {
            authField = new AuthenticationField();
            authField.readFromChannel(din, AUTH_FIELD_LENGTH);
        }
    }

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

    // ================== NTP 头部字段访问方法 ==================

    public int getLeapIndicator() {
        return (header.get(0) & 0xC0) >> 6;
    }

    public void setLeapIndicator(int li) {
        byte value = header.get(0);
        value = (byte) ((value & 0x3F) | ((li & 0x03) << 6));
        header.put(0, value);
    }

    public int getVersionNumber() {
        return (header.get(0) & 0x38) >> 3;
    }

    public void setVersionNumber(int vn) {
        byte value = header.get(0);
        value = (byte) ((value & 0xC7) | ((vn & 0x07) << 3));
        header.put(0, value);
    }

    public int getMode() {
        return header.get(0) & 0x07;
    }

    public void setMode(int mode) {
        byte value = header.get(0);
        value = (byte) ((value & 0xF8) | (mode & 0x07));
        header.put(0, value);
    }

    public int getStratum() {
        return header.get(1) & 0xFF;
    }

    public void setStratum(int stratum) {
        header.put(1, (byte) stratum);
    }

    public int getPollInterval() {
        return header.get(2);
    }

    public void setPollInterval(int poll) {
        header.put(2, (byte) poll);
    }

    public byte getPrecision() {
        return header.get(3);
    }

    public void setPrecision(byte precision) {
        header.put(3,  precision);
    }

    public int getRootDelay() {
        return header.getInt(4);
    }

    public void setRootDelay(int rootDelay) {
        header.putInt(4, rootDelay);
    }

    public int getRootDispersion() {
        return header.getInt(8);
    }

    public void setRootDispersion(int rootDispersion) {
        header.putInt(8, rootDispersion);
    }

    public int getReferenceIdentifier() {
        return header.getInt(12);
    }

    public void setReferenceIdentifier(int refId) {
        header.putInt(12, refId);
    }

    // ================== 时间戳字段访问方法（64位格式） ==================

    /**
     * 获取 Reference Timestamp (64位)
     */
    public long getReferenceTimestamp() {
        return header.getLong(16);
    }

    /**
     * 设置 Reference Timestamp (64位)
     */
    public void setReferenceTimestamp(long timestamp) {
        header.putLong(16, timestamp);
    }

    /**
     * 获取 Originate Timestamp (64位)
     */
    public long getOriginateTimestamp() {
        return header.getLong(24);
    }

    /**
     * 设置 Originate Timestamp (64位)
     */
    public void setOriginateTimestamp(long timestamp) {
        header.putLong(24, timestamp);
    }

    /**
     * 获取 Receive Timestamp (64位)
     */
    public long getReceiveTimestamp() {
        return header.getLong(32);
    }

    /**
     * 设置 Receive Timestamp (64位)
     */
    public void setReceiveTimestamp(long timestamp) {
        header.putLong(32, timestamp);
    }

    /**
     * 获取 Transmit Timestamp (64位)
     */
    public long getTransmitTimestamp() {
        return header.getLong(40);
    }

    /**
     * 设置 Transmit Timestamp (64位)
     */
    public void setTransmitTimestamp(long timestamp) {
        header.putLong(40, timestamp);
    }

    

	public void setReferenceTimestamp64(Long128 longValue) {
		setReferenceTimestamp(longValue.longValue());
	}

	public void setOriginateTimestamp64(Long128 originateTimestamp) {
    	setOriginateTimestamp(originateTimestamp.longValue());
	}
	

	public void setReceiveTimestamp64(Long128 longValue) {
		setReceiveTimestamp(longValue.longValue());
	}
    
    
	public void setTransmitTimestamp64(Long128 longValue) {
		setTransmitTimestamp(longValue.longValue());
	}
	
	public Long128 getReferenceTimestamp64() {
		Long128 rez=Long128.valueOf(getReferenceTimestamp());
		if(rez.signum()<0) {
			rez=rez.add(TWO_POW_64);
		}
		return rez;
	}
    
	public Long128 getOriginateTimestamp64() {
		Long128 rez=Long128.valueOf(getOriginateTimestamp());
		if(rez.signum()<0) {
			rez=rez.add(TWO_POW_64);
		}
		return rez;
	}
	public Long128 getReceiveTimestamp64() {
		Long128 rez=Long128.valueOf(getReceiveTimestamp());
		if(rez.signum()<0) {
			rez=rez.add(TWO_POW_64);
		}
		return rez;
	}
	public Long128 getTransmitTimestamp64() {
    	Long128 rez=Long128.valueOf(getTransmitTimestamp());
		if(rez.signum()<0) {
			rez=rez.add(TWO_POW_64);
		}
		return rez;
	}

    
    // ================== 扩展字段方法 ==================

    public List<ExtensionField> getExtensionFields() {
        return extensionFields;
    }

    public void addExtensionField(ExtensionField field) {
        extensionFields.add(field);
    }

    public void clearExtensionFields() {
        extensionFields.clear();
    }

    // ================== 认证字段方法 ==================

    public AuthenticationField getAuthField() {
        return authField;
    }

    public void setAuthField(AuthenticationField authField) {
        this.authField = authField;
    }

    public boolean hasAuthentication() {
        return authField != null;
    }

    // ================== 工具方法 ==================

    @Override
	public String toString() {
		return "NTPv4Packet [getLeapIndicator()=" + getLeapIndicator() + ", getVersionNumber()=" + getVersionNumber()
				+ ", getMode()=" + getMode() + ", getStratum()=" + getStratum() + ", getPollInterval()="
				+ getPollInterval() + ", getPrecision()=" + getPrecision() + ", getRootDelay()=" + getRootDelay()
				+ ", getRootDispersion()=" + getRootDispersion() + ", getReferenceIdentifier()="
				+ getReferenceIdentifier() + ", getReferenceTimestamp()=" +NTPTimestamps.ntp64ToString( getReferenceTimestamp64())
				+ ", getOriginateTimestamp()=" + NTPTimestamps.ntp64ToString(getOriginateTimestamp64()) + ", getReceiveTimestamp()="
				+ NTPTimestamps.ntp64ToString(getReceiveTimestamp64()) + ", getTransmitTimestamp()=" + NTPTimestamps.ntp64ToString(getTransmitTimestamp64()) + "]";
	}

    

	public Long128 getDestinationTimestamp128() {
		return destinationTimeStamp;
	}




    
	// ================== 内部类：扩展字段 ==================

    public static class ExtensionField {
        private ByteBuffer data;
        
        public ExtensionField() {
            data = NetworkPacket.bufferAllocator.allocate(0);
        }
        
        public ExtensionField(byte[] extensionData) {
            data = NetworkPacket.bufferAllocator.allocate(extensionData.length);
            data.put(extensionData);
            data.flip();
        }
        
        public long getLength() {
            return data.limit() + 4; // 数据长度 + 长度字段(4字节)
        }
        
        public void writeToChannel(WritableByteChannel dto) throws IOException {
            // 写入长度字段
            ByteBuffer lengthBuffer = NetworkPacket.bufferAllocator.allocate(4);
            lengthBuffer.putInt(data.limit());
            lengthBuffer.flip();
            dto.write(lengthBuffer);
            
            // 写入数据
            dto.write(data.slice(0, data.limit()));
        }
        
        public void readFromChannel(ReadableByteChannel din, long maxLength) throws IOException {
            // 读取长度字段
            ByteBuffer lengthBuffer = NetworkPacket.bufferAllocator.allocate(4);
            KNEChannels.readFully(din, lengthBuffer);
            lengthBuffer.flip();
            int length = lengthBuffer.getInt();
            
            // 读取数据
            if (length > 0 && length <= maxLength - 4) {
                data = NetworkPacket.bufferAllocator.allocate(length);
                KNEChannels.readFully(din, data);
                data.flip();
            }
        }
        
        public ByteBuffer getData() {
            return data;
        }
        
        public void setData(ByteBuffer data) {
            this.data = data;
        }
    }

    // ================== 内部类：认证字段 ==================

    public static class AuthenticationField extends NetworkPacket{
        private int keyIdentifier;
        private ByteBuffer digest;
        
        public AuthenticationField() {
            digest = NetworkPacket.bufferAllocator.allocate(16); // MD5摘要长度
        }
        
        public long getTotalLength() {
            return AUTH_FIELD_LENGTH; // 固定20字节
        }
        
        public void writeToChannel(WritableByteChannel dto) throws IOException {
            ByteBuffer buffer = NetworkPacket.bufferAllocator.allocate(AUTH_FIELD_LENGTH);
            buffer.putInt(keyIdentifier);
            
            ByteBuffer digestCopy = digest.duplicate();
            digestCopy.position(0);
            while (digestCopy.hasRemaining()) {
                buffer.put(digestCopy.get());
            }
            
            buffer.flip();
            dto.write(buffer);
        }
        
        public void readFromChannel(ReadableByteChannel din, long length) throws IOException {
            ByteBuffer buffer = NetworkPacket.bufferAllocator.allocate(AUTH_FIELD_LENGTH);
            KNEChannels.readFully(din, buffer);
            buffer.flip();
            
            keyIdentifier = buffer.getInt();
            
            digest = NetworkPacket.bufferAllocator.allocate(16);
            while (buffer.hasRemaining() && digest.hasRemaining()) {
                digest.put(buffer.get());
            }
            digest.flip();
        }
        
        public int getKeyIdentifier() {
            return keyIdentifier;
        }
        
        public void setKeyIdentifier(int keyIdentifier) {
            this.keyIdentifier = keyIdentifier;
        }
        
        public ByteBuffer getDigest() {
            return digest;
        }
        
        public void setDigest(ByteBuffer digest) {
            this.digest = digest;
        }

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

	public Long128 getReceiveTimestamp128() { 
		return NTPTimestamps.inferNtp64To128(getReceiveTimestamp64(),clock.getCurrentTimeNTP128());
	}

	public Long128 getOriginateTimestamp128() {
		return NTPTimestamps.inferNtp64To128(getOriginateTimestamp64(),clock.getCurrentTimeNTP128());
	}

	public Long128 getTransmitTimestamp128() {
		return NTPTimestamps.inferNtp64To128(getTransmitTimestamp64(),clock.getCurrentTimeNTP128());
	}

	public Long128 getReferenceTimestamp128() {
		return NTPTimestamps.inferNtp64To128(getReferenceTimestamp64(),clock.getCurrentTimeNTP128());
	}

    
}