func (m *Message) messageTypeName() (string, error) {
	if m.msh.MessageType == nil || m.msh.MessageType.MessageCode == nil {
		return "", &BadMessageTypeError{}
	}
	name := string(*m.msh.MessageType.MessageCode)
	if m.msh.MessageType.TriggerEvent != nil {
		name += "_" + string(*m.msh.MessageType.TriggerEvent)
	}
	return name, nil
}
messageTypeName will return the name of the message struct that corresponds
to this message, via the type specified in the MSH segment.
Returns a BadMessageTypeError if there is no MessageType.
package hl7
import (
	"bytes"
	"fmt"
	"reflect"
	"regexp"
	"strings"
	"time"
	"github.com/pkg/errors"
	"golang.org/x/text/encoding/charmap"
	"golang.org/x/text/encoding"
	"golang.org/x/text/encoding/unicode"
	"github.com/google/simhospital/pkg/constants"
)
const (
	SegmentTerminator = constants.SegmentTerminator
	SegmentTerminatorStr = constants.SegmentTerminatorStr
	asciiNewLine byte = 0xa
)
var (
	Timezone string
	Location *time.Location
	encodings = map[string]encoding.Encoding{
		"ASCII":   encoding.Nop,
		"8859/1":  charmap.ISO8859_1,
		"8859/2":  charmap.ISO8859_2,
		"8859/3":  charmap.ISO8859_3,
		"8859/4":  charmap.ISO8859_4,
		"8859/5":  charmap.ISO8859_5,
		"8859/6":  charmap.ISO8859_6,
		"8859/7":  charmap.ISO8859_7,
		"8859/8":  charmap.ISO8859_8,
		"8859/9":  charmap.ISO8859_9,
		"8859/15": charmap.ISO8859_15,
		"UNICODE":       encoding.Nop,  
		"UNICODE UTF-8": encoding.Nop,  
	}
	null = []byte(`""`)
	segmentTypeRegex = regexp.MustCompile(`^[a-zA-Z0-9]{3}$`)
	messageTypeRegex = regexp.MustCompile(`^[a-zA-Z0-9]{3}_[a-zA-Z0-9]{3}$`)
	DefaultDelimiters = &Delimiters{
		Field:        '|',
		Component:    '^',
		Subcomponent: '&',
		Repetition:   '~',
		Escape:       '\\',
	}
	DefaultContextWithoutLocation = &Context{
		Decoder:    unicode.UTF8.NewDecoder(),
		Delimiters: DefaultDelimiters,
		Rewrite:    []Rewrite{NopRewrite},
		Nesting:    0,
	}
)
func TimezoneAndLocation(tz string) error {
	loc, err := time.LoadLocation(tz)
	if err != nil {
		return errors.Wrapf(err, "invalid timezone: %q", tz)
	}
	Timezone = tz
	Location = loc
	return nil
}
type Segment interface {
	SegmentName() string
}
type Context struct {
	Decoder    *encoding.Decoder
	Rewrite    []Rewrite
	Delimiters *Delimiters
	Nesting int
	TimezoneLoc     *time.Location
	IncludeTimezone bool
}
func (c *Context) Nested() *Context {
	newContext := *c
	newContext.Nesting++
	return &newContext
}
type ParseMessageOptions struct {
	TimezoneLoc *time.Location
	SegmentTerminator []byte
	Rewrites          *[]Rewrite
	AllowNullHeader   bool
}
func NewParseMessageOptions() *ParseMessageOptions {
	return &ParseMessageOptions{
		TimezoneLoc:       Location,
		SegmentTerminator: []byte{SegmentTerminator},
		Rewrites:          &[]Rewrite{NopRewrite},
	}
}
type Delimiters struct {
	Field        byte
	Component    byte
	Subcomponent byte
	Repetition   byte
	Escape       byte
}
var _ Primitive = (*Delimiters)(nil)
func (d *Delimiters) Marshal(_ *Context) ([]byte, error) {
	return []byte{d.Component, d.Repetition, d.Escape, d.Subcomponent}, nil
}
func (d *Delimiters) Unmarshal(field []byte, c *Context) error {
	if len(field) < 4 {
		return ErrBadValue
	}
	*d = Delimiters{
		Field:        c.Delimiters.Field,
		Component:    field[0],
		Repetition:   field[1],
		Escape:       field[2],
		Subcomponent: field[3],
	}
	c.Delimiters = d
	return nil
}
func (d Delimiters) splitFields(segment Token) []Token {
	return split(segment, d.Field)
}
func (d Delimiters) splitComponents(field Token, nesting int) []Token {
	switch nesting {
	case 0:
		return split(field, d.Component)
	case 1:
		return split(field, d.Subcomponent)
	default:
		return []Token{field}
	}
}
func (d Delimiters) joinComponents(components [][]byte, nesting int) []byte {
	if len(components) == 1 {
		return components[0]
	}
	switch nesting {
	case 0:
		return bytes.Join(components, []byte{d.Component})
	case 1:
		return bytes.Join(components, []byte{d.Subcomponent})
	default:
		panic("Too many nesting levels")
	}
}
func (d Delimiters) splitRepeated(field Token) []Token {
	return split(field, d.Repetition)
}
func (d Delimiters) joinRepeated(repetitions [][]byte) []byte {
	return bytes.Join(repetitions, []byte{d.Repetition})
}
func split(input Token, delimiter byte) []Token {
	return splitMultiCharDelimiter(input, []byte{delimiter})
}
func splitMultiCharDelimiter(input Token, delimiter []byte) []Token {
	r := make([]Token, 0, 16)
	start := 0
	tks := bytes.Split(input.Value, delimiter)
	for _, tk := range tks {
		r = append(r, Token{tk, input.Offset + start, input.Location})
		start += len(tk) + len(delimiter)
	}
	return r
}
type BadSegmentError struct {
	Name string
}
func (e *BadSegmentError) Error() string {
	return fmt.Sprintf("bad segment %q", e.Name)
}
type BadMessageTypeError struct {
	Name string
}
type MessageType interface {
	MessageTypeName() string
}
func (e *BadMessageTypeError) Error() string {
	errMsg := "bad message type"
	if segmentTypeRegex.MatchString(e.Name) || messageTypeRegex.MatchString(e.Name) {
		return fmt.Sprintf("%s: %s", errMsg, e.Name)
	}
	return errMsg
}
func toSegment(v reflect.Value) (Segment, bool) {
	segment := reflect.ValueOf((*Segment)(nil)).Type().Elem()
	if v.Type().Implements(segment) {
		return v.Interface().(Segment), true
	}
	if reflect.PtrTo(v.Type()).Implements(segment) {
		return v.Addr().Interface().(Segment), true
	}
	return nil, false
}
var ErrBadValue = errors.New("bad value for primitive HL7 type")
type ParseError struct {
	Offset   int
	Location string
	Cause    error
}
func (e *ParseError) Error() string {
	return fmt.Sprintf("error in %s: %v", e.Location, e.Cause.Error())
}
type ParseErrors []ParseError
func (e ParseErrors) Error() string {
	s := make([]string, len(e))
	for i, err := range e {
		s[i] = err.Error()
	}
	return fmt.Sprintf("errors (%d): %s", len(e), strings.Join(s, ", "))
}
type Token struct {
	Value  []byte
	Offset int
	Location string
}
func (t Token) Error(err error) *ParseError {
	return &ParseError{t.Offset, t.Location, err}
}
func (t Token) Errors(err error) ParseErrors {
	return ParseErrors{*t.Error(err)}
}
func parseTag(f reflect.StructField) (string, bool) {
	parts := strings.SplitN(f.Tag.Get("hl7"), ",", 2)
	if len(parts) == 2 {
		return parts[1], parts[0] == "true"
	}
	return parts[0], false
}
func fieldLocation(t reflect.Type, i int) string {
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}
	name, _ := parseTag(t.Field(i))
	if name != "" {
		name = "-" + name
	}
	return fmt.Sprintf("%s-%d%s", t.Name(), i+1, name)
}
func appendLocation(location string, next string) string {
	if location != "" {
		return location + "/" + next
	}
	return next
}
type Message struct {
	*Context
	Segments []Token
	msh      MSH
}