entities/chunk.go
package entities
import (
"fmt"
"reflect"
)
// Chunk represents square area. Many Entities are deployed over Chunk.
type Chunk struct {
Base
ChunkPoint
Residence *DelegateResidence
Company *DelegateCompany
RailNode *DelegateRailNode
Parent *Cluster
InRailEdges map[uint]*DelegateRailEdge
OutRailEdges map[uint]*DelegateRailEdge
}
// NewChunk create Chunk on specified Cluster
func (m *Model) NewChunk(p *Cluster, o *Player) *Chunk {
ch := &Chunk{
Base: m.NewBase(CHUNK, o),
ChunkPoint: p.ChunkPoint,
}
ch.Init(m)
ch.Resolve(p)
m.Add(ch)
return ch
}
// B returns base information of this elements.
func (ch *Chunk) B() *Base {
return &ch.Base
}
// Init creates map.
func (ch *Chunk) Init(m *Model) {
ch.Base.Init(CHUNK, m)
ch.InRailEdges = make(map[uint]*DelegateRailEdge)
ch.OutRailEdges = make(map[uint]*DelegateRailEdge)
}
// Add deploy Entity over Chunk
func (ch *Chunk) Add(raw Entity) {
switch obj := raw.(type) {
case Localable:
ch.addLocalable(obj)
case Connectable:
ch.addConnectable(obj)
}
}
func (ch *Chunk) addLocalable(obj Localable) {
fieldName := obj.B().T.String()
oid := obj.B().OwnerID
nodeField := reflect.ValueOf(ch).Elem().FieldByName(fieldName)
if !nodeField.IsValid() {
return
}
if nodeField.IsNil() {
var pids []uint
var pid uint
if parent := ch.Parent.Parent; parent != nil && parent.Data[oid] != nil {
parentTarget := reflect.ValueOf(parent.Data[oid]).Elem().FieldByName(fieldName)
pid = uint(parentTarget.Elem().FieldByName("ID").Uint())
pids = append(parentTarget.Elem().FieldByName("ParentIDs").Interface().([]uint), pid)
}
node := reflect.New(delegateTypes[obj.B().T])
node.Elem().FieldByName("DelegateNode").Set(reflect.ValueOf(ch.NewDelegateNode(obj, pids)))
nodeField.Set(node)
}
nodeField.MethodByName("Add").Call([]reflect.Value{reflect.ValueOf(obj)})
}
func (ch *Chunk) addConnectable(obj Connectable) {
fromID := reflect.ValueOf(ch.ID)
toCh := ch.M.RootCluster.FindChunk(obj.To(), ch.Parent.Scale)
if toCh == nil {
// ex. no Platform, Gate in Chunk referred by Step
return
}
toID := reflect.ValueOf(toCh.ID)
outMapName := fmt.Sprintf("Out%ss", obj.B().T.String())
outMap := reflect.ValueOf(ch).Elem().FieldByName(outMapName)
if !outMap.IsValid() {
// ex. no OutSteps in DelegateNode
return
}
if !outMap.MapIndex(toID).IsValid() {
nodeFieldName := connectTypes[obj.B().T].String()
from := reflect.ValueOf(ch).Elem().FieldByName(nodeFieldName)
to := reflect.ValueOf(toCh).Elem().FieldByName(nodeFieldName)
edge := reflect.New(delegateTypes[obj.B().T])
edge.Elem().FieldByName("DelegateEdge").Set(reflect.ValueOf(ch.NewDelegateEdge(
obj, from.Interface().(delegateLocalable), to.Interface().(delegateLocalable))))
outMap.SetMapIndex(toID, edge)
inMapName := fmt.Sprintf("In%ss", obj.B().T.String())
inMap := reflect.ValueOf(toCh).Elem().FieldByName(inMapName)
inMap.SetMapIndex(fromID, edge)
if _, ok := obj.(*RailEdge); ok {
ch.setReverse(edge.Interface().(*DelegateRailEdge), toCh)
}
}
edge := outMap.MapIndex(toID)
edge.MethodByName("Add").Call([]reflect.Value{reflect.ValueOf(obj)})
}
func (ch *Chunk) setReverse(dre *DelegateRailEdge, toCh *Chunk) {
if reverse, ok := toCh.OutRailEdges[ch.ID]; ok {
dre.Reverse = reverse
dre.ReverseID = reverse.ID
reverse.Reverse = dre
reverse.ReverseID = dre.ID
}
}
// Remove undeploy Entity over Chunk
func (ch *Chunk) Remove(raw Entity) {
switch obj := raw.(type) {
case Localable:
ch.removeLocalable(obj)
case Connectable:
ch.removeConnectable(obj)
}
}
func (ch *Chunk) removeLocalable(obj Localable) {
fieldName := obj.B().T.String()
nodeField := reflect.ValueOf(ch).Elem().FieldByName(fieldName)
if !nodeField.IsValid() {
return
}
nodeField.MethodByName("Remove").Call([]reflect.Value{reflect.ValueOf(obj)})
if nodeField.Elem().FieldByName("List").Len() == 0 {
nodeField.Set(reflect.Zero(nodeField.Type()))
}
}
func (ch *Chunk) removeConnectable(obj Connectable) {
fromID := reflect.ValueOf(ch.ID)
toCh := ch.M.RootCluster.FindChunk(obj.To(), ch.Parent.Scale)
if toCh == nil {
// ex. no Platform, Gate in Chunk referred by Step
return
}
toID := reflect.ValueOf(toCh.ID)
outMapName := fmt.Sprintf("Out%ss", obj.B().T.String())
outMap := reflect.ValueOf(ch).Elem().FieldByName(outMapName)
if !outMap.IsValid() {
// ex. no OutSteps in DelegateNode
return
}
delegate := outMap.MapIndex(toID)
delegate.MethodByName("Remove").Call([]reflect.Value{reflect.ValueOf(obj)})
if delegate.Elem().FieldByName("List").Len() == 0 {
outMap.SetMapIndex(toID, reflect.ValueOf(nil))
inMapName := fmt.Sprintf("In%ss", obj.B().T.String())
inMap := reflect.ValueOf(toCh).Elem().FieldByName(inMapName)
inMap.SetMapIndex(fromID, reflect.ValueOf(nil))
}
}
// Has returns whether specified Entity is deployed over Chunk or not.
func (ch *Chunk) Has(raw Entity) bool {
id := reflect.ValueOf(raw.B().ID)
switch obj := raw.(type) {
case Localable:
fieldName := obj.B().T.String()
nodeField := reflect.ValueOf(ch).Elem().FieldByName(fieldName)
if !nodeField.IsValid() || !nodeField.Elem().IsValid() {
return false
}
return nodeField.Elem().FieldByName("List").MapIndex(id).IsValid()
case Connectable:
toCh := ch.M.RootCluster.FindChunk(obj.To(), ch.Parent.Scale)
if toCh == nil {
// ex. no Platform, Gate in Chunk referred by Step
return false
}
toID := reflect.ValueOf(toCh.ID)
outMapName := fmt.Sprintf("Out%ss", obj.B().T.String())
outMap := reflect.ValueOf(ch).Elem().FieldByName(outMapName)
if !outMap.IsValid() {
// ex. no OutSteps in DelegateNode
return false
}
return outMap.MapIndex(toID).IsValid()
}
return false
}
// IsEmpty returns whether any Entity is deployed over Chunk or not.
func (ch *Chunk) IsEmpty() bool {
return ch.RailNode == nil
}
// CheckDelete check remaining reference.
func (ch *Chunk) CheckDelete() error {
return nil
}
// BeforeDelete remove reference of related entity
func (ch *Chunk) BeforeDelete() {
ch.Parent.UnResolve(ch)
}
// Delete removes this entity with related ones.
func (ch *Chunk) Delete() {
ch.M.Delete(ch)
}
// Resolve set reference
func (ch *Chunk) Resolve(args ...Entity) {
for _, raw := range args {
switch obj := raw.(type) {
case *Cluster:
ch.Parent = obj
default:
panic(fmt.Errorf("invalid type: %T %+v", obj, obj))
}
}
}
// Export set delegate Entity to DelegateMap
func (ch *Chunk) Export(dm *DelegateMap) {
dm.Add(ch.Residence)
dm.Add(ch.Company)
dm.Add(ch.RailNode)
for _, re := range ch.InRailEdges {
dm.Add(re)
dm.Add(re.From)
}
for _, re := range ch.OutRailEdges {
dm.Add(re)
dm.Add(re.To)
}
}
// String represents status
func (ch *Chunk) String() string {
return fmt.Sprintf("%s(%d:%d):u=%d,r=%v,c=%v,rn=%v,i=%d,o=%d:%v", ch.T.Short(),
ch.Parent.Scale, ch.ID, ch.OwnerID,
ch.Residence, ch.Company, ch.RailNode,
len(ch.InRailEdges), len(ch.OutRailEdges), ch.ChunkPoint)
}