/*
 * Copyright 2015 Couchbase, Inc.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.couchbase.mock.memcached.protocol;

import org.couchbase.mock.memcached.SubdocItem;
import org.couchbase.mock.subdoc.Operation;

import java.net.ProtocolException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

public class BinarySubdocCommand extends BinaryCommand {

    private final Operation subdocOp;
    public final static byte PATHFLAG_MKDIR_P = 0x01;
    public final static byte PATHFLAG_XATTR = 0x04;
    public final static byte PATHFLAG_EXPAND_MACROS = 0x10;

    public final static byte DOCFLAG_MKDOC = 0x01;
    public final static byte DOCFLAG_ADD = 0x02;
    public final static byte DOCFLAG_CREATEMASK = DOCFLAG_MKDOC | DOCFLAG_ADD;
    public final static byte DOCFLAG_ACCESS_DELETED = 0x04;

    private final static Map<CommandCode, Operation> opMap = new HashMap<CommandCode, Operation>();
    static {
        opMap.put(CommandCode.SUBDOC_GET, Operation.GET);
        opMap.put(CommandCode.SUBDOC_EXISTS, Operation.EXISTS);
        opMap.put(CommandCode.SUBDOC_DICT_ADD, Operation.DICT_ADD);
        opMap.put(CommandCode.SUBDOC_DICT_UPSERT, Operation.DICT_UPSERT);
        opMap.put(CommandCode.SUBDOC_DELETE, Operation.REMOVE);
        opMap.put(CommandCode.SUBDOC_REPLACE, Operation.REPLACE);
        opMap.put(CommandCode.SUBDOC_ARRAY_PUSH_LAST, Operation.ARRAY_APPEND);
        opMap.put(CommandCode.SUBDOC_ARRAY_PUSH_FIRST, Operation.ARRAY_PREPEND);
        opMap.put(CommandCode.SUBDOC_ARRAY_INSERT, Operation.ARRAY_INSERT);
        opMap.put(CommandCode.SUBDOC_ARRAY_ADD_UNIQUE, Operation.ADD_UNIQUE);
        opMap.put(CommandCode.SUBDOC_COUNTER, Operation.COUNTER);
        opMap.put(CommandCode.SUBDOC_GET_COUNT, Operation.GET_COUNT);
    }

    BinarySubdocCommand(ByteBuffer header) throws ProtocolException {
        super(header);
        switch (extraLength) {
            case 3: // standard header(3) [path + pathflags]
            case 4: // standard header(3) + docflags(1)
            case 7: // standard header(3) + expiry(4)
            case 8: // standard header(3) + expiry(4) + docflags(1)
                break;
            default:
            throw new ProtocolException("Extras must be 3, 4, 7, or 8");
        }

        subdocOp = toSubdocOpcode(getComCode());
        if (subdocOp == null) {
            throw new ProtocolException("Unhandled opcode: " + getComCode());
        }
    }

    public static Operation toSubdocOpcode(CommandCode op) {
        return opMap.get(op);
    }

    public byte getSubdocPathFlags() {
        return bodyBuffer.get(2);
    }

    public byte getSubdocDocFlags() {
        switch (extraLength) {
            case 3:
                return 0;
            case 4:
                return bodyBuffer.get(3);
            case 7:
                return 8;
            case 8:
                return bodyBuffer.get(7);
            default:
                return 0;
        }
    }

    public Operation getSubdocOp() {
        return subdocOp;
    }

    public SubdocItem getItem() {
        int expiryTime = 0;
        if (extraLength == 7) {
            expiryTime = bodyBuffer.getInt(3);
        }

        int pathLength = bodyBuffer.getShort(0);
        byte[] path = new byte[pathLength];

        // Seek and read into buffer for path
        bodyBuffer.position(extraLength + keyLength);
        bodyBuffer.get(path);

        int valueLength = bodyLength - (keyLength + pathLength + extraLength);
        byte[] value = new byte[valueLength];

        bodyBuffer.position(extraLength + keyLength + pathLength);
        bodyBuffer.get(value);

        return new SubdocItem(getKeySpec(), 0, expiryTime, path, value, cas);
    }
}

@@ -3274,16 +3274,50 @@
 case 3:%0A
+                // Path,PathFlags%0A
         
@@ -3354,16 +3354,61 @@
 case 4:%0A
+                // Path, PathFlags, DocFlags%0A
         
@@ -3461,16 +3461,59 @@
 case 7:%0A
+                // Path, PathFlags, Expiry%0A
         
@@ -3527,17 +3527,17 @@
  return 
-8
+0
 ;%0A      
@@ -3550,16 +3550,69 @@
 case 8:%0A
+                // Path, PathFlags, Expiry, DocFlags%0A
         

Fix subdoc doc flags parsing