package com.capgemini.reports.teamcenter;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.capgemini.framework.teamcenter.object.SnecmaObjectProperties;
import com.capgemini.framework.teamcenter.object.pom.status.StatusEnum;
import com.capgemini.framework.teamcenter.object.pom.status.StatusEnum.AbstractStatusFilter;
import com.capgemini.framework.teamcenter.object.pom.status.TcReleaseStatus;
import com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes;
import com.capgemini.reports.ItemRevisionComparator;
import com.capgemini.reports.exceptions.NotFoundException;
import com.capgemini.reports.releasestatus.RevisionStatusAndDate;
import com.google.common.base.Optional;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Effectivity;
import com.teamcenter.soa.client.model.strong.Item;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import com.teamcenter.soa.client.model.strong.ReleaseStatus;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.capgemini.framework.teamcenter.object.SnecmaObjectProperties.PV_ALTERNATIVE;
import static com.capgemini.framework.teamcenter.object.types.SnecmaObjectTypes.EFFECTIVITY;

/**
 * This class is able to obtain specific data from some objects in Teamcenter. <br>
 * To be used when the information is there, it's just buried in layers of TC objects. This helper class will only help retrieve the data reliably.<br>
 * Typical usage is to find effectivity status of an ItemRevision, obtain the latest maturity status etc.
 */
@Slf4j
public class ObjectInfoHelper {

    private static final String WITH_UNIT = " with Unit ";

    private static final Pattern EFFECTIVITY_RANGE_UP = Pattern.compile("([\\d]{2,})-([Uu][Pp]|[Ss][Oo])"); // Matches an open-ended range like '50-UP' (note: can also be '50-SO')
    private static final Pattern EFFECTIVITY_RANGE_NUMBER = Pattern.compile("([\\d]{2,})-[\\d]{2,}"); // Matches a number range like '95-105'
    private static final Pattern EFFECTIVITY_RANGE_ALONE = Pattern.compile("([\\d]{2,})"); // Matches a single number (2+ digits)


    /**
     * Returns a {@link List} of {@link ModelObject}s containing Part (Standard or Part Aero) which are inside
     * the pseudo folder of a provided {@link ItemRevision} (Alternate Revision or DET Revision).<br>
     */
    public static List<Item> getPartsInPseudoFolderOfAltOrDetRevision(final TcSession session, final ItemRevision itemRevision) {
        String objectType = itemRevision.getTypeObject().getName();
        if (SnecmaObjectTypes.isAltOrDet(objectType)) {
            List<Item> listItem = new ArrayList<>();
            session.loadProperties(itemRevision, PV_ALTERNATIVE);
            ModelObject[] alternatives = itemRevision.getPropertyObject(PV_ALTERNATIVE).getModelObjectArrayValue();
            session.loadProperties(alternatives);
            for (ModelObject modelObj : alternatives) {
                listItem.add((Item) modelObj);
            }
            return listItem;
        } else {
            return new ArrayList<>();
        }
    }

    /**
     * Returns a {@link List} of {@link ModelObject}s containing whatever are inside the pseudo folder.<br>
     */
    public static List<ModelObject> getModelObjectsInPseudoFolderUnderItemRevision(final TcSession session, final ItemRevision itemRevision, final String propertyPseudoFolder) {
        session.loadProperties(itemRevision, propertyPseudoFolder);
        ModelObject[] folderObjects = itemRevision.getPropertyObject(propertyPseudoFolder).getModelObjectArrayValue();
        session.loadProperties(folderObjects);
        return Arrays.asList(folderObjects);
    }

    /**
     * Check if Generic or GenericAssemb has an unit less than or equal unitTarget.
     */
    public static boolean getClosestGenerics(final TcSession session, final String genericID, final int targetUnit, final List<ItemRevision> genericsRevision)
            throws NotFoundException {
        boolean isUnitClosest = false;

        if (genericsRevision.isEmpty()) {
            throw new NotFoundException("No unitRevision exists for Generic||Generic Assemb " + genericID + WITH_UNIT + targetUnit);
        }
        for (ItemRevision genericRevision : genericsRevision) {
            session.loadProperties(genericRevision, "release_status_list");
            ReleaseStatus[] releaseStatuses = genericRevision.get_release_status_list();
            session.loadProperties(releaseStatuses);
            for (ReleaseStatus status : releaseStatuses) {
                isUnitClosest = getInferiorUnitFromEffectivities(session, status, targetUnit);
            }
        }

        return isUnitClosest;

    }

    /**
     * Returns the first TcEffectivity whose name is "effectivity" and whose range starts before the provided unit.
     */
    private static boolean getInferiorUnitFromEffectivities(final TcSession session, final ReleaseStatus releaseStatus, final int targetUnit) {
        session.loadProperties(releaseStatus, "effectivities");
        ModelObject[] effectivities = releaseStatus.get_effectivities();
        session.loadProperties(effectivities);
        for (ModelObject objectEffectivity : effectivities) {
            String statusName = releaseStatus.get_name();
            if (!EFFECTIVITY.equals(statusName)) {
                continue;
            }
            Effectivity effectivity = (Effectivity) objectEffectivity;
            String effectivityRange = effectivity.get_range_text();
            log.debug("Check is current TcEffectivity range : " + effectivityRange + " is <= " + targetUnit);
            if (isInRange(targetUnit, effectivityRange)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isInRange(final int targetUnit, final String effectivityRange) {
        boolean found = false;
        if (isEffectivityRangeUP(effectivityRange)) {
            if (targetUnit <= getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_UP)) {
                found = true;
            }
        } else if (isEffectivityRangeNUMBER(effectivityRange)) {
            if (targetUnit <= getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_NUMBER)) {
                found = true;
            }
        } else if (isEffectivityRangeALONE(effectivityRange) && targetUnit <= getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_ALONE)) {
            found = true;
        }
        return found;
    }

    private static boolean equalsRangeStart(final int targetUnit, final String effectivityRange) {
        boolean found = false;
        if (isEffectivityRangeUP(effectivityRange)) {
            if (targetUnit == getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_UP)) {
                found = true;
            }
        } else if (isEffectivityRangeNUMBER(effectivityRange)) {
            if (targetUnit == getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_NUMBER)) {
                found = true;
            }
        } else if (isEffectivityRangeALONE(effectivityRange) && targetUnit == getFirstRangeValue(effectivityRange, EFFECTIVITY_RANGE_ALONE)) {
            found = true;
        }
        return found;
    }

    /**
     * Returns the first match in the unit range, using the provided pattern.
     *
     * @param range   the Unit range (can be a single unit, an pen range, a closed range etc.)
     * @param pattern The patter nto decipher the provided range
     * @return the unit number which is the start of the provided range
     */
    private static int getFirstRangeValue(final String range, final Pattern pattern) {
        String data = "";
        Matcher matcher = pattern.matcher(range);
        if (matcher.matches() && matcher.groupCount() >= 1) {
            data = matcher.group(1);
        }
        return Integer.parseInt(data);
    }

    private static boolean isEffectivityRangeUP(final String input) {
        Matcher matcher = EFFECTIVITY_RANGE_UP.matcher(input);
        return matcher.matches();
    }

    private static boolean isEffectivityRangeNUMBER(final String input) {
        Matcher matcher = EFFECTIVITY_RANGE_NUMBER.matcher(input);
        return matcher.matches();
    }

    private static boolean isEffectivityRangeALONE(final String input) {
        Matcher matcher = EFFECTIVITY_RANGE_ALONE.matcher(input);
        return matcher.matches();
    }

    /**
     * Returns the {@link ItemRevision} with the most recent modification date, of an {@link Item}.<br>
     * This method requires the last_mod_date property on {@link ItemRevision}.
     *
     * @param itemsRevisions the List of candidate {@link ItemRevision}s
     * @param userDate       the limit user date, only modifications before that date will be examined
     * @return An Optional containing the most up-to-date ItemRevision, TcReleaseStatus and mod date. The Optional may be empty if not Revisions were provided!
     */
    public static RevisionStatusAndDate getRevisionAndStatusWithMostRecentModificationDate(final List<ItemRevision> itemsRevisions,
                                                                                           final Calendar userDate) {
        RevisionStatusAndDate bestYet = null;
        for (ItemRevision curItemRevision : itemsRevisions) {
            Calendar modDateCurrentItem = curItemRevision.get_last_mod_date(); // Requires the last_mod_date property
            if (bestYet == null) { // First pass
                bestYet = new RevisionStatusAndDate(modDateCurrentItem, null, curItemRevision);
            } else {
                if (modDateCurrentItem.after(bestYet.getReleaseDate())
                        && (userDate == null || modDateCurrentItem.before(userDate) || sameDay(modDateCurrentItem, userDate))) {
                    bestYet = new RevisionStatusAndDate(modDateCurrentItem, null, curItemRevision);
                }
            }
        }
        return bestYet;
    }

    /**
     * Returns the {@link ItemRevision} with the most recent modification date, of an {@link Item}.<br>
     * This method requires the last_mod_date property on {@link ItemRevision}.
     *
     * @param itemsRevision the List of candidate {@link ItemRevision}s
     * @return An Optional containing the most up-to-date ItemRevision, TcReleaseStatus and mod date. The Optional may be empty if not Revisions were provided!
     */
    public static RevisionStatusAndDate getLastModDate(final ItemRevision itemsRevision) {
        RevisionStatusAndDate bestYet = null;

            Calendar modDateCurrentItem = itemsRevision.get_last_mod_date(); // Requires the last_mod_date property
            bestYet = new RevisionStatusAndDate(modDateCurrentItem, null, itemsRevision);

        return bestYet;
    }

    private static boolean sameDay(final Calendar cal1, final Calendar cal2) {
        return cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR) && cal1.get(Calendar.DAY_OF_YEAR) == cal2.get(Calendar.DAY_OF_YEAR);
    }

    /**
     * Returns the {@link ItemRevision} whose {@link ReleaseStatus} has the most recent release date, amongst the provided ItemRevisions.<br>
     * This method requires the date_released property on {@link ItemRevision}.<br>
     * This method requires the date_released property on {@link ReleaseStatus}.<br>
     * This method requires the release_status_list property on {@link ItemRevision}, with properties.
     *
     * @param session
     * @param itemsRevisions the List of candidate {@link ItemRevision}s
     * @param userDate       the limit user date, only items released before that date will be examined. If <code>null</code>, all dates are accepted.
     * @param statusFilter   A filter on the statuses to examine. Statuses rejected by the filter will not be examined at all.
     * @return An {@link Optional} containing the most up-to-date ItemRevision, TcReleaseStatus and release date (or empty, if no status matching the date could be found)
     */
    public static Optional<RevisionStatusAndDate> getRevisionAndStatusWithMostRecentReleaseDate(final TcSession session, final List<ItemRevision> itemsRevisions,
                                                                                                final Calendar userDate, final AbstractStatusFilter statusFilter) {
        Optional<RevisionStatusAndDate> bestYet = Optional.absent();
        for (ModelObject curItemRevision : itemsRevisions) {
            List<ReleaseStatus> listReleaseStatus = statusFilter.filterStatusOf(session, (ItemRevision) curItemRevision);
            for (ReleaseStatus status : listReleaseStatus) {
                if (status == null) {
                    continue;
                }
                Calendar date = status.get_date_released();

                if ((userDate == null || date.compareTo(userDate) < 0)
                        && (!bestYet.isPresent() || bestYet.get().getReleaseDate().compareTo(date) < 0)) {
                    bestYet = Optional.of(new RevisionStatusAndDate(date, status, (ItemRevision) curItemRevision));
                }
            }
        }
        return bestYet;
    }

    /**
     * Returns the {@link ItemRevision} whose {@link ReleaseStatus} has the most recent release date, amongst the provided ItemRevisions.<br>
     * This method requires the date_released property on {@link ItemRevision}.<br>
     * This method requires the date_released property on {@link ReleaseStatus}.<br>
     * This method requires the release_status_list property on {@link ItemRevision}, with properties.
     *
     * @param session
     * @param itemsRevisions the List of candidate {@link ItemRevision}s
     * @param userDate       the limit user date, only items released before that date will be examined. If <code>null</code>, all dates are accepted.
     * @param allowedStatus  if none are given, all status will be used (except TcEffectivity which is not really a maturity status). If not empty, only the provided statuses will be returned.
     * @return An {@link Optional} containing the most up-to-date ItemRevision, TcReleaseStatus and release date (or empty, if no status matching the date could be found)
     */
    public static Optional<RevisionStatusAndDate> getRevisionAndStatusWithMostRecentReleaseDate(final TcSession session, final List<ItemRevision> itemsRevisions,
                                                                                                final Calendar userDate, final StatusEnum... allowedStatus) {
        AbstractStatusFilter filter =
                (allowedStatus == null || allowedStatus.length == 0) ? StatusEnum.forbid(StatusEnum.EFFECTIVITY) : StatusEnum.allow(allowedStatus); // By default, forbid TcEffectivity.
        return getRevisionAndStatusWithMostRecentReleaseDate(session, itemsRevisions, userDate, filter);
    }

    /**
     * Returns the {@link ItemRevision} whose {@link ReleaseStatus} has the most recent release date, amongst the provided ItemRevisions.<br>
     * This method requires the date_released property on {@link ItemRevision}.<br>
     * This method requires the date_released property on {@link ReleaseStatus}.<br>
     * This method requires the release_status_list property on {@link ItemRevision}.
     *
     * @param session
     * @param itemsRevision the List of candidate {@link ItemRevision}s
     * @param userDate      the limit user date, only Statuses released before that date will be examined. May be null if you want to ignore this
     * @param wantedStatus  Enumeration of wanted status ({@link StatusEnum}).
     * @return an {@link Optional} which may contain the ItemRevision with the latest Release Date that still is before the provided deadline, or nothing if no revision meet the constraints
     */
    public static Optional<ItemRevision> getRevisionFromStatusWithMostRecentReleaseDate(final TcSession session, final List<ItemRevision> itemsRevision,
                                                                                        final Calendar userDate, final StatusEnum... wantedStatus) {
        Optional<RevisionStatusAndDate> latest = getRevisionAndStatusWithMostRecentReleaseDate(session, itemsRevision, userDate, wantedStatus);
        if (latest.isPresent()) {
            return Optional.of(latest.get().getItemRevision());
        } else {
            return Optional.absent();
        }
    }
    /**
     * Returns the {@link ItemRevision} whose {@link ReleaseStatus} has the most recent release date, amongst the provided ItemRevisions not cancelled.<br>
     * This method requires the date_released property on {@link ItemRevision}.<br>
     * This method requires the date_released property on {@link ReleaseStatus}.<br>
     * This method requires the release_status_list property on {@link ItemRevision}.
     *
     * @param session
     * @param itemsRevision the List of candidate {@link ItemRevision}s
     * @param userDate      the limit user date, only Statuses released before that date will be examined. May be null if you want to ignore this
     * @return an {@link Optional} which may contain the ItemRevision with the latest Release Date that still is before the provided deadline, or nothing if no revision meet the constraints
     */
    public static Optional<ItemRevision> getRevisionFromStatusWithMostRecentReleaseDateNotCancelled(final TcSession session, final List<ItemRevision> itemsRevision,
                                                                                                    final Calendar userDate) {
        AbstractStatusFilter filter = StatusEnum.forbid(StatusEnum.EFFECTIVITY,StatusEnum.OLD_EFFECTIVITY);
        Optional<RevisionStatusAndDate> bestYet = Optional.absent();
        Optional<RevisionStatusAndDate> bestYetNoDefApproved= Optional.absent();
        boolean flagDefApproved=false;
        for (ItemRevision curItemRevision : itemsRevision) {
            List<ReleaseStatus> listReleaseStatus = filter.filterStatusOf(session, (ItemRevision) curItemRevision);
            boolean flagCancel = false;
            boolean flagDefApprovedCurrent = false;
            Optional<RevisionStatusAndDate> currentBestYet = Optional.absent();
            for (ReleaseStatus status : listReleaseStatus) {

                if (status == null) {
                    continue;
                }
                if (status.get_name().equals(StatusEnum.CANCELLED.getIdentifier())) {
                    flagCancel = true;
                    //on a trouvé un flag Cancel on à plus besoin de parcourir le rest des status
                    break;
                }
                if (status.get_name().equals(StatusEnum.DEF_APPROVED.getIdentifier())){
                    flagDefApprovedCurrent =true;
                }
                Calendar date = status.get_date_released();
                if ((userDate == null || date.compareTo(userDate) < 0)
                        && (!bestYet.isPresent() || bestYet.get().getReleaseDate().compareTo(date) < 0)) {
                    currentBestYet = Optional.of(new RevisionStatusAndDate(date, status, (ItemRevision) curItemRevision));
                }

            }
            if (!flagCancel) {
                // Cas ou pas de status Def Approved
                if (listReleaseStatus.size() == 0) {
                     if ((!bestYetNoDefApproved.isPresent())
                            || bestYetNoDefApproved.get().getReleaseDate().compareTo((curItemRevision).get_last_mod_date()) <= 0) {
                            bestYetNoDefApproved = Optional.of(getLastModDate(curItemRevision));
                    }
                } else {
                    //Cas ou on à trouvé un statut def approved
                    if (currentBestYet.isPresent()) {
                        bestYet = currentBestYet;
                    }
                    flagDefApproved = flagDefApproved || flagDefApprovedCurrent;
                }
            }
        }
        RevisionStatusAndDate lastRevStatDate = null;
        if (flagDefApproved == true) {
            //return Optional.of(latest.get().getItemRevision());
            lastRevStatDate =bestYet.get();
        } else {

            if(bestYetNoDefApproved.isPresent()){
                lastRevStatDate = bestYetNoDefApproved.get();
            }else{
                //all cancel
                return Optional.absent();

            }
        }
        return Optional.of(lastRevStatDate.getItemRevision());
    }
    /**
     * Returns the {@link ReleaseStatus} with the most recent release date, amongst the provided {@link ItemRevision}s.<br>
     * This method requires the date_released property on {@link ReleaseStatus}
     *
     * @param itemRevision    the entry ItemRevision
     * @param forbiddenStatus the enumeration of all the forbidden status
     * @return ReleaseStatus the appropriate ReleaseStatus with the most recent date released
     */
    public static ReleaseStatus getStatusWithMostRecentReleaseDate(final TcSession session, final ItemRevision itemRevision, final StatusEnum... forbiddenStatus) {
        // Retrieve all the ReleaseStatus
        List<ReleaseStatus> releaseStatusPartRev = new ArrayList<>();
        if (forbiddenStatus.length == 0) {
            session.loadProperties(itemRevision, "release_status_list");
            ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
            session.loadProperties(releaseStatuses);
            Collections.addAll(releaseStatusPartRev, releaseStatuses);
        } else {
            releaseStatusPartRev = TcReleaseStatus.getAllStatusExceptSome(session, itemRevision, forbiddenStatus);
        }

        // Select the most recent ReleaseStatus
        ReleaseStatus selectedRS = null;
        Calendar selectedDate = null;
        for (ReleaseStatus status : releaseStatusPartRev) {
            if (status != null) {
                Calendar date = status.get_date_released();
                if (selectedDate == null) { // First pass
                    selectedRS = status;
                    selectedDate = date;
                } else if (selectedDate.compareTo(date) < 0) {
                    selectedRS = status;
                    selectedDate = date;
                }
            }
        }
        return selectedRS;
    }


    /**
     * Get all the {@link ItemRevision}s whose effectivies are the highest (as long as they stay below the provided rangeRefStr parameter).
     *
     * @param revisionsList List<{@link ItemRevision}> the list of all the {@link ItemRevision}
     * @param rangeRefStr   String the limit range
     * @return the ItemRevisions, in a {@link List}
     */
    public static List<ItemRevision> getRevisionWithHighestRange(final TcSession session, final List<ItemRevision> revisionsList, final String rangeRefStr) {
        int rangeRef = Integer.parseInt(rangeRefStr);
        List<ItemRevision> results = new ArrayList<>(); // The list of all revisions with the highest range
        int resultRange = -1; // THe highest Range

        // Browse all revisions
        for (ItemRevision itemRev : revisionsList) {
            List<ReleaseStatus> effectivityList = TcReleaseStatus.getChosenStatus(session, itemRev, StatusEnum.EFFECTIVITY);
            if (effectivityList.isEmpty()) {
                continue;
            }
            int selected = selectHighestRange(session, effectivityList, rangeRef); // Is negative if none are found
            if (selected > 0) {
                if (selected == resultRange) {
                    results.add(itemRev); // Same range found, add this ItemRev to the list of items found with same range
                } else if (selected > resultRange) { // Closer range found, begin a new list with this ItemRev
                    results.clear();
                    results.add(itemRev);
                    resultRange = selected;
                }
            }
        }
        return results;
    }

    /**
     * Keep the {@link ItemRevision} in the entry list which have the same end Item Id value as the reference value.<br>
     * <b>Note on the policy:</b> The property {@code end_item} must be set on the TcEffectivity, and {@code withProperties} set to <code>true</code>
     *
     * @param revisionsList List<{@link ItemRevision}> - the list of revisions
     * @param endItemId     {@link String} - the wanted value of the end Item Id
     * @return the chosen list
     */
    public static List<ItemRevision> getRevisionWithSameEndItem(final TcSession session, final List<ItemRevision> revisionsList, final String endItemId) {
        List<ItemRevision> results = new ArrayList<>();
        for (ItemRevision itemRev : revisionsList) {
            boolean isSuccess = false;
            for (ReleaseStatus releaseStatus : TcReleaseStatus.getChosenStatus(session, itemRev, StatusEnum.EFFECTIVITY)) {
                session.loadProperties(releaseStatus, "effectivities");
                ModelObject[] effectivities = releaseStatus.get_effectivities();
                session.loadProperties(effectivities);
                for (ModelObject effectivityObject : effectivities) {
                    Effectivity effectivity = (Effectivity) effectivityObject;
                    session.loadProperties(effectivity, "end_item");
                    Item endItem = effectivity.get_end_item();
                    session.loadProperties(endItem);
                    if (endItemId.equals(SnecmaObjectProperties.getItemIdOrNull(endItem))) {
                        results.add(itemRev);
                        isSuccess = true;
                        break;
                    }
                }
                if (isSuccess) {
                    break;
                }
            }
        }
        return results;
    }

    /**
     * Selects the TcEffectivity with the highest range, returns the range. <br>
     * If no Effectivities are eligible (none exist or those that exist are below the provided rangeRef) returns -1.
     */
    private static int selectHighestRange(final TcSession session, final List<ReleaseStatus> effectivityList, final int rangeRef) {
        int selected = -1;
        // This revision seems to be a legitimate candidate, let's look at its effectivities
        for (ReleaseStatus releaseStatus : effectivityList) {
            if (releaseStatus == null) {
                continue;
            }
            // TcEffectivity encountered
            session.loadProperties(releaseStatus, "effectivities");
            ModelObject[] effectivities = releaseStatus.get_effectivities();
            session.loadProperties(effectivities);
            for (ModelObject effectivityObject : effectivities) {
                Effectivity effectivity = (Effectivity) effectivityObject;
                String rangeItemRev = effectivity.get_range_text();
                String[] splitList = rangeItemRev.split("-");
                int candidate = Integer.parseInt(splitList[0]);
                if (candidate <= rangeRef && (selected < 0 || selected < candidate)) {
                    selected = candidate;
                }
            }
        }
        return selected;
    }

    /**
     * Returns a list of object referencer answering to the specification <br>
     * Has statuts Def Approved And is the the highest sequence between object have the same item_id and revision_id .<br>
     * You must give a list objects referencing a document, a list object revision authorized and a list status authorized
     */

    public static List<ItemRevision> filteringObjectsReferencer(final TcSession session, final List<ItemRevision> objectsReferencer, final List<String> authorizedTypes) {
        List<ItemRevision> results = new ArrayList<>();
        // getAuthorized types
        for (ItemRevision object : objectsReferencer) {
            if (authorizedTypes.contains(object.getTypeObject().getName())) {
                results.add(object);
            } else {
                log.info("Rejected unauthorized type={} item_id={}", object.getTypeObject().getName(), object.get_item_id());
            }
        }
        // --findAll parts has status Def Approved
        results = checkStatus(session, results);

        // --Group datas by their item_id,and revision_id and return highest sequence
        results = groupItemRevisionPerFamily(results);

        // --findAll active sequence and filtering them by their last released date to findAll highest revision
        // { part_xx/A.2(-> released_date:15-Jul-2015 11:11), part_xx/B.3 (-> released_date:14-Sept-2015 08:00) } = part_xx/B.3

        // findAll the active sequence
        results = getActiveSequence(results);
        // on se retrouve avec ca list {part_1/A.2,part_1/B.2} dans ce cas on garde la B
        // Chercher les parts en doublons part_1 et prendre la date_released de l'objet le plus recent


        return results;
    }

    /**
     * findAll current item rev if has status Def Approved
     * IN  : list = {partxA1 DefApproved,partxA2,partyB1}
     * OUT : list = {partxA1 DefApproved}
     */
    private static List<ItemRevision> checkStatus(final TcSession session, final List<ItemRevision> objectsReferencer) {
        List<ItemRevision> listPartsHasStatusDefApproved = new ArrayList<>();

        for (ItemRevision itemRevision : objectsReferencer) {
            session.loadProperties(itemRevision, "release_status_list");
            ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
            session.loadProperties(releaseStatuses);
            for (ReleaseStatus releaseStatus : releaseStatuses) {
                if (SnecmaObjectProperties.DEF_APPROVED.equals(releaseStatus.get_name())) {
                    listPartsHasStatusDefApproved.add(itemRevision);
                }
            }
        }
        return listPartsHasStatusDefApproved;
    }

    /**
     * Group datas by their item_id,and revision_id and return highest sequence
     * IN : list = {partx/A.1,partx/A.2,party/B.1,partx/B.1}
     * OUT  : List = {partx/A.2, partx/B.1,party/B.1 }
     */
    private static List<ItemRevision> groupItemRevisionPerFamily(final List<ItemRevision> objectsReferencerDefApproved) {
        List<ItemRevision> returnList = new ArrayList<>();

        List<ItemRevision> temp = new ArrayList<>();
        temp.addAll(objectsReferencerDefApproved);

        for (ItemRevision itemRevision : objectsReferencerDefApproved) {
            // create a sub collection
            List<ItemRevision> subCollection = new ArrayList<>();
            int pos = 0;
            // Compare current itemRevision_id with an element of the temp collection
            // And if ok put the current temp itemRevision in a sub collection
            while (pos < temp.size()) {
                if (itemRevision.get_item_id().equals(temp.get(pos).get_item_id())
                        && itemRevision.get_item_revision_id().equals(temp.get(pos).get_item_revision_id())) {
                    subCollection.add(temp.get(pos));
                    temp.remove(pos);
                    // re start iteration
                    pos = 0;
                    continue;
                }
                pos++;
            }
            // can be empty
            if (!subCollection.isEmpty()) {
                // ordering objects by item_id,rev_id,sequence_id (partx/A.1,partx/A.2,...)
                Collections.sort(subCollection, new ItemRevisionComparator());
                ItemRevision latestSequence = subCollection.get(subCollection.size() - 1);
                log.info("On group per family -> latest sequence is {}/{}.{}", latestSequence.get_item_id(), latestSequence.get_item_revision_id(), latestSequence.get_sequence_id());
                returnList.add(latestSequence);
            }
        }
        return returnList;
    }

    /* Return the latest active sequence check if the highest sequence*/
    private static List<ItemRevision> getActiveSequence(final List<ItemRevision> result) {
        List<ItemRevision> results = new ArrayList<>();
        for (ItemRevision curItemRevision : result) {
            if (curItemRevision.get_active_seq() != 0) {
                results.add(curItemRevision);
            } else {
                log.info("Rejected {} because is not the last sequence", curItemRevision.getUid());
            }
        }
        return getHighestSequenceByReleasedDate(results);
    }

    /*Compare two Item revision have the same item_id and different revision_id, take the last date released */
    private static List<ItemRevision> getHighestSequenceByReleasedDate(final List<ItemRevision> currentList) {
        Map<String, ItemRevision> map = new HashMap<>();
        for (ItemRevision curItemRevision : currentList) {
            if (map.containsKey(defineKey(curItemRevision))) {
                ItemRevision existingItemRevision = map.get(defineKey(curItemRevision));
                // findAll good object by date released
                ItemRevision candidate = getTheHighestItemRevisionByDate(curItemRevision, existingItemRevision);
                map.put(defineKey(candidate), candidate);
            } else {
                map.put(defineKey(curItemRevision), curItemRevision);
            }
        }

        return new ArrayList<>(map.values());
    }

    /* Check if current itemRevision have date released superior than existing item revision */
    private static ItemRevision getTheHighestItemRevisionByDate(final ItemRevision curItemRevision, final ItemRevision existingItemRevision) {
        Calendar dateReleasedOfCurItemRevision = curItemRevision.get_date_released();
        Calendar dateReleasedOfexistingItemRevision = existingItemRevision.get_date_released();
        if (dateReleasedOfCurItemRevision.after(dateReleasedOfexistingItemRevision)) {
            return curItemRevision;
        } else {
            return existingItemRevision;
        }
    }

    /* Helper build key for map*/
    private static String defineKey(final ItemRevision itemRevision) {
        return itemRevision.get_item_id();
    }

    /**
     * Returns true if the {@link ReleaseStatus}'s Effectivities contain one {@link Effectivity} named "effectivity", whose range matches the provided Unit number.
     *
     * @param releaseStatus the {@link ReleaseStatus} holding all the effectivities
     * @param targetUnit    the unit to match
     * @return <code>true</code> if an {@link Effectivity} is found with a unit value matching the targetUnit, <code>false</code> otherwise
     */
    private static boolean getExactUnitInEffectivities(final TcSession session, final ReleaseStatus releaseStatus, final int targetUnit) {
        session.loadProperties(releaseStatus, "effectivities");
        ModelObject[] effectivities = releaseStatus.get_effectivities();
        session.loadProperties(effectivities);
        for (ModelObject objectEffectivity : effectivities) {
            String statusName = releaseStatus.get_name();
            if (EFFECTIVITY.equals(statusName)) {
                Effectivity effectivity = (Effectivity) objectEffectivity;
                String effectivityRange = effectivity.get_range_text();
                log.debug("Check is current TcEffectivity range : {} is equal to {}", effectivityRange, targetUnit);
                if (equalsRangeStart(targetUnit, effectivityRange)) {
                    return true;
                }
            }
        }
        return false;
    }

    protected boolean getClosestProductUnit(final TcSession session, final String productID, final int targetUnit, final List<ItemRevision> unitsRevision) throws NotFoundException {
        if (unitsRevision.isEmpty()) {
            throw new NotFoundException("No unitRevision exists for Product " + productID + WITH_UNIT + targetUnit);
        }
        for (ItemRevision unitRevision : unitsRevision) {
            session.loadProperties(unitRevision, "release_status_list");
            ReleaseStatus[] releaseStatuses = unitRevision.get_release_status_list();
            session.loadProperties(releaseStatuses);
            for (ReleaseStatus status : releaseStatuses) {
                if (getExactUnitInEffectivities(session, status, targetUnit)) {
                    return true;
                }
            }
        }
        return false;
    }
}
