001// --------------------------------------------------------------------------------
002// Copyright 2002-2026 Echo Three, LLC
003//
004// Licensed under the Apache License, Version 2.0 (the "License");
005// you may not use this file except in compliance with the License.
006// You may obtain a copy of the License at
007//
008//     http://www.apache.org/licenses/LICENSE-2.0
009//
010// Unless required by applicable law or agreed to in writing, software
011// distributed under the License is distributed on an "AS IS" BASIS,
012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013// See the License for the specific language governing permissions and
014// limitations under the License.
015// --------------------------------------------------------------------------------
016
017package com.echothree.model.control.core.server.logic;
018
019import com.echothree.control.user.core.common.spec.EntityRefSpec;
020import com.echothree.control.user.core.common.spec.UniversalEntitySpec;
021import com.echothree.control.user.core.common.spec.UuidSpec;
022import com.echothree.model.control.core.common.ComponentVendors;
023import com.echothree.model.control.core.common.EventTypes;
024import com.echothree.model.control.core.common.exception.DeletedEntityRefException;
025import com.echothree.model.control.core.common.exception.DeletedUuidException;
026import com.echothree.model.control.core.common.exception.InvalidComponentVendorException;
027import com.echothree.model.control.core.common.exception.InvalidEntityTypeException;
028import com.echothree.model.control.core.common.exception.InvalidParameterCountException;
029import com.echothree.model.control.core.common.exception.UnknownEntityRefException;
030import com.echothree.model.control.core.common.exception.UnknownUuidException;
031import com.echothree.model.control.core.server.control.EntityInstanceControl;
032import com.echothree.model.control.core.server.control.EventControl;
033import com.echothree.model.data.core.server.entity.EntityInstance;
034import com.echothree.model.data.core.server.entity.EntityType;
035import com.echothree.util.common.message.ExecutionErrors;
036import com.echothree.util.common.persistence.BasePK;
037import com.echothree.util.server.control.BaseLogic;
038import com.echothree.util.server.message.ExecutionErrorAccumulator;
039import com.echothree.util.server.persistence.EntityIdGenerator;
040import javax.enterprise.context.ApplicationScoped;
041import javax.enterprise.inject.spi.CDI;
042import javax.inject.Inject;
043
044@ApplicationScoped
045public class EntityInstanceLogic
046        extends BaseLogic {
047
048    @Inject
049    protected EntityInstanceControl entityInstanceControl;
050
051    @Inject
052    protected EventControl eventControl;
053
054    protected EntityInstanceLogic() {
055        super();
056    }
057
058    public static EntityInstanceLogic getInstance() {
059        return CDI.current().select(EntityInstanceLogic.class).get();
060    }
061
062    public EntityInstance createEntityInstance(final ExecutionErrorAccumulator eea, final EntityType entityType,
063            final BasePK createdBy) {
064        var entityTypeDetail = entityType.getLastDetail();
065        var componentVendorName = entityTypeDetail.getComponentVendor().getLastDetail().getComponentVendorName();
066        EntityInstance entityInstance = null;
067
068        if(!ComponentVendors.ECHO_THREE.name().equals(componentVendorName)) {
069            var entityTypeName = entityTypeDetail.getEntityTypeName();
070            var entityIdGenerator = new EntityIdGenerator(componentVendorName, entityTypeName, 1); // TODO
071            var entityId = entityIdGenerator.getNextEntityId();
072            var basePK = new BasePK(componentVendorName, entityTypeName, entityId);
073            var event = eventControl.sendEvent(basePK, EventTypes.CREATE, null, null, createdBy);
074
075            entityInstance = event.getEntityInstance();
076        } else {
077            handleExecutionError(InvalidComponentVendorException.class, eea, ExecutionErrors.InvalidComponentVendor.name(), componentVendorName);
078        }
079
080        return entityInstance;
081    }
082
083    private boolean checkEntityTimeForDeletion(EntityInstance entityInstance) {
084        boolean wasDeleted = false;
085        var entityTime = eventControl.getEntityTime(entityInstance);
086
087        // If the EntityTime is null, then we're not going to find a DeletedTime, which means it must exist...
088        // do not mark it as having been deleted.
089        if(entityTime != null) {
090            // Check the DeletedTime...
091            if(entityTime.getDeletedTime() != null) {
092                // It's been deleted.
093                wasDeleted = true;
094            }
095        }
096
097        return wasDeleted;
098    }
099    
100    public EntityInstance getEntityInstanceByEntityRef(final ExecutionErrorAccumulator eea, final String entityRef) {
101        var entityInstance = entityInstanceControl.getEntityInstanceByEntityRef(entityRef);
102
103        if(entityInstance == null) {
104            handleExecutionError(UnknownEntityRefException.class, eea, ExecutionErrors.UnknownEntityRef.name(), entityRef);
105        } else {
106            var wasDeleted = checkEntityTimeForDeletion(entityInstance);
107
108            if(wasDeleted) {
109                entityInstance = null;
110                handleExecutionError(DeletedEntityRefException.class, eea, ExecutionErrors.DeletedEntityRef.name(), entityRef);
111            }
112        }
113
114
115        return entityInstance;
116    }
117
118    public EntityInstance getEntityInstanceByEntityRef(final ExecutionErrorAccumulator eea, final EntityRefSpec spec) {
119        return getEntityInstanceByEntityRef(eea, spec.getEntityRef());
120    }
121    
122    public EntityInstance getEntityInstanceByUuid(final ExecutionErrorAccumulator eea, final String uuid) {
123        var entityInstance = entityInstanceControl.getEntityInstanceByUuid(uuid);
124
125        if(entityInstance == null) {
126            handleExecutionError(UnknownUuidException.class, eea, ExecutionErrors.UnknownUuid.name(), uuid);
127        } else {
128            var wasDeleted = checkEntityTimeForDeletion(entityInstanceControl.getEntityInstanceByUuid(uuid));
129
130            if(wasDeleted) {
131                entityInstance = null;
132                handleExecutionError(DeletedUuidException.class, eea, ExecutionErrors.DeletedUuid.name(), uuid);
133            }
134        }
135
136        return entityInstance;
137    }
138
139    public EntityInstance getEntityInstanceByUuid(final ExecutionErrorAccumulator eea, final UuidSpec spec) {
140        return getEntityInstanceByUuid(eea, spec.getUuid());
141    }
142    
143    public EntityInstance getEntityInstance(final ExecutionErrorAccumulator eea, final String entityRef,
144            final String uuid, final String componentVendorName, final String... entityTypeNames) {
145        var parameterCount = countPossibleEntitySpecs(entityRef, uuid);
146        EntityInstance entityInstance = null;
147        
148        if(parameterCount == 1) {
149            if(entityRef != null) {
150                entityInstance = getEntityInstanceByEntityRef(eea, entityRef);
151            } else if(uuid != null) {
152                entityInstance = getEntityInstanceByUuid(eea, uuid);
153            }
154        } else {
155            handleExecutionError(InvalidParameterCountException.class, eea, ExecutionErrors.InvalidParameterCount.name());
156        }
157        
158        if((eea == null || !eea.hasExecutionErrors()) && componentVendorName != null && entityTypeNames.length > 0) {
159            var entityTypeDetail = entityInstance.getEntityType().getLastDetail();
160            var foundComponentVendorName = entityTypeDetail.getComponentVendor().getLastDetail().getComponentVendorName();
161            var foundEntityTypeName = entityTypeDetail.getEntityTypeName();
162            var found = false;
163            
164            if(foundComponentVendorName.equals(componentVendorName)) {
165                for(var entityTypeName : entityTypeNames) {
166                    if(entityTypeName.equals(foundEntityTypeName)) {
167                        found = true;
168                        break;
169                    }
170                }
171            }
172            
173            if(!found) {
174                handleExecutionError(InvalidEntityTypeException.class, eea, ExecutionErrors.InvalidEntityType.name(), foundComponentVendorName, foundEntityTypeName);
175                entityInstance = null;
176            }
177        }
178        
179        return entityInstance;
180    }
181    
182    public EntityInstance getEntityInstance(final ExecutionErrorAccumulator eea, final EntityRefSpec entityRefSpec,
183            final UuidSpec uuidSpec, final String componentVendorName, final String... entityTypeNames) {
184        return getEntityInstance(eea, entityRefSpec.getEntityRef(), uuidSpec.getUuid(),
185                componentVendorName, entityTypeNames);
186    }
187    
188    public EntityInstance getEntityInstance(final ExecutionErrorAccumulator eea, final UniversalEntitySpec universalEntitySpec) {
189        return getEntityInstance(eea, universalEntitySpec.getEntityRef(),
190                universalEntitySpec.getUuid(), null);
191    }
192    
193    public EntityInstance getEntityInstance(final ExecutionErrorAccumulator eea, final UniversalEntitySpec universalEntitySpec,
194            final String componentVendorName, final String... entityTypeNames) {
195        return getEntityInstance(eea, universalEntitySpec.getEntityRef(),
196                universalEntitySpec.getUuid(), componentVendorName, entityTypeNames);
197    }
198    
199    public int countPossibleEntitySpecs(final String entityRef, final String uuid) {
200        return (entityRef == null ? 0 : 1) + (uuid == null ? 0 : 1);
201    }
202    
203    public int countPossibleEntitySpecs(final EntityRefSpec entityRefSpec, final UuidSpec uuidSpec) {
204        return countPossibleEntitySpecs(entityRefSpec == null ? null : entityRefSpec.getEntityRef(),
205                uuidSpec == null ? null : uuidSpec.getUuid());
206    }
207    
208    public int countPossibleEntitySpecs(final UniversalEntitySpec universalEntitySpec) {
209        return universalEntitySpec == null ? 0 : countPossibleEntitySpecs(universalEntitySpec.getEntityRef(),
210                universalEntitySpec.getUuid());
211    }
212    
213    public String getEntityRefFromEntityInstance(EntityInstance entityInstance) {
214        var entityTypeDetail = entityInstance.getEntityType().getLastDetail();
215        var componentVendorDetail = entityTypeDetail.getComponentVendor().getLastDetail();
216        
217        return componentVendorDetail.getComponentVendorName() + '.' +
218                entityTypeDetail.getEntityTypeName() + '.' +
219                entityInstance.getEntityUniqueId();
220    }
221
222    public void deleteEntityInstance(final ExecutionErrorAccumulator eea, final EntityInstance entityInstance, final BasePK deletedBy) {
223        var componentVendorName = entityInstance.getEntityType().getLastDetail().getComponentVendor().getLastDetail().getComponentVendorName();
224
225        if(!ComponentVendors.ECHO_THREE.name().equals(componentVendorName)) {
226            entityInstanceControl.deleteEntityInstance(entityInstance, deletedBy);
227        } else {
228            handleExecutionError(InvalidComponentVendorException.class, eea, ExecutionErrors.InvalidComponentVendor.name(), componentVendorName);
229        }
230    }
231
232    public void removeEntityInstance(final ExecutionErrorAccumulator eea, final EntityInstance entityInstance) {
233        var componentVendorName = entityInstance.getEntityType().getLastDetail().getComponentVendor().getLastDetail().getComponentVendorName();
234
235        if(!ComponentVendors.ECHO_THREE.name().equals(componentVendorName)) {
236            entityInstanceControl.removeEntityInstance(entityInstance);
237        } else {
238            handleExecutionError(InvalidComponentVendorException.class, eea, ExecutionErrors.InvalidComponentVendor.name(), componentVendorName);
239        }
240    }
241
242}