package proto

import (
	
	
	
	
	
	
	

	
)

// redis resp protocol data type.
const (
	RespStatus    = '+' // +<string>\r\n
	RespError     = '-' // -<string>\r\n
	RespString    = '$' // $<length>\r\n<bytes>\r\n
	RespInt       = ':' // :<number>\r\n
	RespNil       = '_' // _\r\n
	RespFloat     = ',' // ,<floating-point-number>\r\n (golang float)
	RespBool      = '#' // true: #t\r\n false: #f\r\n
	RespBlobError = '!' // !<length>\r\n<bytes>\r\n
	RespVerbatim  = '=' // =<length>\r\nFORMAT:<bytes>\r\n
	RespBigInt    = '(' // (<big number>\r\n
	RespArray     = '*' // *<len>\r\n... (same as resp2)
	RespMap       = '%' // %<len>\r\n(key)\r\n(value)\r\n... (golang map)
	RespSet       = '~' // ~<len>\r\n... (same as Array)
	RespAttr      = '|' // |<len>\r\n(key)\r\n(value)\r\n... + command reply
	RespPush      = '>' // ><len>\r\n... (same as Array)
)

// Not used temporarily.
// Redis has not used these two data types for the time being, and will implement them later.
// Streamed           = "EOF:"
// StreamedAggregated = '?'

//------------------------------------------------------------------------------

const Nil = RedisError("redis: nil") // nolint:errname

type RedisError string

func ( RedisError) () string { return string() }

func (RedisError) () {}

func ( []byte) error {
	return RedisError([1:])
}

//------------------------------------------------------------------------------

type Reader struct {
	rd *bufio.Reader
}

func ( io.Reader) *Reader {
	return &Reader{
		rd: bufio.NewReader(),
	}
}

func ( *Reader) () int {
	return .rd.Buffered()
}

func ( *Reader) ( int) ([]byte, error) {
	return .rd.Peek()
}

func ( *Reader) ( io.Reader) {
	.rd.Reset()
}

// PeekReplyType returns the data type of the next response without advancing the Reader,
// and discard the attribute type.
func ( *Reader) () (byte, error) {
	,  := .rd.Peek(1)
	if  != nil {
		return 0, 
	}
	if [0] == RespAttr {
		if  = .DiscardNext();  != nil {
			return 0, 
		}
		return .()
	}
	return [0], nil
}

// ReadLine Return a valid reply, it will check the protocol or redis error,
// and discard the attribute type.
func ( *Reader) () ([]byte, error) {
	,  := .readLine()
	if  != nil {
		return nil, 
	}
	switch [0] {
	case RespError:
		return nil, ParseErrorReply()
	case RespNil:
		return nil, Nil
	case RespBlobError:
		var  string
		,  = .readStringReply()
		if  == nil {
			 = RedisError()
		}
		return nil, 
	case RespAttr:
		if  = .Discard();  != nil {
			return nil, 
		}
		return .()
	}

	// Compatible with RESP2
	if IsNilReply() {
		return nil, Nil
	}

	return , nil
}

// readLine returns an error if:
//   - there is a pending read error;
//   - or line does not end with \r\n.
func ( *Reader) () ([]byte, error) {
	,  := .rd.ReadSlice('\n')
	if  != nil {
		if  != bufio.ErrBufferFull {
			return nil, 
		}

		 := make([]byte, len())
		copy(, )

		,  = .rd.ReadBytes('\n')
		if  != nil {
			return nil, 
		}

		 = append(, ...) //nolint:makezero
		 = 
	}
	if len() <= 2 || [len()-1] != '\n' || [len()-2] != '\r' {
		return nil, fmt.Errorf("redis: invalid reply: %q", )
	}
	return [:len()-2], nil
}

func ( *Reader) () (interface{}, error) {
	,  := .ReadLine()
	if  != nil {
		return nil, 
	}

	switch [0] {
	case RespStatus:
		return string([1:]), nil
	case RespInt:
		return util.ParseInt([1:], 10, 64)
	case RespFloat:
		return .readFloat()
	case RespBool:
		return .readBool()
	case RespBigInt:
		return .readBigInt()

	case RespString:
		return .readStringReply()
	case RespVerbatim:
		return .readVerb()

	case RespArray, RespSet, RespPush:
		return .readSlice()
	case RespMap:
		return .readMap()
	}
	return nil, fmt.Errorf("redis: can't parse %.100q", )
}

func ( *Reader) ( []byte) (float64, error) {
	 := string([1:])
	switch string([1:]) {
	case "inf":
		return math.Inf(1), nil
	case "-inf":
		return math.Inf(-1), nil
	}
	return strconv.ParseFloat(, 64)
}

func ( *Reader) ( []byte) (bool, error) {
	switch string([1:]) {
	case "t":
		return true, nil
	case "f":
		return false, nil
	}
	return false, fmt.Errorf("redis: can't parse bool reply: %q", )
}

func ( *Reader) ( []byte) (*big.Int, error) {
	 := new(big.Int)
	if ,  := .SetString(string([1:]), 10);  {
		return , nil
	}
	return nil, fmt.Errorf("redis: can't parse bigInt reply: %q", )
}

func ( *Reader) ( []byte) (string, error) {
	,  := replyLen()
	if  != nil {
		return "", 
	}

	 := make([]byte, +2)
	_,  = io.ReadFull(.rd, )
	if  != nil {
		return "", 
	}

	return util.BytesToString([:]), nil
}

func ( *Reader) ( []byte) (string, error) {
	,  := .readStringReply()
	if  != nil {
		return "", 
	}
	if len() < 4 || [3] != ':' {
		return "", fmt.Errorf("redis: can't parse verbatim string reply: %q", )
	}
	return [4:], nil
}

func ( *Reader) ( []byte) ([]interface{}, error) {
	,  := replyLen()
	if  != nil {
		return nil, 
	}

	 := make([]interface{}, )
	for  := 0;  < len(); ++ {
		,  := .ReadReply()
		if  != nil {
			if  == Nil {
				[] = nil
				continue
			}
			if ,  := .(RedisError);  {
				[] = 
				continue
			}
			return nil, 
		}
		[] = 
	}
	return , nil
}

func ( *Reader) ( []byte) (map[interface{}]interface{}, error) {
	,  := replyLen()
	if  != nil {
		return nil, 
	}
	 := make(map[interface{}]interface{}, )
	for  := 0;  < ; ++ {
		,  := .ReadReply()
		if  != nil {
			return nil, 
		}
		,  := .ReadReply()
		if  != nil {
			if  == Nil {
				[] = nil
				continue
			}
			if ,  := .(RedisError);  {
				[] = 
				continue
			}
			return nil, 
		}
		[] = 
	}
	return , nil
}

// -------------------------------

func ( *Reader) () (int64, error) {
	,  := .ReadLine()
	if  != nil {
		return 0, 
	}
	switch [0] {
	case RespInt, RespStatus:
		return util.ParseInt([1:], 10, 64)
	case RespString:
		,  := .readStringReply()
		if  != nil {
			return 0, 
		}
		return util.ParseInt([]byte(), 10, 64)
	case RespBigInt:
		,  := .readBigInt()
		if  != nil {
			return 0, 
		}
		if !.IsInt64() {
			return 0, fmt.Errorf("bigInt(%s) value out of range", .String())
		}
		return .Int64(), nil
	}
	return 0, fmt.Errorf("redis: can't parse int reply: %.100q", )
}

func ( *Reader) () (uint64, error) {
	,  := .ReadLine()
	if  != nil {
		return 0, 
	}
	switch [0] {
	case RespInt, RespStatus:
		return util.ParseUint([1:], 10, 64)
	case RespString:
		,  := .readStringReply()
		if  != nil {
			return 0, 
		}
		return util.ParseUint([]byte(), 10, 64)
	case RespBigInt:
		,  := .readBigInt()
		if  != nil {
			return 0, 
		}
		if !.IsUint64() {
			return 0, fmt.Errorf("bigInt(%s) value out of range", .String())
		}
		return .Uint64(), nil
	}
	return 0, fmt.Errorf("redis: can't parse uint reply: %.100q", )
}

func ( *Reader) () (float64, error) {
	,  := .ReadLine()
	if  != nil {
		return 0, 
	}
	switch [0] {
	case RespFloat:
		return .readFloat()
	case RespStatus:
		return strconv.ParseFloat(string([1:]), 64)
	case RespString:
		,  := .readStringReply()
		if  != nil {
			return 0, 
		}
		return strconv.ParseFloat(, 64)
	}
	return 0, fmt.Errorf("redis: can't parse float reply: %.100q", )
}

func ( *Reader) () (string, error) {
	,  := .ReadLine()
	if  != nil {
		return "", 
	}

	switch [0] {
	case RespStatus, RespInt, RespFloat:
		return string([1:]), nil
	case RespString:
		return .readStringReply()
	case RespBool:
		,  := .readBool()
		return strconv.FormatBool(), 
	case RespVerbatim:
		return .readVerb()
	case RespBigInt:
		,  := .readBigInt()
		if  != nil {
			return "", 
		}
		return .String(), nil
	}
	return "", fmt.Errorf("redis: can't parse reply=%.100q reading string", )
}

func ( *Reader) () (bool, error) {
	,  := .ReadString()
	if  != nil {
		return false, 
	}
	return  == "OK" ||  == "1" ||  == "true", nil
}

func ( *Reader) () ([]interface{}, error) {
	,  := .ReadLine()
	if  != nil {
		return nil, 
	}
	return .readSlice()
}

// ReadFixedArrayLen read fixed array length.
func ( *Reader) ( int) error {
	,  := .ReadArrayLen()
	if  != nil {
		return 
	}
	if  !=  {
		return fmt.Errorf("redis: got %d elements in the array, wanted %d", , )
	}
	return nil
}

// ReadArrayLen Read and return the length of the array.
func ( *Reader) () (int, error) {
	,  := .ReadLine()
	if  != nil {
		return 0, 
	}
	switch [0] {
	case RespArray, RespSet, RespPush:
		return replyLen()
	default:
		return 0, fmt.Errorf("redis: can't parse array/set/push reply: %.100q", )
	}
}

// ReadFixedMapLen reads fixed map length.
func ( *Reader) ( int) error {
	,  := .ReadMapLen()
	if  != nil {
		return 
	}
	if  !=  {
		return fmt.Errorf("redis: got %d elements in the map, wanted %d", , )
	}
	return nil
}

// ReadMapLen reads the length of the map type.
// If responding to the array type (RespArray/RespSet/RespPush),
// it must be a multiple of 2 and return n/2.
// Other types will return an error.
func ( *Reader) () (int, error) {
	,  := .ReadLine()
	if  != nil {
		return 0, 
	}
	switch [0] {
	case RespMap:
		return replyLen()
	case RespArray, RespSet, RespPush:
		// Some commands and RESP2 protocol may respond to array types.
		,  := replyLen()
		if  != nil {
			return 0, 
		}
		if %2 != 0 {
			return 0, fmt.Errorf("redis: the length of the array must be a multiple of 2, got: %d", )
		}
		return  / 2, nil
	default:
		return 0, fmt.Errorf("redis: can't parse map reply: %.100q", )
	}
}

// DiscardNext read and discard the data represented by the next line.
func ( *Reader) () error {
	,  := .readLine()
	if  != nil {
		return 
	}
	return .Discard()
}

// Discard the data represented by line.
func ( *Reader) ( []byte) ( error) {
	if len() == 0 {
		return errors.New("redis: invalid line")
	}
	switch [0] {
	case RespStatus, RespError, RespInt, RespNil, RespFloat, RespBool, RespBigInt:
		return nil
	}

	,  := replyLen()
	if  != nil &&  != Nil {
		return 
	}

	switch [0] {
	case RespBlobError, RespString, RespVerbatim:
		// +\r\n
		_,  = .rd.Discard( + 2)
		return 
	case RespArray, RespSet, RespPush:
		for  := 0;  < ; ++ {
			if  = .DiscardNext();  != nil {
				return 
			}
		}
		return nil
	case RespMap, RespAttr:
		// Read key & value.
		for  := 0;  < *2; ++ {
			if  = .DiscardNext();  != nil {
				return 
			}
		}
		return nil
	}

	return fmt.Errorf("redis: can't parse %.100q", )
}

func replyLen( []byte) ( int,  error) {
	,  = util.Atoi([1:])
	if  != nil {
		return 0, 
	}

	if  < -1 {
		return 0, fmt.Errorf("redis: invalid reply: %q", )
	}

	switch [0] {
	case RespString, RespVerbatim, RespBlobError,
		RespArray, RespSet, RespPush, RespMap, RespAttr:
		if  == -1 {
			return 0, Nil
		}
	}
	return , nil
}

// IsNilReply detects redis.Nil of RESP2.
func ( []byte) bool {
	return len() == 3 &&
		([0] == RespString || [0] == RespArray) &&
		[1] == '-' && [2] == '1'
}