// Copyright The Prometheus 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 nfs

import (
	"fmt"
)

func parseReplyCache(v []uint64) (ReplyCache, error) {
	if len(v) != 3 {
		return ReplyCache{}, fmt.Errorf("invalid ReplyCache line %q", v)
	}

	return ReplyCache{
		Hits:    v[0],
		Misses:  v[1],
		NoCache: v[2],
	}, nil
}

func parseFileHandles(v []uint64) (FileHandles, error) {
	if len(v) != 5 {
		return FileHandles{}, fmt.Errorf("invalid FileHandles, line %q", v)
	}

	return FileHandles{
		Stale:        v[0],
		TotalLookups: v[1],
		AnonLookups:  v[2],
		DirNoCache:   v[3],
		NoDirNoCache: v[4],
	}, nil
}

func parseInputOutput(v []uint64) (InputOutput, error) {
	if len(v) != 2 {
		return InputOutput{}, fmt.Errorf("invalid InputOutput line %q", v)
	}

	return InputOutput{
		Read:  v[0],
		Write: v[1],
	}, nil
}

func parseThreads(v []uint64) (Threads, error) {
	if len(v) != 2 {
		return Threads{}, fmt.Errorf("invalid Threads line %q", v)
	}

	return Threads{
		Threads: v[0],
		FullCnt: v[1],
	}, nil
}

func parseReadAheadCache(v []uint64) (ReadAheadCache, error) {
	if len(v) != 12 {
		return ReadAheadCache{}, fmt.Errorf("invalid ReadAheadCache line %q", v)
	}

	return ReadAheadCache{
		CacheSize:      v[0],
		CacheHistogram: v[1:11],
		NotFound:       v[11],
	}, nil
}

func parseNetwork(v []uint64) (Network, error) {
	if len(v) != 4 {
		return Network{}, fmt.Errorf("invalid Network line %q", v)
	}

	return Network{
		NetCount:   v[0],
		UDPCount:   v[1],
		TCPCount:   v[2],
		TCPConnect: v[3],
	}, nil
}

func parseServerRPC(v []uint64) (ServerRPC, error) {
	if len(v) != 5 {
		return ServerRPC{}, fmt.Errorf("invalid RPC line %q", v)
	}

	return ServerRPC{
		RPCCount: v[0],
		BadCnt:   v[1],
		BadFmt:   v[2],
		BadAuth:  v[3],
		BadcInt:  v[4],
	}, nil
}

func parseClientRPC(v []uint64) (ClientRPC, error) {
	if len(v) != 3 {
		return ClientRPC{}, fmt.Errorf("invalid RPC line %q", v)
	}

	return ClientRPC{
		RPCCount:        v[0],
		Retransmissions: v[1],
		AuthRefreshes:   v[2],
	}, nil
}

func parseV2Stats(v []uint64) (V2Stats, error) {
	values := int(v[0])
	if len(v[1:]) != values || values < 18 {
		return V2Stats{}, fmt.Errorf("invalid V2Stats line %q", v)
	}

	return V2Stats{
		Null:     v[1],
		GetAttr:  v[2],
		SetAttr:  v[3],
		Root:     v[4],
		Lookup:   v[5],
		ReadLink: v[6],
		Read:     v[7],
		WrCache:  v[8],
		Write:    v[9],
		Create:   v[10],
		Remove:   v[11],
		Rename:   v[12],
		Link:     v[13],
		SymLink:  v[14],
		MkDir:    v[15],
		RmDir:    v[16],
		ReadDir:  v[17],
		FsStat:   v[18],
	}, nil
}

func parseV3Stats(v []uint64) (V3Stats, error) {
	values := int(v[0])
	if len(v[1:]) != values || values < 22 {
		return V3Stats{}, fmt.Errorf("invalid V3Stats line %q", v)
	}

	return V3Stats{
		Null:        v[1],
		GetAttr:     v[2],
		SetAttr:     v[3],
		Lookup:      v[4],
		Access:      v[5],
		ReadLink:    v[6],
		Read:        v[7],
		Write:       v[8],
		Create:      v[9],
		MkDir:       v[10],
		SymLink:     v[11],
		MkNod:       v[12],
		Remove:      v[13],
		RmDir:       v[14],
		Rename:      v[15],
		Link:        v[16],
		ReadDir:     v[17],
		ReadDirPlus: v[18],
		FsStat:      v[19],
		FsInfo:      v[20],
		PathConf:    v[21],
		Commit:      v[22],
	}, nil
}

func parseClientV4Stats(v []uint64) (ClientV4Stats, error) {
	values := int(v[0])
	if len(v[1:]) != values {
		return ClientV4Stats{}, fmt.Errorf("invalid ClientV4Stats line %q", v)
	}

	// This function currently supports mapping 59 NFS v4 client stats.  Older
	// kernels may emit fewer stats, so we must detect this and pad out the
	// values to match the expected slice size.
	if values < 59 {
		newValues := make([]uint64, 60)
		copy(newValues, v)
		v = newValues
	}

	return ClientV4Stats{
		Null:               v[1],
		Read:               v[2],
		Write:              v[3],
		Commit:             v[4],
		Open:               v[5],
		OpenConfirm:        v[6],
		OpenNoattr:         v[7],
		OpenDowngrade:      v[8],
		Close:              v[9],
		Setattr:            v[10],
		FsInfo:             v[11],
		Renew:              v[12],
		SetClientID:        v[13],
		SetClientIDConfirm: v[14],
		Lock:               v[15],
		Lockt:              v[16],
		Locku:              v[17],
		Access:             v[18],
		Getattr:            v[19],
		Lookup:             v[20],
		LookupRoot:         v[21],
		Remove:             v[22],
		Rename:             v[23],
		Link:               v[24],
		Symlink:            v[25],
		Create:             v[26],
		Pathconf:           v[27],
		StatFs:             v[28],
		ReadLink:           v[29],
		ReadDir:            v[30],
		ServerCaps:         v[31],
		DelegReturn:        v[32],
		GetACL:             v[33],
		SetACL:             v[34],
		FsLocations:        v[35],
		ReleaseLockowner:   v[36],
		Secinfo:            v[37],
		FsidPresent:        v[38],
		ExchangeID:         v[39],
		CreateSession:      v[40],
		DestroySession:     v[41],
		Sequence:           v[42],
		GetLeaseTime:       v[43],
		ReclaimComplete:    v[44],
		GetDeviceInfo:      v[45],
		LayoutGet:          v[46],
		LayoutCommit:       v[47],
		LayoutReturn:       v[48],
		SecinfoNoName:      v[49],
		TestStateID:        v[50],
		FreeStateID:        v[51],
		GetDeviceList:      v[52],
		BindConnToSession:  v[53],
		DestroyClientID:    v[54],
		Seek:               v[55],
		Allocate:           v[56],
		DeAllocate:         v[57],
		LayoutStats:        v[58],
		Clone:              v[59],
	}, nil
}

func parseServerV4Stats(v []uint64) (ServerV4Stats, error) {
	values := int(v[0])
	if len(v[1:]) != values || values != 2 {
		return ServerV4Stats{}, fmt.Errorf("invalid V4Stats line %q", v)
	}

	return ServerV4Stats{
		Null:     v[1],
		Compound: v[2],
	}, nil
}

func parseV4Ops(v []uint64) (V4Ops, error) {
	values := int(v[0])
	if len(v[1:]) != values || values < 39 {
		return V4Ops{}, fmt.Errorf("invalid V4Ops line %q", v)
	}

	// nfs v2.5.x 39field and >=v2.6.x 40 field;
	v40 := uint64(0)
	if values > 39 {
		v40 = v[40]
	}

	stats := V4Ops{
		Op0Unused:          v[1],
		Op1Unused:          v[2],
		Op2Future:          v[3],
		Access:             v[4],
		Close:              v[5],
		Commit:             v[6],
		Create:             v[7],
		DelegPurge:         v[8],
		DelegReturn:        v[9],
		GetAttr:            v[10],
		GetFH:              v[11],
		Link:               v[12],
		Lock:               v[13],
		Lockt:              v[14],
		Locku:              v[15],
		Lookup:             v[16],
		LookupRoot:         v[17],
		Nverify:            v[18],
		Open:               v[19],
		OpenAttr:           v[20],
		OpenConfirm:        v[21],
		OpenDgrd:           v[22],
		PutFH:              v[23],
		PutPubFH:           v[24],
		PutRootFH:          v[25],
		Read:               v[26],
		ReadDir:            v[27],
		ReadLink:           v[28],
		Remove:             v[29],
		Rename:             v[30],
		Renew:              v[31],
		RestoreFH:          v[32],
		SaveFH:             v[33],
		SecInfo:            v[34],
		SetAttr:            v[35],
		SetClientID:        v[36],
		SetClientIDConfirm: v[37],
		Verify:             v[38],
		Write:              v[39],
		RelLockOwner:       v40,
	}

	return stats, nil
}
