package com.capgemini.framework.teamcenter.object.pom.status;

import com.capgemini.framework.teamcenter.authent.TcSession;
import com.teamcenter.soa.client.model.ModelObject;
import com.teamcenter.soa.client.model.strong.Effectivity;
import com.teamcenter.soa.client.model.strong.ItemRevision;
import com.teamcenter.soa.client.model.strong.ReleaseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.List;

import static com.capgemini.framework.teamcenter.TeamcenterObjectProperties.NAME;

/**
 * Represents a Release TcReleaseStatus.<br>
 * A Release TcReleaseStatus may represent either of the following :
 * <ul>
 * <li>An effectivity<br>
 * In this case the ReleaseStatus is named "TcEffectivity" and owns a {@link List} of {@link TcEffectivity Effectivities}</li>
 * <li>A Maturity (J3A, J3B etc.)</li>
 * <li>Any other TcReleaseStatus ('Interface Todo', etc.)</li>
 * </ul>
 */
public class TcReleaseStatus {
  private static final Logger LOGGER = LoggerFactory.getLogger(TcReleaseStatus.class);

  private final ReleaseStatus releaseStatus;
  private TcSession session;

  public TcReleaseStatus(final TcSession session, final ReleaseStatus status) {
    this.session = session;
    this.releaseStatus = status;
  }

  /**
   * Filters a {@code List} of {@link TcReleaseStatus} according to a list of eligible statuses.
   *
   * @param releaseStatusList List<{@link TcReleaseStatus}> - all the releaseStatus of an object
   * @param eligible          ({@link StatusEnum}) - Enumeration of eligible statuses
   * @return the list of the appropriate releaseStatus
   */
  public static List<TcReleaseStatus> filter(final List<TcReleaseStatus> releaseStatusList, final StatusEnum... eligible) {
    List<TcReleaseStatus> listOfChosenRS = new ArrayList<>();
    for (TcReleaseStatus status : releaseStatusList) {
      if (status != null) {
        for (StatusEnum wantedStatus : eligible) {
          if (wantedStatus == status.getStatusEnum()) {
            listOfChosenRS.add(status);
          }
        }
      }
    }
    return listOfChosenRS;
  }

  /**
   * Obtains the list of {@link ReleaseStatus} and filter them according to a list of eligible statuses.
   *
   * @param itemRevision   {@link ItemRevision}
   * @param eligibleStatus Enumeration of wanted releaseStatus ({@link StatusEnum}).
   * @return List<ReleaseStatus>
   */
  public static List<ReleaseStatus> getChosenStatus(final TcSession session, final ItemRevision itemRevision, final StatusEnum... eligibleStatus) {
    List<ReleaseStatus> listOfChosenRS = new ArrayList<>();
    session.loadProperties(itemRevision, "release_status_list");
    ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
    session.loadProperties(releaseStatuses);
    for (ReleaseStatus status : releaseStatuses) {
      if (status != null) {
        for (StatusEnum wantedStatus : eligibleStatus) {
          if (wantedStatus.getIdentifier().equals(status.get_name())) {
            listOfChosenRS.add(status);
          }
        }
      }
    }
    return listOfChosenRS;
  }

  /**
   * Get the list of {@link ReleaseStatus} and filter the {@link ReleaseStatus} in function of the forbidden releaseStatus.
   *
   * @param itemRevision    {@link ItemRevision}
   * @param forbiddenStatus Enumeration of forbidden releaseStatus({@link StatusEnum})
   * @return List<ReleaseStatus>
   */
  public static List<ReleaseStatus> getAllStatusExceptSome(final TcSession session, final ItemRevision itemRevision, final StatusEnum... forbiddenStatus) {
    List<ReleaseStatus> listOfChosenRS = new ArrayList<>();
    session.loadProperties(itemRevision, "release_status_list");
    ReleaseStatus[] releaseStatuses = itemRevision.get_release_status_list();
    session.loadProperties(releaseStatuses);
    for (ReleaseStatus status : releaseStatuses) {
      if (status != null) {
        boolean isSelected = true;
        for (StatusEnum wantedStatus : forbiddenStatus) {
          if (wantedStatus.getIdentifier().equals(status.get_name())) {
            isSelected = false;
          }
        }
        if (isSelected) {
          listOfChosenRS.add(status);
        }
      }
    }
    return listOfChosenRS;
  }

  public StatusEnum getStatusEnum() {
    session.loadProperties(releaseStatus, "name");
    return StatusEnum.getStatusEnum(releaseStatus.get_name());
  }

  public Calendar dateReleased() {
    session.loadProperties(releaseStatus, "date_released");
    return releaseStatus.get_date_released();
  }

  public String getDisplayableName() {
    return releaseStatus.getPropertyObject(NAME).getDisplayableValue();
  }

  public TcEffectivity getEffectivity() {
    if (isEffectivity()) {
      session.loadProperties(releaseStatus, "effectivities");
      ModelObject[] effectivities = releaseStatus.get_effectivities();
      session.loadProperties(effectivities);
      if (effectivities == null || effectivities.length == 0) {
        LOGGER.debug("ReleaseStatus named '{}' does not contain Effectivities", getDisplayableName());
      }
      else {
        ModelObject objectEffectivity = effectivities[0]; // There should only be one effectivity, we ignore the rest of the list's content
        // Cast of the ModelObject to the TcEffectivity of Teamcenter, do not confuse with the TcEffectivity of this application
        return new TcEffectivity(session, (Effectivity) objectEffectivity);
      }
    }
    return null;
  }

  public List<TcEffectivity> getEffectivities() {
    List<TcEffectivity> tcEffectivities = new ArrayList<>();
    if (isEffectivity()) {
      session.loadProperties(releaseStatus, "effectivities");
      ModelObject[] effectivities = releaseStatus.get_effectivities();
      session.loadProperties(effectivities);
      for (ModelObject effectivity : effectivities) {
        tcEffectivities.add(new TcEffectivity(session, (Effectivity) effectivity));
      }
    }
    return tcEffectivities;
  }

  public boolean isEffectivity() {
    StatusEnum statusName = getStatusEnum();
    return StatusEnum.ONLY_EFFECTIVITIES.isIncluded(statusName) || StatusEnum.ONLY_DMU.isIncluded(statusName);
  }

  /**
   * Returns <code>true</code> if this ReleaseStatus controls a Change's maturity.
   *
   * @return true if the releaseStatus represents a Maturity
   */
  public boolean isChangeMaturity() {
    return StatusEnum.CHANGE_MATURITY.isIncluded(getStatusEnum());
  }

  public boolean isOneOf(StatusEnum... statuses) {
    StatusEnum statusEnum = getStatusEnum();
    for (StatusEnum status : statuses) {
      if (statusEnum.equals(status)) {
        return true;
      }
    }
    return false;
}

  @Override
  public String toString() {
    TcEffectivity effectivity = getEffectivity();
    if (effectivity == null) {
      return "TcReleaseStatus with NAME= " + getStatusEnum();
    }
    else {
      return "TcReleaseStatus with NAME= " + getStatusEnum() + " RANGE= " + effectivity.unitRange();
    }
  }

  public enum Order implements Comparator<TcReleaseStatus> {
    BY_RELEASE_DATE() {
      @Override
      public int compare(final TcReleaseStatus releaseStatus1, final TcReleaseStatus releaseStatus2) {
        return releaseStatus1.dateReleased().compareTo(releaseStatus2.dateReleased());
      }
    };

    @Override
    public abstract int compare(final TcReleaseStatus releaseStatus1, final TcReleaseStatus releaseStatus2);
  }
}
