package com.digitalpetri.enip.logix.services;

import java.util.function.Consumer;

import com.digitalpetri.enip.cip.CipResponseException;
import com.digitalpetri.enip.cip.epath.EPath.PaddedEPath;
import com.digitalpetri.enip.cip.services.CipService;
import com.digitalpetri.enip.cip.structs.MessageRouterRequest;
import com.digitalpetri.enip.cip.structs.MessageRouterResponse;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;

public class WriteTagFragmentedService implements CipService<ByteBuf> {

    public static final int SERVICE_CODE = 0x53;

    private final Consumer<ByteBuf> dataEncoder = this::encode;

    private final PaddedEPath requestPath;
    private final int elementCount;
    private final boolean structured;
    private final int tagType;
    private final ByteBuf data;
    private final int offset;
    private final int fragmentSize;

    public WriteTagFragmentedService(PaddedEPath requestPath,
            boolean structured,
            int tagType,
            int elementCount,
            int offset,
            int fragmentSize,
            ByteBuf data) {

        this.requestPath = requestPath;
        this.structured = structured;
        this.tagType = tagType;
        this.elementCount = elementCount;
        this.offset = offset;
        this.fragmentSize = fragmentSize;
        this.data = data;
    }

    @Override
    public void encodeRequest(ByteBuf buffer) {

        MessageRouterRequest request = new MessageRouterRequest(
                SERVICE_CODE,
                requestPath,
                dataEncoder);

        MessageRouterRequest.encode(request, buffer);
    }

    private void encode(ByteBuf buffer) {

        if (structured) {
            buffer.writeByte(0xA0).writeByte(0x02);
        }

        buffer.writeShort(tagType);
        buffer.writeShort(elementCount);
        buffer.writeInt(offset);

        data.readerIndex(offset);
        int sliceLength = Math.min(data.readableBytes(), fragmentSize);
        buffer.writeBytes(data.readSlice(sliceLength));
    }

    @Override
    public ByteBuf decodeResponse(ByteBuf buffer) throws CipResponseException, PartialResponseException {

        MessageRouterResponse response = MessageRouterResponse.decode(buffer);

        int generalStatus = response.getGeneralStatus();

        try {
            if (generalStatus == 0x00) {
                return null;
            } else {
                throw new CipResponseException(generalStatus, response.getAdditionalStatus());
            }
        } finally {
            ReferenceCountUtil.release(response.getData());
        }

    }

}

@@ -847,44 +847,611 @@
 et;%0A
-    private final int fragmentSize;%0A
+%0A    /**%0A     * @param requestPath %7B@link PaddedEPath Path%7D of tag%0A     * @param structured True if tag is structured%0A     * @param tagType Type of tag%0A     * @param elementCount Total number of elements being sent. Usually the number of bytes, but%0A     *                     can vary based on data type.%0A     * @param offset Total number of bytes of data transferred in previous requests%0A     * @param data %7B@link ByteBuf Data%7D to be sent in request. Data should be a slice of original data, starting%0A     *             at offset and ending at an appropriate length for the CIP connection size.%0A     */
 %0A   
@@ -1484,16 +1484,25 @@
 Service(
+%0A        
 PaddedEP
@@ -1526,20 +1526,16 @@
         
- 
-   
 boolean 
@@ -1550,28 +1550,24 @@
 ed,%0A        
-    
 int tagType,
@@ -1571,28 +1571,24 @@
 pe,%0A        
-   
- 
 int elementC
@@ -1597,28 +1597,24 @@
 nt,%0A        
-    
 int offset,%0A
@@ -1617,42 +1617,8 @@
 et,%0A
-            int fragmentSize,%0A    
     
@@ -1824,50 +1824,8 @@
 et;%0A
-        this.fragmentSize = fragmentSize;%0A
     
@@ -2377,115 +2377,8 @@
 et);
-%0A%0A        data.readerIndex(offset);%0A        int sliceLength = Math.min(data.readableBytes(), fragmentSize);
 %0A   
@@ -2408,31 +2408,8 @@
 data
-.readSlice(sliceLength)
 );%0A 

Send full data instead of splitting in Service