// Adds common utility functions to the root JS object. These are then
// available for use by the map-reduce functions for each measure.
// lib/qme/mongo_helpers.rb executes this function on a database
// connection.

var root = this;



root.map = function(logic,record, population, denominator, numerator, exclusion, denexcep, msrpopl, msrpoplex, observ, occurrenceId, isContinuousVariable, stratification, correlation_id) {
  var value = {IPP: 0, patient_id: record._id,
               medical_record_id: record.medical_record_number,
               first: record.given(), last: record.last(), gender: record.gender(),
               birthdate: record.birthdate,
               provider_performances: record.provider_performances,
               race: record.race(), ethnicity: record.ethnicity(), languages: record.languages(),
               payer: extract_payer(record)};

  value.measure_id = logic.hqmf_id
  value.sub_id = logic.sub_id
  value.nqf_id = logic.nqf_id
  value.effective_date = logic.effective_date;
  value.correlation_id = correlation_id;

  if (isContinuousVariable) {
    value = calculateCV(record, population, msrpopl, msrpoplex, observ, occurrenceId, value, stratification)
  } else {
    value = calculate(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value, stratification)
  }

  if (typeof Logger != 'undefined') {
    value['logger'] = Logger.logger
    value['rationale'] = Logger.rationale
  }
  
  if (value.provider_performances) {
    var tmp = [];
    for(var i=0; i<value.provider_performances.length; i++) {
      var performance = value.provider_performances[i];
      if ((performance['start_date'] <= logic.effective_date || performance['start_date'] == null) && (performance['end_date'] >= logic.effective_date || performance['end_date'] == null))
        tmp.push(performance);
    }
    if (tmp.length == 0) tmp = null;
    value.provider_performances = tmp;
  } else {
    value.provider_performances = null;
  }

  return value;
};

root.extract_payer = function(record) {
  var payer = {};
  if(record.insurance_providers && record.insurance_providers.length > 0){
    var ip = record.insurance_providers[0];
    if(ip.codes.SOP && ip.codes.SOP.length >0){
      payer["code"] = ip.codes.SOP[0];
      payer["codeSystem"] = "SOP";
    }
  }
  return payer;
};

root.calculate = function(record, population, denominator, numerator, exclusion, denexcep, occurrenceId, value, stratification) {
  
  finalSpecifics = {};
  value = _.extend(value, {IPP: 0, DENOM: 0, NUMER: 0, DENEXCEP: 0, DENEX: 0, antinumerator: 0});

  // For the moment, we are doing this to allow for the disabling of storage of finalSpecifics in Mongo.
  // At some point, it may make more sense to move this to its own variable, rather than using enable_rationale
  // For example, if Cypress starts running into finalSpecifics size issues.
  if (Logger.enable_rationale) {
    value = _.extend(value, {finalSpecifics: finalSpecifics});
  }

  var strat;
  var ipp;
  var validStrat = false;
  if(stratification) {
    strat = stratification()
    hqmf.SpecificsManager.storeFinal('STRAT', strat, finalSpecifics);
    value.STRAT = hqmf.SpecificsManager.countUnique(occurrenceId, strat);
    if (hqmf.SpecificsManager.validate(strat)) {
      ipp = hqmf.SpecificsManager.intersectSpecifics(population(), strat, occurrenceId);
      validStrat = true;
    }
  } else {
    ipp = population();
  }

  if (ipp) {
    hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
    if (hqmf.SpecificsManager.validate(ipp)) {
      value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
      // if we have a stratification that does not reference an episode of care then we need to take the IPP count
      if (validStrat && value.STRAT < value.IPP) value.STRAT = value.IPP;
      var denom = hqmf.SpecificsManager.intersectSpecifics(denominator(), ipp, occurrenceId);
      hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);
      if (hqmf.SpecificsManager.validate(denom)) {
        
        value.DENOM = hqmf.SpecificsManager.countUnique(occurrenceId, denom);
        var exclusions = hqmf.SpecificsManager.intersectSpecifics(exclusion(), denom, occurrenceId);
        hqmf.SpecificsManager.storeFinal('DENEX', exclusions, finalSpecifics);
        if (hqmf.SpecificsManager.validate(exclusions)) {
          value.DENEX = hqmf.SpecificsManager.countUnique(occurrenceId, exclusions);
          denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, exclusions);
        }

      }
      // DENEX is a subset of the denominator, so we should set the specifics before the exclusion
      // hqmf.SpecificsManager.storeFinal('DENOM', denom, finalSpecifics);

      // we need to check the denominator again to make sure we still have viable candidates after 
      // exclusions have been removed
      if (hqmf.SpecificsManager.validate(denom)) {
        var numer = hqmf.SpecificsManager.intersectSpecifics(numerator(), denom, occurrenceId);
        hqmf.SpecificsManager.storeFinal('NUMER', numer, finalSpecifics);
        if (hqmf.SpecificsManager.validate(numer)) {
          value.NUMER = hqmf.SpecificsManager.countUnique(occurrenceId, numer);
        }
        
        var excep = hqmf.SpecificsManager.intersectSpecifics(denexcep(), denom, occurrenceId);
        hqmf.SpecificsManager.storeFinal('DENEXCEP', excep, finalSpecifics);
        if (hqmf.SpecificsManager.validate(excep)) {
          excep = hqmf.SpecificsManager.exclude(occurrenceId, excep, numer);
          value.DENEXCEP = hqmf.SpecificsManager.countUnique(occurrenceId, excep);
          denom = hqmf.SpecificsManager.exclude(occurrenceId, denom, excep);
        }
        value.antinumerator = value.DENOM-value.NUMER;
      }

    }
  }
  return value;
};

root.calculateCV = function(record, population, msrpopl, msrpoplex, observ, occurrenceId, value, stratification) {
  finalSpecifics = {};
  value = _.extend(value, {MSRPOPL: 0, MSRPOPLEX: 0, values: []});
  
  // For the moment, we are doing this to allow for the disabling of storage of finalSpecifics in Mongo.
  // At some point, it may make more sense to move this to its own variable, rather than using enable_rationale
  // For example, if Cypress starts running into finalSpecifics size issues.
  if (Logger.enable_rationale) {
    value = _.extend(value, {finalSpecifics: finalSpecifics});
  }

  var strat;
  var ipp;
  var validStrat = false;
  if(stratification) {
    strat = stratification()
    hqmf.SpecificsManager.storeFinal('STRAT', strat, finalSpecifics);
    value.STRAT = hqmf.SpecificsManager.countUnique(occurrenceId, strat);
    if (hqmf.SpecificsManager.validate(strat)) {
      ipp = hqmf.SpecificsManager.intersectSpecifics(population(), strat, occurrenceId);
      validStrat = true;
    }
  } else {
    ipp = population();
  }


  if (ipp) {
    hqmf.SpecificsManager.storeFinal('IPP', ipp, finalSpecifics);
    if (hqmf.SpecificsManager.validate(ipp)) {
      value.IPP = hqmf.SpecificsManager.countUnique(occurrenceId, ipp);
      // if we have a stratification that does not reference an episode of care then we need to take the IPP count
      if (validStrat && value.STRAT < value.IPP) value.STRAT = value.IPP;
      var measurePopulation = hqmf.SpecificsManager.intersectSpecifics(msrpopl(), ipp, occurrenceId);
      hqmf.SpecificsManager.storeFinal('MSRPOPL', measurePopulation, finalSpecifics);
      if (hqmf.SpecificsManager.validate(measurePopulation)) {
        
        var observations = observ(measurePopulation.specificContext);
        value.MSRPOPL = hqmf.SpecificsManager.countUnique(occurrenceId, measurePopulation);
        value.values = observations;

        var measurePopulationExclusions = hqmf.SpecificsManager.intersectSpecifics(msrpoplex(), measurePopulation, occurrenceId);
        hqmf.SpecificsManager.storeFinal('MSRPOPLEX', measurePopulationExclusions, finalSpecifics);
        if (hqmf.SpecificsManager.validate(measurePopulationExclusions)) {
          value.MSRPOPLEX = hqmf.SpecificsManager.countUnique(occurrenceId, measurePopulationExclusions);
          measurePopulation = hqmf.SpecificsManager.exclude(occurrenceId, measurePopulation, measurePopulationExclusions);
        }
      }
    }
  }
  return value;
};

 
