package performa.utils;

import java.util.*;
import oneit.logging.*;
import oneit.objstore.*;
import oneit.objstore.rdbms.filters.*;
import oneit.utils.*;
import oneit.utils.filter.Filter;
import oneit.utils.math.Statistics;
import oneit.utils.parsers.FieldException;
import performa.orm.*;
import performa.orm.types.*;

/**
 *
 * @author nilu
 */
public class AnalysisEngine 
{
    private static final    Long    MAX_VALID_FACTOR_NUMBER =   49L; //Don't consider unusally high/low answer factors i.e 50/51
    
    public static void analyseAnswers(JobApplication jobApplication) throws StorageException, FieldException
    {
        LogMgr.log(JobApplication.LOG, LogLevel.PROCESSING1, "Inside AnalysisEngine --> analyseAnswers");
        
        ObjectTransaction           objTran         =   jobApplication.getTransaction();
        Set<Factor>                 levelFactors    =   jobApplication.pipelineJobApplication().toJob().toLevel().toFactors().toFactor().uniqueVals();
        Filter<FactorQuestionLink>  factorFilter    =   FactorQuestionLink.SearchByAll().andFactor(new InFilter(levelFactors));
        
        //Preloading Data
        jobApplication.pipelineJobApplication().toProfileAssessmentAnswers().toQuestion().toSection().uniqueVals();
        jobApplication.pipelineJobApplication().toProfileAssessmentAnswers().toQuestion().toFactors().toFactor().uniqueVals();
        Factor.pipesFactor(levelFactors).toResults().uniqueVals();
        Factor.pipesFactor(levelFactors).toLevelFactorTypes().uniqueVals();

        LogMgr.log(JobApplication.LOG, LogLevel.PROCESSING1, "Processing Job Application ", jobApplication);

        Map<Factor, Integer>    factorScoreMap  =   new HashMap();
            
        for (Answer answer : jobApplication.getProfileAssessmentAnswersSet())
        {
            Set<FactorQuestionLink> links   =   answer.pipelineAnswer().toQuestion().toFactors(factorFilter).uniqueVals();

            for (FactorQuestionLink link : links) 
            {
                int     factorScore =   0;
                Factor  factor      =   link.getFactor();
                
                if(factor.getID().longID() <= MAX_VALID_FACTOR_NUMBER)  //Don't consider unusally high/low answer factors i.e 50/51
                {
                    if(factorScoreMap.containsKey(factor))
                    {
                        factorScore =   factorScoreMap.get(factor);
                    }

                    if(link.isTrue(link.getReverseScore()))
                    {
                        if(answer.getQuestion().getQuestionType() == QuestionType.IPSATIVE)
                        {
                            factorScore += (10 - answer.getAnswerNo());
                        }
                        else
                        {
                            factorScore += (8 - answer.getAnswerNo());
                        }
                    }
                    else
                    {
                        factorScore +=  StringUtils.subNulls(answer.getAnswerNo(), 0);
                    }
                    factorScoreMap.put(factor, factorScore);
                }
            }
        }

        //Unusually High Answers/Unusually Low Answers
        Filter<Question>                questFilter             =   Question.SearchByAll().andHighLowFactor(new IsNotNullFilter());
        MultiHashtable<Factor, Answer>  highLowAnswersByFactor  =   new MultiHashtable();
        
        highLowAnswersByFactor.groupValues(jobApplication.getProfileAssessmentAnswersSet(), Answer.pipesAnswer().toQuestion(questFilter).toHighLowFactor());
        
        for(Factor factor : highLowAnswersByFactor.keySet())
        {
            if(factor != null)
            {
                double  answerTotal =   Statistics.sum(Answer.pipesAnswer(highLowAnswersByFactor.getValuesForKeyNN(factor)).toAnswerNo().vals());
                
                factorScoreMap.put(factor, Double.valueOf(answerTotal).intValue());
            }
        }
        
        TestAnalysis    testAnalysis    =   TestAnalysis.createTestAnalysis(objTran);
            
        jobApplication.getJob().getLevel().addToTestAnalysises(testAnalysis);
        jobApplication.getCandidate().addToTestAnalysises(testAnalysis);
        testAnalysis.setJob(jobApplication.getJob());
        
        for(Factor factor : factorScoreMap.keySet())
        {
            int score   =   factorScoreMap.get(factor);

            LogMgr.log(JobApplication.LOG, LogLevel.PROCESSING1, "Candidate:", jobApplication.getCandidate(), " Factor:", factor, " Score:", score);

            Filter<FactorScoreResult>   factorScoreFilter   =   FactorScoreResult.SearchByAll().andLevel(new EqualsFilter<>(jobApplication.getJob().getLevel()))
                                                                                            .andFromScore(new LessThanEqualFilter<>(score))
                                                                                            .andToScore(new GreaterThanEqualFilter<>(score));

            List<FactorScoreResult>     factorScoreResults  =   (List<FactorScoreResult>) factor.pipelineFactor().toResults(factorScoreFilter).vals();

            if(factorScoreResults != null && !factorScoreResults.isEmpty())
            {
                FactorScoreResult   factorScoreResult   =   factorScoreResults.get(0);

                FactorScore factorScore =   FactorScore.createFactorScore(objTran);

                factorScore.setFactor(factor);
                factorScore.setLevel(jobApplication.getJob().getLevel());
                factorScore.setScore(score);
                factorScore.setColorCode(factorScoreResult.getColorCode());
                factorScore.setColorRank(factorScoreResult.getColorCode() != null ? factorScoreResult.getColorCode().getColorRank() : 0);
                factorScore.setNarrative(factorScoreResult.getNarrative());
                
                /**
                 *  Weighted Score.
                 * 
                 *  10 where color rank = 1 and flag = Primary
                 *  5 where color rank = 1 and flag = Secondary
                 *  4 where color rank = 2 and flag = Primary
                 *  2 where color rank = 2 and flag = Secondary
                */
                Integer weightedScore   =   0;
                
                if(factorScoreResult.getColorCode() == ColorCode.GREEN || factorScoreResult.getColorCode() == ColorCode.AMBER)
                {
                    Filter<LevelFactorType> levelFactorFilter   =   LevelFactorType.SearchByAll().andLevel(new EqualsFilter(jobApplication.getJob().getLevel()));
                    
                    LevelFactorType levelFactorType =   factor.pipelineFactor().toLevelFactorTypes(levelFactorFilter).val();
                    
                    if(levelFactorType != null && levelFactorType.getTypeFlag() != null)
                    {
                        if(factorScoreResult.getColorCode() == ColorCode.GREEN)//Color Code: Green  --> Color Rank = 1
                        {
                            weightedScore   =   (levelFactorType.getTypeFlag() == TypeFlag.PRIMARY) ? 10 : 5;
                        }
                        else    //Color Code: Amber  --> Color Rank = 2
                        {
                            weightedScore   =   (levelFactorType.getTypeFlag() == TypeFlag.PRIMARY) ? 4 : 2;
                        }
                    }
                }
                factorScore.setWghtdScore(weightedScore);

                testAnalysis.addToFactorScores(factorScore);
                jobApplication.getCandidate().addToFactorScores(factorScore);
            }
        }
        
        LogMgr.log(JobApplication.LOG, LogLevel.PROCESSING1, "AnalysisEngine --> analyseAnswers completed");
    }
}