func parsePCFResponse(buf []byte) ([]*ibmmq.PCFParameter, bool) {
	var elem *ibmmq.PCFParameter
	var elemList []*ibmmq.PCFParameter
	var bytesRead int
	rc := false
	cfh, offset := ibmmq.ReadPCFHeader(buf)
	for i := 0; i < int(cfh.ParameterCount); i++ {
		elem, bytesRead = ibmmq.ReadPCFParameter(buf[offset:])
		offset += bytesRead
		elemList = append(elemList, elem)
		if elem.Type == ibmmq.MQCFT_GROUP {
			groupElem := elem
			for j := 0; j < int(groupElem.ParameterCount); j++ {
				elem, bytesRead = ibmmq.ReadPCFParameter(buf[offset:])
				offset += bytesRead
				groupElem.GroupList = append(groupElem.GroupList, elem)
			}
		}
	}
	if cfh.Control == ibmmq.MQCFC_LAST {
		rc = true
	}
	return elemList, rc
}
/*
Parse a PCF response message, returning the
elements. If an element represents a PCF group, that element
has the pieces of the group attached to itself. While
it is theoretically possible for groups to contain groups, MQ never
does that, so the code here does not need to recurse through multiple
levels.

Returns TRUE if this is the last response in a
set, based on the MQCFH.Control value.
package mqmetric
import (
	"bufio"
	"fmt"
	"os"
	"regexp"
	"strings"
	"github.com/ibm-messaging/mq-golang/ibmmq"
)
type MonElement struct {
	Parent      *MonType
	Description string  
	MetricName  string  
	Datatype    int32
	Values      map[string]int64
}
type MonType struct {
	Parent       *MonClass
	Name         string
	Description  string
	ObjectTopic  string  
	elementTopic string  
	Elements     map[int]*MonElement
	subHobj      map[string]ibmmq.MQObject
}
type MonClass struct {
	Parent      *AllMetrics
	Name        string
	Description string
	typesTopic  string
	flags       int
	Types       map[int]*MonType
}
type AllMetrics struct {
	Classes map[int]*MonClass
}
const QMgrMapKey = "@self"
var Metrics AllMetrics
var qList []string
func DiscoverAndSubscribe(queueList string, checkQueueList bool, metaPrefix string) error {
	var err error
	if err == nil {
		err = discoverStats(metaPrefix)
	}
	if err == nil {
		if checkQueueList {
			discoverQueues(queueList)
		} else {
			qList = strings.Split(queueList, ",")
		}
	}
	if err == nil {
		createSubscriptions()
	}
	return err
}
func discoverClasses(metaPrefix string) error {
	var data []byte
	var sub ibmmq.MQObject
	var err error
	var rootTopic string
	if metaPrefix == "" {
		rootTopic = "$SYS/MQ/INFO/QMGR/" + qMgr.Name + "/Monitor/METADATA/CLASSES"
	} else {
		rootTopic = metaPrefix + "/INFO/QMGR/" + qMgr.Name + "/Monitor/METADATA/CLASSES"
	}
	sub, err = subscribe(rootTopic)
	if err == nil {
		data, err = getMessage(true)
		sub.Close(0)
		elemList, _ := parsePCFResponse(data)
		for i := 0; i < len(elemList); i++ {
			if elemList[i].Type != ibmmq.MQCFT_GROUP {
				continue
			}
			group := elemList[i]
			cl := new(MonClass)
			classIndex := 0
			cl.Types = make(map[int]*MonType)
			cl.Parent = &Metrics
			for j := 0; j < len(group.GroupList); j++ {
				elem := group.GroupList[j]
				switch elem.Parameter {
				case ibmmq.MQIAMO_MONITOR_CLASS:
					classIndex = int(elem.Int64Value[0])
				case ibmmq.MQIAMO_MONITOR_FLAGS:
					cl.flags = int(elem.Int64Value[0])
				case ibmmq.MQCAMO_MONITOR_CLASS:
					cl.Name = elem.String[0]
				case ibmmq.MQCAMO_MONITOR_DESC:
					cl.Description = elem.String[0]
				case ibmmq.MQCA_TOPIC_STRING:
					cl.typesTopic = elem.String[0]
				default:
					return fmt.Errorf("Unknown parameter %d in class discovery", elem.Parameter)
				}
			}
			Metrics.Classes[classIndex] = cl
		}
	}
	subsOpened = true
	return err
}
func discoverTypes(cl *MonClass) error {
	var data []byte
	var sub ibmmq.MQObject
	var err error
	sub, err = subscribe(cl.typesTopic)
	if err == nil {
		data, err = getMessage(true)
		sub.Close(0)
		elemList, _ := parsePCFResponse(data)
		for i := 0; i < len(elemList); i++ {
			if elemList[i].Type != ibmmq.MQCFT_GROUP {
				continue
			}
			group := elemList[i]
			ty := new(MonType)
			ty.Elements = make(map[int]*MonElement)
			ty.subHobj = make(map[string]ibmmq.MQObject)
			typeIndex := 0
			ty.Parent = cl
			for j := 0; j < len(group.GroupList); j++ {
				elem := group.GroupList[j]
				switch elem.Parameter {
				case ibmmq.MQIAMO_MONITOR_TYPE:
					typeIndex = int(elem.Int64Value[0])
				case ibmmq.MQCAMO_MONITOR_TYPE:
					ty.Name = elem.String[0]
				case ibmmq.MQCAMO_MONITOR_DESC:
					ty.Description = elem.String[0]
				case ibmmq.MQCA_TOPIC_STRING:
					ty.elementTopic = elem.String[0]
				default:
					return fmt.Errorf("Unknown parameter %d in type discovery", elem.Parameter)
				}
			}
			cl.Types[typeIndex] = ty
		}
	}
	return err
}
func discoverElements(ty *MonType) error {
	var err error
	var data []byte
	var sub ibmmq.MQObject
	var elem *MonElement
	sub, err = subscribe(ty.elementTopic)
	if err == nil {
		data, err = getMessage(true)
		sub.Close(0)
		elemList, _ := parsePCFResponse(data)
		for i := 0; i < len(elemList); i++ {
			if elemList[i].Type == ibmmq.MQCFT_STRING && elemList[i].Parameter == ibmmq.MQCA_TOPIC_STRING {
				ty.ObjectTopic = elemList[i].String[0]
				continue
			}
			if elemList[i].Type != ibmmq.MQCFT_GROUP {
				continue
			}
			group := elemList[i]
			elem = new(MonElement)
			elementIndex := 0
			elem.Parent = ty
			elem.Values = make(map[string]int64)
			for j := 0; j < len(group.GroupList); j++ {
				e := group.GroupList[j]
				switch e.Parameter {
				case ibmmq.MQIAMO_MONITOR_ELEMENT:
					elementIndex = int(e.Int64Value[0])
				case ibmmq.MQIAMO_MONITOR_DATATYPE:
					elem.Datatype = int32(e.Int64Value[0])
				case ibmmq.MQCAMO_MONITOR_DESC:
					elem.Description = e.String[0]
				default:
					return fmt.Errorf("Unknown parameter %d in type discovery", e.Parameter)
				}
			}
			elem.MetricName = formatDescription(elem)
			ty.Elements[elementIndex] = elem
		}
	}
	return err
}
func discoverStats(metaPrefix string) error {
	var err error
	Metrics.Classes = make(map[int]*MonClass)
	err = discoverClasses(metaPrefix)
	if err == nil {
		for _, cl := range Metrics.Classes {
			err = discoverTypes(cl)
			if err == nil {
				for _, ty := range cl.Types {
					err = discoverElements(ty)
				}
			}
		}
	}
	return err
}
func discoverQueues(monitoredQueues string) error {
	var err error
	var elem *ibmmq.PCFParameter
	var datalen int
	if monitoredQueues == "" {
		return err
	}
	queues := strings.Split(monitoredQueues, ",")
	for i := 0; i < len(queues) && err == nil; i++ {
		var buf []byte
		pattern := queues[i]
		if strings.Count(pattern, "*") > 1 ||
			(strings.Count(pattern, "*") == 1 && !strings.HasSuffix(pattern, "*")) {
			return fmt.Errorf("Queue pattern '%s' is not valid", pattern)
		}
		putmqmd := ibmmq.NewMQMD()
		pmo := ibmmq.NewMQPMO()
		pmo.Options = ibmmq.MQPMO_NO_SYNCPOINT
		pmo.Options |= ibmmq.MQPMO_NEW_MSG_ID
		pmo.Options |= ibmmq.MQPMO_NEW_CORREL_ID
		pmo.Options |= ibmmq.MQPMO_FAIL_IF_QUIESCING
		putmqmd.Format = "MQADMIN"
		putmqmd.ReplyToQ = replyQObj.Name
		putmqmd.MsgType = ibmmq.MQMT_REQUEST
		putmqmd.Report = ibmmq.MQRO_PASS_DISCARD_AND_EXPIRY
		cfh := ibmmq.NewMQCFH()
		cfh.Command = ibmmq.MQCMD_INQUIRE_Q_NAMES
		pcfparm := new(ibmmq.PCFParameter)
		pcfparm.Type = ibmmq.MQCFT_STRING
		pcfparm.Parameter = ibmmq.MQCA_Q_NAME
		pcfparm.String = []string{pattern}
		cfh.ParameterCount++
		buf = append(buf, pcfparm.Bytes()...)
		pcfparm = new(ibmmq.PCFParameter)
		pcfparm.Type = ibmmq.MQCFT_INTEGER
		pcfparm.Parameter = ibmmq.MQIA_Q_TYPE
		pcfparm.Int64Value = []int64{int64(ibmmq.MQQT_LOCAL)}
		cfh.ParameterCount++
		buf = append(buf, pcfparm.Bytes()...)
		buf = append(cfh.Bytes(), buf...)
		err = cmdQObj.Put(putmqmd, pmo, buf)
		if err != nil {
			return err
		}
		getmqmd := ibmmq.NewMQMD()
		gmo := ibmmq.NewMQGMO()
		gmo.Options = ibmmq.MQGMO_NO_SYNCPOINT
		gmo.Options |= ibmmq.MQGMO_FAIL_IF_QUIESCING
		gmo.Options |= ibmmq.MQGMO_WAIT
		gmo.Options |= ibmmq.MQGMO_CONVERT
		gmo.WaitInterval = 30 * 1000
		buf = make([]byte, 32768)
		datalen, err = replyQObj.Get(getmqmd, gmo, buf)
		if err == nil {
			cfh, offset := ibmmq.ReadPCFHeader(buf)
			if cfh.CompCode != ibmmq.MQCC_OK {
				return fmt.Errorf("PCF command failed with CC %d RC %d", cfh.CompCode, cfh.Reason)
			} else {
				parmAvail := true
				bytesRead := 0
				for parmAvail && cfh.CompCode != ibmmq.MQCC_FAILED {
					elem, bytesRead = ibmmq.ReadPCFParameter(buf[offset:])
					offset += bytesRead
					if offset >= datalen {
						parmAvail = false
					}
					switch elem.Parameter {
					case ibmmq.MQCACF_Q_NAMES:
						if len(elem.String) == 0 {
							return fmt.Errorf("No queues matching '%s' exist", pattern)
						}
						for i := 0; i < len(elem.String); i++ {
							qList = append(qList, strings.TrimSpace(elem.String[i]))
						}
					}
				}
			}
		} else {
			return err
		}
	}
	return err
}
func createSubscriptions() error {
	var err error
	var sub ibmmq.MQObject
	for _, cl := range Metrics.Classes {
		for _, ty := range cl.Types {
			if strings.Contains(ty.ObjectTopic, "%s") {
				for i := 0; i < len(qList); i++ {
					topic := fmt.Sprintf(ty.ObjectTopic, qList[i])
					sub, err = subscribe(topic)
					ty.subHobj[qList[i]] = sub
				}
			} else {
				sub, err = subscribe(ty.ObjectTopic)
				ty.subHobj[QMgrMapKey] = sub
			}
			if err != nil {
				return fmt.Errorf("Error subscribing to %s: %v", ty.ObjectTopic, err)
			}
		}
	}
	return err
}
func ProcessPublications() error {
	var err error
	var data []byte
	var qName string
	var classidx int
	var typeidx int
	var elementidx int
	var value int64
	cnt := 0
	for err == nil {
		data, err = getMessage(false)
		if err == nil {
			cnt++
			elemList, _ := parsePCFResponse(data)
			values := make(map[int]int64)
			qName = ""
			for i := 0; i < len(elemList); i++ {
				switch elemList[i].Parameter {
				case ibmmq.MQCA_Q_MGR_NAME:
					_ = strings.TrimSpace(elemList[i].String[0])
				case ibmmq.MQCA_Q_NAME:
					qName = strings.TrimSpace(elemList[i].String[0])
				case ibmmq.MQCA_TOPIC_NAME:
					qName = strings.TrimSpace(elemList[i].String[0])
				case ibmmq.MQIACF_OBJECT_TYPE:
					_ = ibmmq.MQItoString("OT", int(elemList[i].Int64Value[0]))
				case ibmmq.MQIAMO_MONITOR_CLASS:
					classidx = int(elemList[i].Int64Value[0])
				case ibmmq.MQIAMO_MONITOR_TYPE:
					typeidx = int(elemList[i].Int64Value[0])
				case ibmmq.MQIAMO64_MONITOR_INTERVAL:
					_ = elemList[i].Int64Value[0]
				case ibmmq.MQIAMO_MONITOR_FLAGS:
					_ = int(elemList[i].Int64Value[0])
				default:
					value = elemList[i].Int64Value[0]
					elementidx = int(elemList[i].Parameter)
					values[elementidx] = value
				}
			}
			for key, newValue := range values {
				if elem, ok := Metrics.Classes[classidx].Types[typeidx].Elements[key]; ok {
					objectName := qName
					if objectName == "" {
						objectName = QMgrMapKey
					}
					if oldValue, ok := elem.Values[objectName]; ok {
						if elem.Datatype == ibmmq.MQIAMO_MONITOR_DELTA {
							value = oldValue + newValue
						} else {
							value = newValue
						}
					} else {
						value = newValue
					}
					elem.Values[objectName] = value
				}
			}
		} else {
			mqreturn := err.(*ibmmq.MQReturn)
			if mqreturn.MQCC == ibmmq.MQCC_FAILED && mqreturn.MQRC != ibmmq.MQRC_NO_MSG_AVAILABLE {
				return mqreturn
			}
		}
	}
	return nil
}