package eu.reply.supplychain.scheduler.entities.activity;

import java.time.Duration;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.persistence.CascadeType;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.Index;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonIgnore;

import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.sce.backend.core.entity.StandardEntity;
import eu.reply.sce.backend.core.entity.configuration.StringifyAttributes;
import eu.reply.sce.backend.core.sequence.businesskey.BusinessKeySequence;
import eu.reply.supplychain.calendar.CalendarUtils;
import eu.reply.supplychain.calendar.entities.BusinessCalendar;
import eu.reply.supplychain.core.Constants;
import eu.reply.supplychain.scheduler.entities.effort.EffortSize;
import eu.reply.supplychain.scheduler.entities.resource.Resource;
import eu.reply.supplychain.scheduler.enumerations.ActivityAllocationStatus;
import eu.reply.supplychain.scheduler.enumerations.ActivityStatus;

@Entity
@Table(uniqueConstraints = {@UniqueConstraint(columnNames = {"code"})},
    indexes = @Index(columnList = "entityId"))
@NamedQueries({
    @NamedQuery(name = Activity.Q_FIND_BY_CODE, query = "select i from Activity i where i.code = :code"),

    @NamedQuery(name = Activity.Q_FIND_BY_REF,
        query = "select i from Activity i "
                + "where referencedEntity.entityId = :entityId "
                + "and referencedEntity.entityName = :entityName "
                + "and referencedEntity.module = :module"),

    @NamedQuery(name = Activity.Q_FIND_BY_CODE_AND_REF,
        query = "select i from Activity i  "
                + "where i.code = :code "
                + "and referencedEntity.entityId = :entityId "
                + "and referencedEntity.entityName = :entityName "
                + "and referencedEntity.module = :module"),

    @NamedQuery(name = Activity.Q_FIND_BY_REF_LIST_BOUNDS, query =
        "select i from Activity i  "
        + "where i.referencedEntity.entityId in :ids "
        + "and i.referencedEntity.entityName = :entityName "
        + "and i.referencedEntity.module = :module "
        + "and i.upperBoundTo > :from and i.lowerBoundFrom < :to ")}

)
@BusinessKeySequence(prefix = "ACT", length = Constants.DEFAULT_BUSINESS_KEY_LENGTH)
@StringifyAttributes(attributes = {"code"})
public class Activity extends StandardEntity {

    private static final long serialVersionUID = 7317684989250232647L;

    public static final String Q_FIND_BY_CODE = "Activity.Q_FIND_BY_CODE";
    public static final String Q_FIND_BY_REF = "Activity.Q_FIND_BY_REF";
    public static final String Q_FIND_BY_CODE_AND_REF = "Activity.Q_FIND_BY_CODE_AND_REF";
    public static final String Q_FIND_BY_REF_LIST_BOUNDS = "Activity.Q_FIND_BY_REF_LIST_BOUNDS";

    @NotNull
    private String code;
    private String description;

    @ManyToOne(optional = true)
    private ActivityType activityType;

    @OneToMany(mappedBy = "activity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = false)
    private final Set<EffortSize> efforts = new HashSet<>();

    private EntityReference referencedEntity;

    @OneToMany(mappedBy = "activity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<ResourceUsage> resourceUsages = new HashSet<>();

    @OneToMany(mappedBy = "activity", fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<TimeSlot> timeSlots = new TreeSet<>();

    @ElementCollection(fetch = FetchType.EAGER)
    private final Set<EntityReference> relatedEntities = new HashSet<>();

    private Long totalEffortSec = 0L;
    private Long allocatedSec = 0L;
    private Long elapsedSec = 0L;

    @NotNull
    @Enumerated(EnumType.STRING)
    private ActivityAllocationStatus resourcesAllocationStatus;
    @NotNull
    @Enumerated(EnumType.STRING)
    private ActivityAllocationStatus timeSlotAllocationStatus;

    @NotNull
    @Enumerated(EnumType.STRING)
    private ActivityStatus status;

    @Temporal(TemporalType.TIMESTAMP)
    private Calendar elapsedFrom;
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar elapsedTo;

    @Temporal(TemporalType.TIMESTAMP)
    private Calendar closureDate;
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar cancellationDate;
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar completionDate;

    @Temporal(TemporalType.TIMESTAMP)
    private Calendar upperBoundTo;
    private Boolean upperBoundNow = false;
    @Temporal(TemporalType.TIMESTAMP)
    private Calendar lowerBoundFrom;
    private Boolean lowerBoundNow = false;

    @Temporal(TemporalType.TIMESTAMP)
    private Calendar maxTimeToModify;

    @Temporal(TemporalType.TIMESTAMP)
    private Calendar lastOperationDate;

    private String screenLabel;

    private boolean toReschedule = false;

    //
    // Constructors
    //
    public Activity() {
        super();
        resourcesAllocationStatus = ActivityAllocationStatus.PENDING;
        timeSlotAllocationStatus = ActivityAllocationStatus.PENDING;
        status = ActivityStatus.TO_PLAN;
    }

    public Calendar getMaxTimeToModify() {
        return maxTimeToModify;
    }

    public void setMaxTimeToModify(final Calendar maxTimeToModify) {
        this.maxTimeToModify = maxTimeToModify;
    }

    public Activity(final String code, final String description) {
        this();
        this.code = code;
        this.description = description;
    }

    public Activity(final ActivityType activityType, final String code, final String description) {
        this(code, description);
        this.activityType = activityType;
    }

    public Activity(final ActivityType activityType, final String code, final String description,
                    final Collection<EffortSize> efforts) {
        this(code, description);
        this.activityType = activityType;
        addAllEfforts(efforts);
    }

    //
    // Resource Usages
    //
    public boolean addResourceUsage(@Nonnull final ResourceUsage usage) {
        Objects.requireNonNull(usage, "usage");

        if (resourceUsages == null) {
            resourceUsages = new HashSet<>();
        }

        if (this.addChild(usage, resourceUsages)) {
            usage.setActivity(this);
            return true;
        }

        return false;
    }

    public boolean removeResourceUsage(@Nonnull final ResourceUsage usage) {
        if ((resourceUsages == null) || (usage == null)) {
            return false;
        }

        if (this.removeChild(usage, resourceUsages)) {
            usage.setActivity(null);
            return true;
        }

        return false;
    }

    //
    // Time Slots
    //
    public boolean addTimeSlot(@Nonnull final TimeSlot timeSlot) {
        Objects.requireNonNull(timeSlot, "timeSlot");

        if (timeSlots == null) {
            timeSlots = new TreeSet<>();
        }

        if (this.addChild(timeSlot, timeSlots)) {
            timeSlot.setActivity(this);
            return true;
        }

        return false;
    }

    public boolean removeTimeSlot(@Nonnull final TimeSlot timeSlot) {
        if ((timeSlots == null) || (timeSlot == null)) {
            return false;
        }

        if (this.removeChild(timeSlot, timeSlots)) {
            timeSlot.setActivity(null);
            return true;
        }

        return false;
    }

    private void computeElapsed() {
        elapsedFrom = null;
        elapsedTo = null;
        // Computes min-from and max-to
        for (final TimeSlot t : getTimeSlots()) {
            elapsedFrom = CalendarUtils.nvlLeast(elapsedFrom, CalendarUtils.clone(t.getFrom()));
            elapsedTo = CalendarUtils.nvlGreatest(elapsedTo, CalendarUtils.clone(t.getTo()));
        }
        // Computes elapsed
        if ((elapsedFrom != null) && (elapsedTo != null)) {
            elapsedSec = Duration.between(elapsedFrom.toInstant(), elapsedTo.toInstant()).getSeconds();
        } else {
            elapsedSec = 0L;
        }
    }

    private void computeAllocatedStatus() {
        if (allocatedSec == 0L) {
            setTimeSlotAllocationStatus(ActivityAllocationStatus.PENDING);
        } else if (allocatedSec.compareTo(totalEffortSec) < 0) {
            setTimeSlotAllocationStatus(ActivityAllocationStatus.PARTIAL);
        } else {
            setTimeSlotAllocationStatus(ActivityAllocationStatus.COMPLETE);
        }
    }

    private void computeAllocated() {
        allocatedSec = 0L;
        // Computes total duration summing time slots and
        for (final TimeSlot t : getTimeSlots()) {
            allocatedSec += t.computeDuration().getSeconds();
        }
    }

    /**
     * Computes all summary indicators from Time slots
     */
    public void computeFromTimeSlot() {
        computeAllocated();
        computeElapsed();
        computeAllocatedStatus();
    }

    /**
     * Checks if this activity is using given resource
     *
     * @param resource Resource to be searched
     * @return true if the activity has a usage referring given resource, false
     * otherwise. Also returns false if resource is passed null.
     */
    public boolean useResource(final Resource resource) {
        return findResource(resource) != null;
    }

    /**
     * Returns a resource usage entry for this activity referencing given
     * resource.
     *
     * @param resource
     * @return Found ResourceUsage instance for this activity and given
     * resource. Null if the resource doesn't uses that resource or if
     * resource is passed null.
     */
    public ResourceUsage findResource(final Resource resource) {
        if ((resource == null) || (resourceUsages == null)) {
            return null;
        }
        return resourceUsages.stream().filter(u -> u.getResource().equals(resource)).findFirst().orElse(null);
    }

    /**
     * @return True if an activity type is defined for this activity. False
     * otherwise.
     */
    public boolean hasType() {
        return getActivityType() != null;
    }

    //
    // Getters and setters
    //
    public String getCode() {
        return code;
    }

    public String getDescription() {
        return description;
    }

    public EntityReference getReferencedEntity() {
        return referencedEntity;
    }

    public void setCode(final String code) {
        this.code = code;
    }

    public void setDescription(final String description) {
        this.description = description;
    }

    public void setReferencedEntity(final EntityReference referencedEntity) {
        this.referencedEntity = referencedEntity;
    }

    public ActivityType getActivityType() {
        return activityType;
    }

    public Set<ResourceUsage> getResourceUsages() {
        if (resourceUsages == null) {
            return Collections.emptySet();
        }
        return Collections.unmodifiableSet(resourceUsages);
    }

    @JsonIgnore
    public Set<Resource> getResources() {
        if (resourceUsages == null) {
            return Collections.emptySet();
        }
        resourceUsages.size();
        // All used resources
        return resourceUsages.stream().map(ResourceUsage::getResource).collect(Collectors.toSet());
    }

    @JsonIgnore
    public Set<BusinessCalendar> getBusinessCalendars() {
        if (resourceUsages == null) {
            return Collections.emptySet();
        }
        resourceUsages.size();
        // BusinessCalendars for all used resources (excluding resources without
        // a calendar)
        return resourceUsages.stream().filter(u -> u.getResource().getCalendar() != null).map(u -> u.getResource().getCalendar())
            .collect(Collectors.toSet());
    }

    public void setActivityType(final ActivityType activityType) {
        this.activityType = activityType;
    }

    public void setResourceUsages(final Set<ResourceUsage> resourceUsages) {
        this.resourceUsages = resourceUsages;
    }

    public Set<TimeSlot> getTimeSlots() {
        return timeSlots != null ? timeSlots : Collections.emptySet();
    }

    public Set<EntityReference> getRelatedEntities() {
        return Collections.unmodifiableSet(relatedEntities);
    }

    public void addRelatedEntity(final EntityReference relatedEntity) {
        relatedEntities.add(relatedEntity);
    }

    public void removeRelatedEntity(final EntityReference relatedEntity) {
        relatedEntities.remove(relatedEntity);
    }

    public void addEffort(final EffortSize effortSize) {
        effortSize.setActivity(this);
        efforts.add(effortSize);
    }

    public void addAllEfforts(final Collection<EffortSize> efforts) {
        if (efforts != null) {
            efforts.forEach(effort -> {
                addEffort(effort);
            });
        }
    }

    public Long getTotalEffortSec() {
        return totalEffortSec;
    }

    public void setTotalEffortSec(final Long totalEffortSec) {
        this.totalEffortSec = totalEffortSec;
    }

    public Long getAllocatedSec() {
        return allocatedSec;
    }

    public void setAllocatedSec(final Long allocatedSec) {
        this.allocatedSec = allocatedSec;
    }

    public Long getElapsedSec() {
        return elapsedSec;
    }

    public void setElapsed(final Long elapsedSec) {
        this.elapsedSec = elapsedSec;
    }

    public ActivityAllocationStatus getResourcesAllocationStatus() {
        return resourcesAllocationStatus;
    }

    public void setResourcesAllocationStatus(final ActivityAllocationStatus resourcesAllocationStatus) {
        this.resourcesAllocationStatus = resourcesAllocationStatus;
    }

    public ActivityAllocationStatus getTimeSlotAllocationStatus() {
        return timeSlotAllocationStatus;
    }

    public void setTimeSlotAllocationStatus(final ActivityAllocationStatus timeSlotAllocationStatus) {
        this.timeSlotAllocationStatus = timeSlotAllocationStatus;
    }

    public void setElapsedSec(final Long elapsedSec) {
        this.elapsedSec = elapsedSec;
    }

    public ActivityStatus getStatus() {
        return status;
    }

    //
    // Set status
    //
    public void setStatus(final ActivityStatus status) {
        this.status = status;
    }

    public Calendar getElapsedFrom() {
        return elapsedFrom;
    }

    public void setElapsedFrom(final Calendar elapsedFrom) {
        this.elapsedFrom = elapsedFrom;
    }

    public Calendar getElapsedTo() {
        return elapsedTo;
    }

    public void setElapsedTo(final Calendar elapsedTo) {
        this.elapsedTo = elapsedTo;
    }

    public Calendar getClosureDate() {
        return closureDate;
    }

    public void setClosureDate(final Calendar closureDate) {
        this.closureDate = closureDate;
    }

    public Calendar getCancellationDate() {
        return cancellationDate;
    }

    public void setCancellationDate(final Calendar cancellationDate) {
        this.cancellationDate = cancellationDate;
    }

    public Calendar getCompletionDate() {
        return completionDate;
    }

    public void setCompletionDate(final Calendar completionDate) {
        this.completionDate = completionDate;
    }

    public Calendar getUpperBoundTo() {
        return upperBoundTo;
    }

    public void setUpperBoundTo(final Calendar upperBoundTo) {
        this.upperBoundTo = upperBoundTo;
    }

    public Boolean getUpperBoundNow() {
        return upperBoundNow;
    }

    public void setUpperBoundNow(final Boolean upperBoundNow) {
        this.upperBoundNow = upperBoundNow;
    }

    public Calendar getLowerBoundFrom() {
        return lowerBoundFrom;
    }

    public void setLowerBoundFrom(final Calendar lowerboundFrom) {
        lowerBoundFrom = lowerboundFrom;
    }

    public Boolean isLowerBoundNow() {
        return lowerBoundNow;
    }

    public void setLowerBoundNow(final Boolean lowerboundNow) {
        lowerBoundNow = lowerboundNow;
    }

    public Calendar getLastOperationDate() {
        return lastOperationDate;
    }

    public void setLastOperationDate(final Calendar lastOperationDate) {
        this.lastOperationDate = lastOperationDate;
    }

    public void setScreenLabel(final String screenLabel) {
        this.screenLabel = screenLabel;
    }

    public Set<EffortSize> getEfforts() {
        return efforts;
    }

    public String getScreenLabel() {
        return screenLabel;
    }

    public boolean isToReschedule() {
        return toReschedule;
    }

    public void setToReschedule(final boolean toReschedule) {
        this.toReschedule = toReschedule;
    }

}


I want to put the information in 

package eu.reply.ds.boundary.rest;

import java.util.Calendar;

import eu.reply.sce.backend.core.entity.EntityReference;
import eu.reply.supplychain.scheduler.entities.activity.ActivityType;
import eu.reply.supplychain.scheduler.enumerations.ActivityAllocationStatus;
import eu.reply.supplychain.scheduler.enumerations.ActivityStatus;

public class ActivityDTO {

        private String code;
        private String description;

        private ActivityType activityType;

        private EntityReference referencedEntity;

        private Long totalEffortSec = 0L;
        private Long allocatedSec = 0L;
        private Long elapsedSec = 0L;

        private ActivityAllocationStatus resourcesAllocationStatus;
        private ActivityAllocationStatus timeSlotAllocationStatus;

        private ActivityStatus status;

        private Calendar elapsedFrom;
        private Calendar elapsedTo;

        private Calendar closureDate;
        private Calendar cancellationDate;
        private Calendar completionDate;

        private Calendar upperBoundTo;
        private Boolean upperBoundNow = false;
        private Calendar lowerBoundFrom;
        private Boolean lowerBoundNow = false;

        private Calendar maxTimeToModify;

        private Calendar lastOperationDate;

        private String screenLabel;

        private boolean toReschedule = false;

        //
        // Constructors
        //
        public ActivityDTO() {
            super();
            resourcesAllocationStatus = ActivityAllocationStatus.PENDING;
            timeSlotAllocationStatus = ActivityAllocationStatus.PENDING;
            status = ActivityStatus.TO_PLAN;
        }

        public Calendar getMaxTimeToModify() {
            return maxTimeToModify;
        }

        public void setMaxTimeToModify(final Calendar maxTimeToModify) {
            this.maxTimeToModify = maxTimeToModify;
        }

}

also i want to add a localdatetime for each calendar (in the dto) compiled with the correct date when converting the information from the activity