package org.msh.tb.cases.calculated;

import org.jboss.seam.Component;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.msh.tb.entities.enums.HIVResult;
import org.msh.tb.misc.EntityEvent;
import org.msh.tb.ng.entities.ExamHIV_Ng;
import org.msh.tb.ng.entities.TbCaseNG;
import org.msh.tb.ng.entities.enums.HIVPosition;

import javax.persistence.EntityManager;
import java.util.Date;
import java.util.List;

/**
 * Update the HIV result field automatically calculated by the system when
 * the case data or HIV test is changed
 * Created by rmemoria on 1/9/17.
 */
@Name("caseHIVService")
@AutoCreate
@Scope(ScopeType.APPLICATION)
public class HIVStatusCalcService {

    @Observer("entity.TbCaseNG")
    public void onTBCaseChange(EntityEvent evt) {
        TbCaseNG tbcase = (TbCaseNG) evt.getEntity();
        updateHIVResultCalculated(tbcase);
    }

    @Observer("entity.ExamHIV_Ng")
    public void onHIVChange(EntityEvent evt) {
        ExamHIV_Ng exam = (ExamHIV_Ng)evt.getEntity();
        TbCaseNG tbcase = (TbCaseNG)exam.getTbcase();
        updateHIVResultCalculated(tbcase);
    }

    private void updateHIVResultCalculated(TbCaseNG tbcase) {
        HIVPosition pos = tbcase.getHivPosition();

        HIVResult res = convertToResult(pos);

        // get the results from HIV informed in the case
        EntityManager em = (EntityManager) Component.getInstance("entityManager");
        List<Object[]> lst = em.createNativeQuery("select result, startedARTDate, startedCPTDate " +
                "from examhiv where case_id=:id")
                .setParameter("id", tbcase.getId())
                .getResultList();

        // evaluate HIV result tests
        for (Object[] val: lst) {
            HIVResult aux = HIVResult.values()[(Integer)val[0]];

            // positive has priority over any result
            if (aux == HIVResult.POSITIVE) {
                res = HIVResult.POSITIVE;
            } else {
                // if negative or no result is set yet, get this result
                if ((aux == HIVResult.NEGATIVE) || (res == null)) {
                    res = aux;
                }
            }

            Date startedARTDate = (Date)val[1];
            Date startedCPTDate = (Date)val[2];

            tbcase.setStartedARTdate(minDate(startedARTDate, tbcase.getStartedARTdate()));
            tbcase.setStartedCPTdate(minDate(startedCPTDate, tbcase.getStartedCPTdate()));
        }

        tbcase.setHivResult(res);
    }

    /**
     * Convert an instance of {@link HIVPosition} to an instance of {@link HIVResult}
     * @param pos
     * @return
     */
    private HIVResult convertToResult(HIVPosition pos) {
        if (pos == null) {
            return null;
        }

        switch (pos) {
            case POSITIVE: return HIVResult.POSITIVE;
            case NEGATIVE: return HIVResult.NEGATIVE;
            default: return null;
        }
    }

    /**
     * Return the minimum date between the 2 given dates. Null dates is ignored
     * @param dt1
     * @param dt2
     * @return
     */
    private Date minDate(Date dt1, Date dt2) {
        if (dt1 == null) {
            return dt2;
        }

        if (dt2 == null) {
            return dt1;
        }

        return dt1.getTime() < dt2.getTime() ? dt1 : dt2;
    }
}
