// Copyright 2012-2023 The NATS Authors
// 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 nats

import (
	
)

type msgArg struct {
	subject []byte
	reply   []byte
	sid     int64
	hdr     int
	size    int
}

const MAX_CONTROL_LINE_SIZE = 4096

type parseState struct {
	state     int
	as        int
	drop      int
	hdr       int
	ma        msgArg
	argBuf    []byte
	msgBuf    []byte
	msgCopied bool
	scratch   [MAX_CONTROL_LINE_SIZE]byte
}

const (
	OP_START = iota
	OP_PLUS
	OP_PLUS_O
	OP_PLUS_OK
	OP_MINUS
	OP_MINUS_E
	OP_MINUS_ER
	OP_MINUS_ERR
	OP_MINUS_ERR_SPC
	MINUS_ERR_ARG
	OP_M
	OP_MS
	OP_MSG
	OP_MSG_SPC
	MSG_ARG
	MSG_PAYLOAD
	MSG_END
	OP_H
	OP_P
	OP_PI
	OP_PIN
	OP_PING
	OP_PO
	OP_PON
	OP_PONG
	OP_I
	OP_IN
	OP_INF
	OP_INFO
	OP_INFO_SPC
	INFO_ARG
)

// parse is the fast protocol parser engine.
func ( *Conn) ( []byte) error {
	var  int
	var  byte

	// Move to loop instead of range syntax to allow jumping of i
	for  = 0;  < len(); ++ {
		 = []

		switch .ps.state {
		case OP_START:
			switch  {
			case 'M', 'm':
				.ps.state = OP_M
				.ps.hdr = -1
				.ps.ma.hdr = -1
			case 'H', 'h':
				.ps.state = OP_H
				.ps.hdr = 0
				.ps.ma.hdr = 0
			case 'P', 'p':
				.ps.state = OP_P
			case '+':
				.ps.state = OP_PLUS
			case '-':
				.ps.state = OP_MINUS
			case 'I', 'i':
				.ps.state = OP_I
			default:
				goto 
			}
		case OP_H:
			switch  {
			case 'M', 'm':
				.ps.state = OP_M
			default:
				goto 
			}
		case OP_M:
			switch  {
			case 'S', 's':
				.ps.state = OP_MS
			default:
				goto 
			}
		case OP_MS:
			switch  {
			case 'G', 'g':
				.ps.state = OP_MSG
			default:
				goto 
			}
		case OP_MSG:
			switch  {
			case ' ', '\t':
				.ps.state = OP_MSG_SPC
			default:
				goto 
			}
		case OP_MSG_SPC:
			switch  {
			case ' ', '\t':
				continue
			default:
				.ps.state = MSG_ARG
				.ps.as = 
			}
		case MSG_ARG:
			switch  {
			case '\r':
				.ps.drop = 1
			case '\n':
				var  []byte
				if .ps.argBuf != nil {
					 = .ps.argBuf
				} else {
					 = [.ps.as : -.ps.drop]
				}
				if  := .processMsgArgs();  != nil {
					return 
				}
				.ps.drop, .ps.as, .ps.state = 0, +1, MSG_PAYLOAD

				// jump ahead with the index. If this overruns
				// what is left we fall out and process a split buffer.
				 = .ps.as + .ps.ma.size - 1
			default:
				if .ps.argBuf != nil {
					.ps.argBuf = append(.ps.argBuf, )
				}
			}
		case MSG_PAYLOAD:
			if .ps.msgBuf != nil {
				if len(.ps.msgBuf) >= .ps.ma.size {
					.processMsg(.ps.msgBuf)
					.ps.argBuf, .ps.msgBuf, .ps.msgCopied, .ps.state = nil, nil, false, MSG_END
				} else {
					// copy as much as we can to the buffer and skip ahead.
					 := .ps.ma.size - len(.ps.msgBuf)
					 := len() - 

					if  <  {
						 = 
					}

					if  > 0 {
						 := len(.ps.msgBuf)
						// This is needed for copy to work.
						.ps.msgBuf = .ps.msgBuf[:+]
						copy(.ps.msgBuf[:], [:+])
						// Update our index
						 = ( + ) - 1
					} else {
						.ps.msgBuf = append(.ps.msgBuf, )
					}
				}
			} else if -.ps.as >= .ps.ma.size {
				.processMsg([.ps.as:])
				.ps.argBuf, .ps.msgBuf, .ps.msgCopied, .ps.state = nil, nil, false, MSG_END
			}
		case MSG_END:
			switch  {
			case '\n':
				.ps.drop, .ps.as, .ps.state = 0, +1, OP_START
			default:
				continue
			}
		case OP_PLUS:
			switch  {
			case 'O', 'o':
				.ps.state = OP_PLUS_O
			default:
				goto 
			}
		case OP_PLUS_O:
			switch  {
			case 'K', 'k':
				.ps.state = OP_PLUS_OK
			default:
				goto 
			}
		case OP_PLUS_OK:
			switch  {
			case '\n':
				.processOK()
				.ps.drop, .ps.state = 0, OP_START
			}
		case OP_MINUS:
			switch  {
			case 'E', 'e':
				.ps.state = OP_MINUS_E
			default:
				goto 
			}
		case OP_MINUS_E:
			switch  {
			case 'R', 'r':
				.ps.state = OP_MINUS_ER
			default:
				goto 
			}
		case OP_MINUS_ER:
			switch  {
			case 'R', 'r':
				.ps.state = OP_MINUS_ERR
			default:
				goto 
			}
		case OP_MINUS_ERR:
			switch  {
			case ' ', '\t':
				.ps.state = OP_MINUS_ERR_SPC
			default:
				goto 
			}
		case OP_MINUS_ERR_SPC:
			switch  {
			case ' ', '\t':
				continue
			default:
				.ps.state = MINUS_ERR_ARG
				.ps.as = 
			}
		case MINUS_ERR_ARG:
			switch  {
			case '\r':
				.ps.drop = 1
			case '\n':
				var  []byte
				if .ps.argBuf != nil {
					 = .ps.argBuf
					.ps.argBuf = nil
				} else {
					 = [.ps.as : -.ps.drop]
				}
				.processErr(string())
				.ps.drop, .ps.as, .ps.state = 0, +1, OP_START
			default:
				if .ps.argBuf != nil {
					.ps.argBuf = append(.ps.argBuf, )
				}
			}
		case OP_P:
			switch  {
			case 'I', 'i':
				.ps.state = OP_PI
			case 'O', 'o':
				.ps.state = OP_PO
			default:
				goto 
			}
		case OP_PO:
			switch  {
			case 'N', 'n':
				.ps.state = OP_PON
			default:
				goto 
			}
		case OP_PON:
			switch  {
			case 'G', 'g':
				.ps.state = OP_PONG
			default:
				goto 
			}
		case OP_PONG:
			switch  {
			case '\n':
				.processPong()
				.ps.drop, .ps.state = 0, OP_START
			}
		case OP_PI:
			switch  {
			case 'N', 'n':
				.ps.state = OP_PIN
			default:
				goto 
			}
		case OP_PIN:
			switch  {
			case 'G', 'g':
				.ps.state = OP_PING
			default:
				goto 
			}
		case OP_PING:
			switch  {
			case '\n':
				.processPing()
				.ps.drop, .ps.state = 0, OP_START
			}
		case OP_I:
			switch  {
			case 'N', 'n':
				.ps.state = OP_IN
			default:
				goto 
			}
		case OP_IN:
			switch  {
			case 'F', 'f':
				.ps.state = OP_INF
			default:
				goto 
			}
		case OP_INF:
			switch  {
			case 'O', 'o':
				.ps.state = OP_INFO
			default:
				goto 
			}
		case OP_INFO:
			switch  {
			case ' ', '\t':
				.ps.state = OP_INFO_SPC
			default:
				goto 
			}
		case OP_INFO_SPC:
			switch  {
			case ' ', '\t':
				continue
			default:
				.ps.state = INFO_ARG
				.ps.as = 
			}
		case INFO_ARG:
			switch  {
			case '\r':
				.ps.drop = 1
			case '\n':
				var  []byte
				if .ps.argBuf != nil {
					 = .ps.argBuf
					.ps.argBuf = nil
				} else {
					 = [.ps.as : -.ps.drop]
				}
				.processAsyncInfo()
				.ps.drop, .ps.as, .ps.state = 0, +1, OP_START
			default:
				if .ps.argBuf != nil {
					.ps.argBuf = append(.ps.argBuf, )
				}
			}
		default:
			goto 
		}
	}
	// Check for split buffer scenarios
	if (.ps.state == MSG_ARG || .ps.state == MINUS_ERR_ARG || .ps.state == INFO_ARG) && .ps.argBuf == nil {
		.ps.argBuf = .ps.scratch[:0]
		.ps.argBuf = append(.ps.argBuf, [.ps.as:-.ps.drop]...)
		// FIXME, check max len
	}
	// Check for split msg
	if .ps.state == MSG_PAYLOAD && .ps.msgBuf == nil {
		// We need to clone the msgArg if it is still referencing the
		// read buffer and we are not able to process the msg.
		if .ps.argBuf == nil {
			.cloneMsgArg()
		}

		// If we will overflow the scratch buffer, just create a
		// new buffer to hold the split message.
		if .ps.ma.size > cap(.ps.scratch)-len(.ps.argBuf) {
			 := len([.ps.as:])

			.ps.msgBuf = make([]byte, , .ps.ma.size)
			copy(.ps.msgBuf, [.ps.as:])
			.ps.msgCopied = true
		} else {
			.ps.msgBuf = .ps.scratch[len(.ps.argBuf):len(.ps.argBuf)]
			.ps.msgBuf = append(.ps.msgBuf, ([.ps.as:])...)
		}
	}

	return nil

:
	return fmt.Errorf("nats: Parse Error [%d]: '%s'", .ps.state, [:])
}

// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
// we need to hold onto it into the next read.
func ( *Conn) () {
	.ps.argBuf = .ps.scratch[:0]
	.ps.argBuf = append(.ps.argBuf, .ps.ma.subject...)
	.ps.argBuf = append(.ps.argBuf, .ps.ma.reply...)
	.ps.ma.subject = .ps.argBuf[:len(.ps.ma.subject)]
	if .ps.ma.reply != nil {
		.ps.ma.reply = .ps.argBuf[len(.ps.ma.subject):]
	}
}

const argsLenMax = 4

func ( *Conn) ( []byte) error {
	// Use separate function for header based messages.
	if .ps.hdr >= 0 {
		return .processHeaderMsgArgs()
	}

	// Unroll splitArgs to avoid runtime/heap issues
	 := [argsLenMax][]byte{}
	 := [:0]
	 := -1
	for ,  := range  {
		switch  {
		case ' ', '\t', '\r', '\n':
			if  >= 0 {
				 = append(, [:])
				 = -1
			}
		default:
			if  < 0 {
				 = 
			}
		}
	}
	if  >= 0 {
		 = append(, [:])
	}

	switch len() {
	case 3:
		.ps.ma.subject = [0]
		.ps.ma.sid = parseInt64([1])
		.ps.ma.reply = nil
		.ps.ma.size = int(parseInt64([2]))
	case 4:
		.ps.ma.subject = [0]
		.ps.ma.sid = parseInt64([1])
		.ps.ma.reply = [2]
		.ps.ma.size = int(parseInt64([3]))
	default:
		return fmt.Errorf("nats: processMsgArgs Parse Error: '%s'", )
	}
	if .ps.ma.sid < 0 {
		return fmt.Errorf("nats: processMsgArgs Bad or Missing Sid: '%s'", )
	}
	if .ps.ma.size < 0 {
		return fmt.Errorf("nats: processMsgArgs Bad or Missing Size: '%s'", )
	}
	return nil
}

// processHeaderMsgArgs is for a header based message.
func ( *Conn) ( []byte) error {
	// Unroll splitArgs to avoid runtime/heap issues
	 := [argsLenMax][]byte{}
	 := [:0]
	 := -1
	for ,  := range  {
		switch  {
		case ' ', '\t', '\r', '\n':
			if  >= 0 {
				 = append(, [:])
				 = -1
			}
		default:
			if  < 0 {
				 = 
			}
		}
	}
	if  >= 0 {
		 = append(, [:])
	}

	switch len() {
	case 4:
		.ps.ma.subject = [0]
		.ps.ma.sid = parseInt64([1])
		.ps.ma.reply = nil
		.ps.ma.hdr = int(parseInt64([2]))
		.ps.ma.size = int(parseInt64([3]))
	case 5:
		.ps.ma.subject = [0]
		.ps.ma.sid = parseInt64([1])
		.ps.ma.reply = [2]
		.ps.ma.hdr = int(parseInt64([3]))
		.ps.ma.size = int(parseInt64([4]))
	default:
		return fmt.Errorf("nats: processHeaderMsgArgs Parse Error: '%s'", )
	}
	if .ps.ma.sid < 0 {
		return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Sid: '%s'", )
	}
	if .ps.ma.hdr < 0 || .ps.ma.hdr > .ps.ma.size {
		return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Header Size: '%s'", )
	}
	if .ps.ma.size < 0 {
		return fmt.Errorf("nats: processHeaderMsgArgs Bad or Missing Size: '%s'", )
	}
	return nil
}

// ASCII numbers 0-9
const (
	ascii_0 = 48
	ascii_9 = 57
)

// parseInt64 expects decimal positive numbers. We
// return -1 to signal error
func parseInt64( []byte) ( int64) {
	if len() == 0 {
		return -1
	}
	for ,  := range  {
		if  < ascii_0 ||  > ascii_9 {
			return -1
		}
		 = *10 + (int64() - ascii_0)
	}
	return 
}