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.ExamDST;
import org.msh.tb.entities.ExamXpert;
import org.msh.tb.entities.enums.DstResult;
import org.msh.tb.entities.enums.RifResistance;
import org.msh.tb.entities.enums.XpertRifResult;
import org.msh.tb.misc.EntityEvent;
import org.msh.tb.ng.entities.TbCaseNG;

import javax.persistence.EntityManager;
import java.util.List;

/**
 * Created by rmemoria on 2/9/17.
 */
@Name("RifResistanceCalcService")
@AutoCreate
@Scope(ScopeType.APPLICATION)
public class RifResistanceCalcService {

    @Observer("entity.ExamXpertNG")
    public void onXpertChange(EntityEvent evt) {
        ExamXpert exam = (ExamXpert)evt.getEntity();
        TbCaseNG tbcase = (TbCaseNG)exam.getTbcase();
        updateRifResistance(tbcase);
    }

    @Observer("entity.ExamDST")
    public void onDSTChange(EntityEvent evt) {
        ExamDST exam = (ExamDST)evt.getEntity();
        TbCaseNG tbcase = (TbCaseNG)exam.getTbcase();
        updateRifResistance(tbcase);
    }

    /**
     * Update the rifampicin resistance status
     * @param tbcase
     */
    private void updateRifResistance(TbCaseNG tbcase) {
        EntityManager em = (EntityManager) Component.getInstance("entityManager");

        // calculate and set value in tbcase
        tbcase.setRifResistance(calcRifResistance(tbcase));

        // persist in DB
        em.persist(tbcase);
        em.flush();
    }

    public static RifResistance calcRifResistance(TbCaseNG tbcase) {
        EntityManager em = (EntityManager) Component.getInstance("entityManager");

        // check if there is any Xpert RR
        Number countRR = (Number)em.createQuery("select count(*) from ExamXpert where tbcase.id = :id and rifResult = :rr")
                .setParameter("id", tbcase.getId())
                .setParameter("rr", XpertRifResult.RIF_DETECTED)
                .getSingleResult();

        Number count = (Number)em.createQuery("select count(*) from ExamXpert where tbcase.id = :id")
                .setParameter("id", tbcase.getId())
                .getSingleResult();

        DstResult dstRes = calcDstRifResult(tbcase);

        RifResistance rr;

        // the first level of if - else evaluates the xperts counts queried before.
        if (count.intValue() == 0) {
            // result based on xpert queries
            rr = RifResistance.NOTDONE;
            if (dstRes == DstResult.RESISTANT) {
                rr = RifResistance.DST;
            } else if (dstRes == DstResult.SUSCEPTIBLE) {
                rr = RifResistance.SUSCEPTIBLE;
            }
        } else if (countRR.intValue() == 0) {
            // result based on xpert queries
            rr = RifResistance.SUSCEPTIBLE;
            if (dstRes == DstResult.RESISTANT) {
                rr = RifResistance.DST;
            } else if (dstRes == DstResult.SUSCEPTIBLE) {
                rr = RifResistance.SUSCEPTIBLE;
            }
        } else {
            // result based on xpert queries
            rr = RifResistance.XPERT;
            if (dstRes == DstResult.RESISTANT) {
                rr = RifResistance.XPERT_DST;
            } else if (dstRes == DstResult.SUSCEPTIBLE) {
                rr = RifResistance.FALSE_POSITIVE;
            }
        }

        return rr;
    }

    /**
     * Check DST result of the given case and compare its value with the previous
     * @param tbcase
     * @return
     */
    private static DstResult calcDstRifResult(TbCaseNG tbcase) {
        EntityManager em = (EntityManager)Component.getInstance("entityManager");

        // check DST results
        List<DstResult> lst = em.createQuery("select result from ExamDSTResult " +
                "where exam.tbcase.id = :id and result in (:res1, :res2) " +
                "and substance.abbrevName.name1 = :rname")
                .setParameter("id", tbcase.getId())
                .setParameter("res1", DstResult.RESISTANT)
                .setParameter("res2", DstResult.SUSCEPTIBLE)
                .setParameter("rname", "R")
                .getResultList();

        boolean dstSusceptible = false;

        for (DstResult res: lst) {
            if (res == DstResult.RESISTANT) {
                return DstResult.RESISTANT;
            } else if (res == DstResult.SUSCEPTIBLE) {
                dstSusceptible = true;
            }
        }

        if (dstSusceptible) {
            return DstResult.SUSCEPTIBLE;
        }

        return DstResult.NOTDONE;
    }
}
