package npcs import ( "sort" "gitea.maximumdirect.net/eric/notarius/internal/core/source" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd" "gitea.maximumdirect.net/eric/notarius/internal/modules/dnd/npcs/identity" ) func canonicalizeResponse(response *extractionResponse, doc *source.SourceDocument) { if response == nil { return } for index := range response.NPCs { canonicalizeNPC(&response.NPCs[index]) } sort.SliceStable(response.NPCs, func(i, j int) bool { left, leftOK := earliestSourceIndex(doc, response.NPCs[i]) right, rightOK := earliestSourceIndex(doc, response.NPCs[j]) if leftOK != rightOK { return leftOK } if !leftOK { return false } return left < right }) } func canonicalizeNPC(npc *npcResponse) { if npc == nil { return } sort.SliceStable(npc.SourceRefs, func(i, j int) bool { left := npc.SourceRefs[i] right := npc.SourceRefs[j] if unitSortValue(left.StartUnitID) != unitSortValue(right.StartUnitID) { return unitSortValue(left.StartUnitID) < unitSortValue(right.StartUnitID) } return unitSortValue(left.EndUnitID) < unitSortValue(right.EndUnitID) }) npc.SourceRefs = dedupeSourceRefs(npc.SourceRefs) } func dedupeSourceRefs(refs []npcSourceRefResponse) []npcSourceRefResponse { if len(refs) < 2 { return refs } out := refs[:0] var previous npcSourceRefResponse for index, ref := range refs { if index > 0 && sameSourceRef(previous, ref) { continue } out = append(out, ref) previous = ref } return out } func sameSourceRef(left npcSourceRefResponse, right npcSourceRefResponse) bool { return left.StartUnitID == right.StartUnitID && left.EndUnitID == right.EndUnitID } func earliestSourceIndex(doc *source.SourceDocument, npc npcResponse) (int, bool) { earliest := 0 found := false for _, ref := range npc.SourceRefs { start := ref.StartUnitID end := ref.EndUnitID if start > 0 && end > 0 { startIndex, startOK := source.UnitIndex(doc, start) endIndex, endOK := source.UnitIndex(doc, end) if !startOK || !endOK || startIndex > endIndex { continue } if !found || startIndex < earliest { earliest = startIndex found = true } } } return earliest, found } func unitSortValue(value int) int { if value <= 0 { return int(^uint(0) >> 1) } return value } func canonicalNPCList(response extractionResponse, sourceID string) dnd.NPCList { if response.NPCs == nil { return dnd.NPCList{NPCs: nil} } npcs := make([]dnd.NPC, len(response.NPCs)) for index, npc := range response.NPCs { npcs[index] = dnd.NPC{ ID: identity.DeriveID(npc.Name), Name: npc.Name, SourceRefs: canonicalSourceRefs(npc.SourceRefs, sourceID), } } return dnd.NPCList{NPCs: npcs} } func canonicalSourceRefs(values []npcSourceRefResponse, sourceID string) []source.SourceRef { if values == nil { return nil } out := make([]source.SourceRef, len(values)) for index, value := range values { out[index] = source.SourceRef{ SourceID: sourceID, StartUnitID: value.StartUnitID, EndUnitID: value.EndUnitID, } } return out }