package elastigo

import (
	"fmt"
	"strconv"
	"strings"
)

// newCatNodeInfo returns an instance of CatNodeInfo populated with the
// the information in the cat output indexLine which contains the
// specified fields. An err is returned if a field is not known.
func newCatNodeInfo(fields []string, indexLine string) (catNode *CatNodeInfo, err error) {

	split := strings.Fields(indexLine)
	catNode = &CatNodeInfo{}

	// Check the fields length compared to the number of stats
	lf, ls := len(fields), len(split)
	if lf > ls {
		return nil, fmt.Errorf("Number of fields (%d) greater than number of stats (%d)", lf, ls)
	}

	// Populate the appropriate field in CatNodeInfo
	for i, field := range fields {

		switch field {
		case "id", "nodeId":
			catNode.Id = split[i]
		case "pid", "p":
			catNode.PID = split[i]
		case "host", "h":
			catNode.Host = split[i]
		case "ip", "i":
			catNode.IP = split[i]
		case "port", "po":
			catNode.Port = split[i]
		case "version", "v":
			catNode.Version = split[i]
		case "build", "b":
			catNode.Build = split[i]
		case "jdk", "j":
			catNode.JDK = split[i]
		case "disk.avail", "d", "disk", "diskAvail":
			catNode.DiskAvail = split[i]
		case "heap.current", "hc", "heapCurrent":
			catNode.HeapCur = split[i]
		case "heap.percent", "hp", "heapPercent":
			catNode.HeapPerc = split[i]
		case "heap.max", "hm", "heapMax":
			catNode.HeapMax = split[i]
		case "ram.current", "rc", "ramCurrent":
			catNode.RamCur = split[i]
		case "ram.percent", "rp", "ramPercent":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.RamPerc = int16(val)
		case "ram.max", "rm", "ramMax":
			catNode.RamMax = split[i]
		case "file_desc.current", "fdc", "fileDescriptorCurrent":
			catNode.FileDescCur = split[i]
		case "file_desc.percent", "fdp", "fileDescriptorPercent":
			catNode.FileDescPerc = split[i]
		case "file_desc.max", "fdm", "fileDescriptorMax":
			catNode.FileDescMax = split[i]
		case "load", "l":
			catNode.Load = split[i]
		case "uptime", "u":
			catNode.UpTime = split[i]
		case "node.role", "r", "role", "dc", "nodeRole":
			catNode.NodeRole = split[i]
		case "master", "m":
			catNode.Master = split[i]
		case "name", "n":
			catNode.Name = strings.Join(split[i:], " ")
		case "completion.size", "cs", "completionSize":
			catNode.CmpltSize = split[i]
		case "fielddata.memory_size", "fm", "fielddataMemory":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.FieldMem = val
		case "fielddata.evictions", "fe", "fieldataEvictions":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.FieldEvict = val
		case "filter_cache.memory_size", "fcm", "filterCacheMemory":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.FiltMem = val
		case "filter_cache.evictions", "fce", "filterCacheEvictions":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.FiltEvict = val
		case "flush.total", "ft", "flushTotal":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.FlushTotal = val
		case "flush.total_time", "ftt", "flushTotalTime":
			catNode.FlushTotalTime = split[i]
		case "get.current", "gc", "getCurrent":
			catNode.GetCur = split[i]
		case "get.time", "gti", "getTime":
			catNode.GetTime = split[i]
		case "get.total", "gto", "getTotal":
			catNode.GetTotal = split[i]
		case "get.exists_time", "geti", "getExistsTime":
			catNode.GetExistsTime = split[i]
		case "get.exists_total", "geto", "getExistsTotal":
			catNode.GetExistsTotal = split[i]
		case "get.missing_time", "gmti", "getMissingTime":
			catNode.GetMissingTime = split[i]
		case "get.missing_total", "gmto", "getMissingTotal":
			catNode.GetMissingTotal = split[i]
		case "id_cache.memory_size", "im", "idCacheMemory":
			val, err := strconv.Atoi(split[i])
			if err != nil {
				return nil, err
			}
			catNode.IDCacheMemory = val
		case "indexing.delete_current", "idc", "indexingDeleteCurrent":
			catNode.IdxDelCur = split[i]
		case "indexing.delete_time", "idti", "indexingDeleteime":
			catNode.IdxDelTime = split[i]
		case "indexing.delete_total", "idto", "indexingDeleteTotal":
			catNode.IdxDelTotal = split[i]
		case "indexing.index_current", "iic", "indexingIndexCurrent":
			catNode.IdxIdxCur = split[i]
		case "indexing.index_time", "iiti", "indexingIndexTime":
			catNode.IdxIdxTime = split[i]
		case "indexing.index_total", "iito", "indexingIndexTotal":
			catNode.IdxIdxTotal = split[i]
		case "merges.current", "mc", "mergesCurrent":
			catNode.MergCur = split[i]
		case "merges.current_docs", "mcd", "mergesCurrentDocs":
			catNode.MergCurDocs = split[i]
		case "merges.current_size", "mcs", "mergesCurrentSize":
			catNode.MergCurSize = split[i]
		case "merges.total", "mt", "mergesTotal":
			catNode.MergTotal = split[i]
		case "merges.total_docs", "mtd", "mergesTotalDocs":
			catNode.MergTotalDocs = split[i]
		case "merges.total_size", "mts", "mergesTotalSize":
			catNode.MergTotalSize = split[i]
		case "merges.total_time", "mtt", "mergesTotalTime":
			catNode.MergTotalTime = split[i]
		case "percolate.current", "pc", "percolateCurrent":
			catNode.PercCur = split[i]
		case "percolate.memory_size", "pm", "percolateMemory":
			catNode.PercMem = split[i]
		case "percolate.queries", "pq", "percolateQueries":
			catNode.PercQueries = split[i]
		case "percolate.time", "pti", "percolateTime":
			catNode.PercTime = split[i]
		case "percolate.total", "pto", "percolateTotal":
			catNode.PercTotal = split[i]
		case "refresh.total", "rto", "refreshTotal":
			catNode.RefreshTotal = split[i]
		case "refresh.time", "rti", "refreshTime":
			catNode.RefreshTime = split[i]
		case "search.fetch_current", "sfc", "searchFetchCurrent":
			catNode.SearchFetchCur = split[i]
		case "search.fetch_time", "sfti", "searchFetchTime":
			catNode.SearchFetchTime = split[i]
		case "search.fetch_total", "sfto", "searchFetchTotal":
			catNode.SearchFetchTotal = split[i]
		case "search.open_contexts", "so", "searchOpenContexts":
			catNode.SearchOpenContexts = split[i]
		case "search.query_current", "sqc", "searchQueryCurrent":
			catNode.SearchQueryCur = split[i]
		case "search.query_time", "sqti", "searchQueryTime":
			catNode.SearchQueryTime = split[i]
		case "search.query_total", "sqto", "searchQueryTotal":
			catNode.SearchQueryTotal = split[i]
		case "segments.count", "sc", "segmentsCount":
			catNode.SegCount = split[i]
		case "segments.memory", "sm", "segmentsMemory":
			catNode.SegMem = split[i]
		case "segments.index_writer_memory", "siwm", "segmentsIndexWriterMemory":
			catNode.SegIdxWriterMem = split[i]
		case "segments.index_writer_max_memory", "siwmx", "segmentsIndexWriterMaxMemory":
			catNode.SegIdxWriterMax = split[i]
		case "segments.version_map_memory", "svmm", "segmentsVersionMapMemory":
			catNode.SegVerMapMem = split[i]
		default:
			return nil, fmt.Errorf("Invalid cat nodes field: %s", field)
		}
	}

	return catNode, nil
}

// GetCatNodeInfo issues an elasticsearch cat nodes request with the specified
// fields and returns a list of CatNodeInfos, one for each node, whose requested
// members are populated with statistics. If fields is nil or empty, the default
// cat output is used.
// NOTE: if you include the name field, make sure it is the last field in the
// list, because name values can contain spaces which screw up the parsing
func (c *Conn) GetCatNodeInfo(fields []string) (catNodes []CatNodeInfo, err error) {

	catNodes = make([]CatNodeInfo, 0)

	// If no fields have been specified, use the "default" arrangement
	if len(fields) < 1 {
		fields = []string{"host", "ip", "heap.percent", "ram.percent", "load",
			"node.role", "master", "name"}
	}

	// Issue a request for stats on the requested fields
	args := map[string]interface{}{
		"bytes": "b",
		"h":     strings.Join(fields, ","),
	}
	indices, err := c.DoCommand("GET", "/_cat/nodes/", args, nil)
	if err != nil {
		return catNodes, err
	}

	// Create a CatIndexInfo for each line in the response
	indexLines := strings.Split(string(indices[:]), "\n")
	for _, index := range indexLines {

		// Ignore empty output lines
		if len(index) < 1 {
			continue
		}

		// Create a CatNodeInfo and append it to the result
		info, err := newCatNodeInfo(fields, index)
		if info != nil {
			catNodes = append(catNodes, *info)
		} else if err != nil {
			return catNodes, err
		}
	}
	return catNodes, nil
}
