func BuildPathologyORRO02(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  ORR,
		TriggerEvent: "O02",
	}
	var segments []string
	msh, err := BuildMSH(msgTime, msgType, h)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build MSH segment")
	}
	segments = append(segments, msh)
	msa, err := BuildMSA(o.MessageControlIDOriginalOrder)
	if err != nil {
		return nil, errors.Wrap(err, "MSA build MSH segment")
	}
	segments = append(segments, msa)
	pid, err := BuildPID(p.Person)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PID segment")
	}
	segments = append(segments, pid)
	orc, err := BuildORC(o)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build ORC segment")
	}
	segments = append(segments, orc)
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}
BuildPathologyORRO02 builds and returns a HL7 ORR^O02 message.
package message
import (
	"bytes"
	"fmt"
	"strings"
	"text/template"
	"time"
	"github.com/pkg/errors"
	"github.com/google/simhospital/pkg/constants"
	"github.com/google/simhospital/pkg/hl7"
	"github.com/google/simhospital/pkg/ir"
	"github.com/google/simhospital/pkg/logging"
)
const (
	ADT = "ADT"
	ORM = "ORM"
	ORR = "ORR"
	ORU = "ORU"
	MDM = "MDM"
)
const DiagnosticServIDMDOC = "MDOC"
const SegmentTerminator = constants.SegmentTerminatorStr
const (
	listItemsSeparator           = "~"
	componentSeparator           = "^"
	escapedComponentSeparator    = "\\S\\"
	subComponentSeparator        = "&"
	escapedSubComponentSeparator = "\\T\\"
	lineBreak                    = "\n"
	escapedLineBreak             = "\\.br\\"
	backwardSlash                = "\\"
	escapedBackwardSlash         = "\\E\\"
)
type HL7Message struct {
	Type    *Type
	Message string
}
type Type struct {
	MessageType  string
	TriggerEvent string
}
type HeaderInfo struct {
	SendingApplication   string
	SendingFacility      string
	ReceivingApplication string
	ReceivingFacility    string
	MessageControlID string
}
var (
	log = logging.ForCallerPackage()
	funcMap = template.FuncMap{
		"HL7_date":     ToHL7Date,
		"HL7_repeated": toHL7RepeatedField,
		"expand_mrns":  expandMRNs,
		"HL7_unit":     toHL7Unit,
		"escape_HL7":   escapeHL7,
	}
)
func ToHL7Date(t ir.Formattable) (string, error) {
	nt, ok := t.(ir.NullTime)
	if ok && !nt.Valid {
		return "", nil
	}
	if nt.Location() != time.UTC {
		return "", fmt.Errorf("found time with non-UTC location: %v", nt)
	}
	if nt.Midnight {
		localT := t.In(hl7.Location)
		t = time.Date(localT.Year(), localT.Month(), localT.Day(), 0, 0, 0, 0, hl7.Location)
	}
	return t.In(hl7.Location).Format("20060102150405"), nil
}
func toHL7RepeatedField(s string) string {
	return strings.Replace(s, "\n", listItemsSeparator, -1)
}
func expandMRNs(mrns []string) (string, error) {
	fields := make([]string, len(mrns))
	for i, m := range mrns {
		f, err := executeTemplate(parsedCXMRNTemplate, struct {
			MRN string
		}{m})
		if err != nil {
			return "", errors.Wrap(err, "cannot expand MRNs")
		}
		fields[i] = f
	}
	return strings.Join(fields, listItemsSeparator), nil
}
func toHL7Unit(s string) string {
	return strings.Replace(s, componentSeparator, escapedComponentSeparator, -1)
}
func escapeHL7(s string) string {
	r := strings.NewReplacer(
		componentSeparator, escapedComponentSeparator,
		subComponentSeparator, escapedSubComponentSeparator,
		lineBreak, escapedLineBreak,
		backwardSlash, escapedBackwardSlash,
	)
	return r.Replace(s)
}
const (
	MSH             = "MSH"
	MSA             = "MSA"
	EVN             = "EVN"
	PID             = "PID"
	ORC             = "ORC"
	OBR             = "OBR"
	OBRClinicalNote = "OBRClinicalNote"
	OBX             = "OBX"
	OBXClinicalNote = "OBXClinicalNote"
	OBXForMDM       = "OBXForMDM"
	PV1             = "PV1"
	PV2             = "PV2"
	NK1             = "NK1"
	AL1             = "AL1"
	NTE             = "NTE"
	MRG             = "MRG"
	DG1             = "DG1"
	PD1             = "PD1"
	PR1             = "PR1"
	TXA             = "TXA"
)
const (
	locationTemplate      = "LocationTmpl"
	doctorTemplate        = "DoctorTmpl"
	personNameTemplate    = "PersonNameTmpl"
	addressTemplate       = "AddressTmpl"
	homeNumberTemplate    = "HomeNumberTmpl"
	ceTemplate            = "CETmpl"
	ceNoteTemplate        = "CENoteTmpl"
	ceAdmitReasonTemplate = "CEAdmitReasonTmpl"
	cxVisitTemplate       = "CXVisitTmpl"
	cxMRNTemplate         = "CXMRNTmpl"
	primFacTemplate       = "PrimFacTmpl"
	noteTemplate          = "NoteTmpl"
)
var (
	locationTmpl = "{{.Poc}}^{{.Room}}^{{.Bed}}^{{.Facility}}^^{{.LocationType}}^{{.Building}}^{{.Floor}}"
	doctorTmpl = "{{.ID}}^{{.Surname}}^{{.FirstName}}^^^{{.Prefix}}^^^DRNBR^PRSNL^^^ORGDR"
	personNameTmpl = "{{.Surname}}^{{.FirstName}}^{{.MiddleName}}^{{.Suffix}}^{{.Prefix}}^{{.Degree}}^CURRENT"
	addressTmpl = "{{.FirstLine}}^{{.SecondLine}}^{{.City}}^^{{.PostalCode}}^{{.Country}}^{{.Type}}"
	homeNumberTmpl = "{{.}}^HOME"
	ceTmpl = "{{escape_HL7 .ID}}^{{escape_HL7 .Text}}^{{.CodingSystem}}^^{{escape_HL7 .AlternateText}}"
	ceNoteTmpl = "{{.DocumentType}}^{{.DocumentType}}"
	ceAdmitReasonTmpl = "^{{.}}"
	primFacTmpl = "{{.Organization}}^^{{.ID}}"
	cxVisitTmpl = "{{.}}^^^^visitid"
	cxMRNTmpl = "{{.MRN}}^^^SIMULATOR MRN^MRN"
	stOBXNoteVal = "^^{{.ContentType}}^{{.DocumentEncoding}}^{{escape_HL7 .DocumentContent}}"
	parsedCXMRNTemplate = mustParseTemplateWithoutFuncs(cxMRNTemplate, cxMRNTmpl)
)
var templates = map[string]*template.Template{
	MSH: mustParseTemplate(MSH, "MSH|^~\\&|{{.Header.SendingApplication}}|{{.Header.SendingFacility}}|{{.Header.ReceivingApplication}}|{{.Header.ReceivingFacility}}|{{HL7_date .T}}||{{.MsgType.MessageType}}^{{.MsgType.TriggerEvent}}|{{.Header.MessageControlID}}|T|2.3|||AL||44|ASCII"),
	MSA: mustParseTemplate(MSA, "MSA|AA|{{.OrderMessageControlID}}"),
	EVN: mustParseTemplates(EVN, map[string]string{
		doctorTemplate: doctorTmpl,
		EVN:            `EVN|{{.MsgType.TriggerEvent}}|{{HL7_date .T}}|{{HL7_date .DateTimePlannedEvent}}||{{template "DoctorTmpl" .Operator}}|{{HL7_date .EventOccurredDateTime}}`,
	}),
	PID: mustParseTemplates(PID, map[string]string{
		personNameTemplate: personNameTmpl,
		addressTemplate:    addressTmpl,
		homeNumberTemplate: homeNumberTmpl,
		ceTemplate:         ceTmpl,
		cxMRNTemplate:      cxMRNTmpl,
		PID:                `PID|1|{{template "CXMRNTmpl" .}}|{{template "CXMRNTmpl" .}}~{{.NHS}}^^^NHSNBR^NHSNMBR||{{template "PersonNameTmpl" .}}||{{HL7_date .Birth}}|{{.Gender}}|||{{template "AddressTmpl" .Address}}||{{template "HomeNumberTmpl" .PhoneNumber}}|||||||||{{template "CETmpl" .Ethnicity}}|||||||{{HL7_date .DateOfDeath}}|{{.DeathIndicator}}`,
	}),
	MRG: mustParseTemplate(MRG, "MRG|{{expand_mrns .MRNs}}|"),
	ORC: mustParseTemplate(ORC, "ORC|{{.OrderControl}}|{{.Placer}}|{{.Filler}}||{{.OrderStatus}}||||{{HL7_date .OrderDateTime}}"),
	OBR: mustParseTemplates(OBR, map[string]string{
		ceTemplate:     ceTmpl,
		doctorTemplate: doctorTmpl,
		OBR:            `OBR|1|{{.Placer}}|{{.Filler}}|{{template "CETmpl" .OrderProfile}}||{{HL7_date .OrderDateTime}}|{{HL7_date .CollectedDateTime}}|||||||{{HL7_date .ReceivedInLabDateTime}}|{{.SpecimenSource}}|{{template "DoctorTmpl" .OrderingProvider}}||||||{{HL7_date .ReportedDateTime}}||{{.DiagnosticServID}}|{{.ResultsStatus}}||1`,
	}),
	OBRClinicalNote: mustParseTemplates(OBR, map[string]string{
		ceTemplate:     ceTmpl,
		doctorTemplate: doctorTmpl,
		OBR:            `OBR|1|{{.Placer}}|{{.DocumentID}}|{{template "CETmpl" .OrderProfile}}||{{HL7_date .OrderDateTime}}|{{HL7_date .CollectedDateTime}}|||||||{{HL7_date .ReceivedInLabDateTime}}|{{.SpecimenSource}}|{{template "DoctorTmpl" .OrderingProvider}}||||||{{HL7_date .ReportedDateTime}}||{{.DiagnosticServID}}|{{.ResultsStatus}}||1`,
	}),
	OBX: mustParseTemplates(OBX, map[string]string{
		ceTemplate: ceTmpl,
		OBX:        `OBX|{{.ID}}|{{.ValueType}}|{{template "CETmpl" .TestName}}||{{HL7_repeated .Value}}|{{HL7_unit .Unit}}|{{escape_HL7 .Range}}|{{.AbnormalFlag}}|||{{.Status}}|||{{HL7_date .ObservationDateTime}}||`,
	}),
	OBXClinicalNote: mustParseTemplates(OBX, map[string]string{
		ceNoteTemplate: ceNoteTmpl,
		noteTemplate:   stOBXNoteVal,
		doctorTemplate: doctorTmpl,
		OBX:            `OBX|{{.ID}}|{{.ValueType}}|{{template "CENoteTmpl" .ClinicalNote}}||{{template "NoteTmpl" .Content}}|||||||||{{HL7_date .ObservationDateTime}}||{{template "DoctorTmpl" .OrderingProvider}}`,
	}),
	OBXForMDM: mustParseTemplates(OBX, map[string]string{
		ceTemplate: ceTmpl,
		OBX:        `OBX|{{.ID}}|TX|{{template "CETmpl" .ObservationIdentifier}}|1|{{.Content}}||||||F||||||`,
	}),
	PV1: mustParseTemplates(PV1, map[string]string{
		locationTemplate: locationTmpl,
		doctorTemplate:   doctorTmpl,
		cxVisitTemplate:  cxVisitTmpl,
		PV1:              `PV1|1|{{.Class}}|{{template "LocationTmpl" .Location}}|28b||{{template "LocationTmpl" .PriorLocation}}|{{template "DoctorTmpl" .AttendingDoctor}}|||{{.HospitalService}}|{{template "LocationTmpl" .TemporaryLocation}}|||||||{{.Type}}|{{template "CXVisitTmpl" .VisitID}}||||||||||||||||||||||{{.AccountStatus}}|{{template "LocationTmpl" .PendingLocation}}|{{template "LocationTmpl" .PriorTemporaryLocation}}|{{HL7_date .AdmissionDate}}|{{HL7_date .DischargeDate}}|`,
	}),
	PV2: mustParseTemplates(PV2, map[string]string{
		locationTemplate:      locationTmpl,
		ceAdmitReasonTemplate: ceAdmitReasonTmpl,
		PV2:                   `PV2|{{template "LocationTmpl" .PriorPendingLocation}}||{{template "CEAdmitReasonTmpl" .AdmitReason}}|||||{{HL7_date .ExpectedAdmitDateTime}}|{{HL7_date .ExpectedDischargeDateTime}}`,
	}),
	NK1: mustParseTemplates(NK1, map[string]string{
		personNameTemplate: personNameTmpl,
		addressTemplate:    addressTmpl,
		homeNumberTemplate: homeNumberTmpl,
		ceTemplate:         ceTmpl,
		NK1:                `NK1|{{.ID}}|{{template "PersonNameTmpl" .}}|{{template "CETmpl" .Relationship}}|{{template "AddressTmpl" .Address}}|{{template "HomeNumberTmpl" .PhoneNumber}}||{{template "CETmpl" .ContactRole}}||||||||{{.Gender}}|`,
	}),
	AL1: mustParseTemplates(AL1, map[string]string{
		ceTemplate: ceTmpl,
		AL1:        `AL1|{{.ID}}|{{.Type}}|{{template "CETmpl" .Description}}|{{.Severity}}|{{.Reaction}}|{{HL7_date .IdentificationDateTime}}`,
	}),
	NTE: mustParseTemplate(NTE, `NTE|{{.ID}}||{{.Note}}|`),
	DG1: mustParseTemplates(DG1, map[string]string{
		ceTemplate:     ceTmpl,
		doctorTemplate: doctorTmpl,
		DG1:            `DG1|{{.ID}}|SNMCT|{{template "CETmpl" .Description}}|{{.Description.Text}}|{{HL7_date .DateTime}}|{{.Type}}|||||||||0|{{template "DoctorTmpl" .Clinician}}`,
	}),
	PD1: mustParseTemplates(PD1, map[string]string{
		primFacTemplate: primFacTmpl,
		PD1:             `PD1|||{{template "PrimFacTmpl" .PrimaryFacility}}|`,
	}),
	PR1: mustParseTemplates(PR1, map[string]string{
		ceTemplate:     ceTmpl,
		doctorTemplate: doctorTmpl,
		PR1:            `PR1|{{.ID}}|SNMCT|{{template "CETmpl" .Description}}|{{.Description.Text}}|{{HL7_date .DateTime}}|{{.Type}}||||||{{template "DoctorTmpl" .Clinician}}||0||`,
	}),
	TXA: mustParseTemplates(TXA, map[string]string{
		doctorTemplate: doctorTmpl,
		TXA:            `TXA|1|{{.DocumentType}}||{{HL7_date .ActivityDateTime}}|{{template "DoctorTmpl" .AttendingDoctor}}|||{{HL7_date .EditDateTime}}||||{{.UniqueDocumentNumber}}|||||{{.DocumentCompletionStatus}}||||||`,
	}),
}
func BuildDocumentNotificationMDMT02(h *HeaderInfo, p *ir.PatientInfo, d *ir.Document, eventTime time.Time, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  MDM,
		TriggerEvent: "T02",
	}
	var segments []string
	msh, err := BuildMSH(msgTime, msgType, h)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build MSH segment")
	}
	segments = append(segments, msh)
	evn, err := BuildEVN(eventTime, msgType, ir.NewInvalidTime(), p.AttendingDoctor, ir.NewInvalidTime())
	if err != nil {
		return nil, errors.Wrap(err, "cannot build EVN segment")
	}
	segments = append(segments, evn)
	pid, err := BuildPID(p.Person)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PID segment")
	}
	segments = append(segments, pid)
	pv1, err := BuildPV1(p)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PV1 segment")
	}
	segments = append(segments, pv1)
	txa, err := BuildTXA(p, d)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build TXA segment")
	}
	segments = append(segments, txa)
	for id, note := range d.ContentLine {
		obx, err := BuildOBXForMDM(id+1, d.ObservationIdentifier, note)
		if err != nil {
			return nil, errors.Wrap(err, "cannot build OBX segment")
		}
		segments = append(segments, obx)
	}
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}
func BuildResultORUR01(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  ORU,
		TriggerEvent: "R01",
	}
	segments, err := segmentsORU(h, p, o, msgTime, msgType)
	if err != nil {
		return nil, err
	}
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}
func BuildResultORUR03(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  ORU,
		TriggerEvent: "R03",
	}
	segments, err := segmentsORU(h, p, o, msgTime, msgType)
	if err != nil {
		return nil, err
	}
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}
func BuildResultORUR32(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  ORU,
		TriggerEvent: "R32",
	}
	segments, err := segmentsORU(h, p, o, msgTime, msgType)
	if err != nil {
		return nil, err
	}
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}
func segmentsORU(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time, msgType *Type) ([]string, error) {
	var segments []string
	msh, err := BuildMSH(msgTime, msgType, h)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build MSH segment")
	}
	segments = append(segments, msh)
	pid, err := BuildPID(p.Person)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PID segment")
	}
	segments = append(segments, pid)
	pv1, err := BuildPV1(p)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PV1 segment")
	}
	segments = append(segments, pv1)
	orc, err := BuildORC(o)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build ORC segment")
	}
	segments = append(segments, orc)
	obr, err := BuildOBR(o)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build OBR segment")
	}
	segments = append(segments, obr)
	if o.DiagnosticServID == DiagnosticServIDMDOC {
		return clinicalNotesOBX(o, segments)
	}
	return resultsOBX(o, segments)
}
func clinicalNotesOBX(o *ir.Order, segments []string) ([]string, error) {
	for _, result := range o.Results {
		for id := range result.ClinicalNote.Contents {
			obx, err := BuildOBXForClinicalNote(id+1, id, result, o)
			if err != nil {
				return nil, errors.Wrap(err, "cannot build OBX segment")
			}
			segments = append(segments, obx)
		}
	}
	return segments, nil
}
func resultsOBX(o *ir.Order, segments []string) ([]string, error) {
	for id, result := range o.Results {
		obx, err := BuildOBX(o.NumberOfPreviousResults+id+1, result, o)
		if err != nil {
			return nil, errors.Wrap(err, "cannot build OBX segment")
		}
		segments = append(segments, obx)
		for noteID, note := range result.Notes {
			nte, err := BuildNTE(noteID, note)
			if err != nil {
				return nil, errors.Wrap(err, "cannot build NTE segment")
			}
			segments = append(segments, nte)
		}
	}
	return segments, nil
}
func BuildOrderORMO01(h *HeaderInfo, p *ir.PatientInfo, o *ir.Order, msgTime time.Time) (*HL7Message, error) {
	msgType := &Type{
		MessageType:  ORM,
		TriggerEvent: "O01",
	}
	var segments []string
	msh, err := BuildMSH(msgTime, msgType, h)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build MSH segment")
	}
	segments = append(segments, msh)
	pid, err := BuildPID(p.Person)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PID segment")
	}
	segments = append(segments, pid)
	pv1, err := BuildPV1(p)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build PV1 segment")
	}
	segments = append(segments, pv1)
	orc, err := BuildORC(o)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build ORC segment")
	}
	segments = append(segments, orc)
	obr, err := BuildOBR(o)
	if err != nil {
		return nil, errors.Wrap(err, "cannot build OBR segment")
	}
	segments = append(segments, obr)
	for noteID, note := range o.NotesForORM {
		nte, err := BuildNTE(noteID, note)
		if err != nil {
			return nil, errors.Wrap(err, "cannot build NTE segment")
		}
		segments = append(segments, nte)
	}
	for id, result := range o.ResultsForORM {
		obx, err := BuildOBX(id+1, result, o)
		if err != nil {
			return nil, errors.Wrap(err, "cannot build OBX segment")
		}
		segments = append(segments, obx)
		for noteID, note := range result.Notes {
			nte, err := BuildNTE(noteID, note)
			if err != nil {
				return nil, errors.Wrap(err, "cannot build NTE segment")
			}
			segments = append(segments, nte)
		}
	}
	return &HL7Message{
		Type:    msgType,
		Message: strings.Join(segments, SegmentTerminator),
	}, nil
}