var util = require('../util'),
  consts = require('../consts'),
  vl = require('vegalite'),
  isDimension = vl.field.isDimension;

module.exports = projections;

// TODO support other mode of projections generation
// powerset, chooseK, chooseKorLess are already included in the util

/**
 * fields
 * @param  {[type]} fields array of fields and query information
 * @return {[type]}        [description]
 */
function projections(fields, stats, opt) {
  opt = vl.schema.util.extend(opt||{}, consts.gen.projections);

  // First categorize field, selected, fieldsToAdd, and save indices
  var selected = [], fieldsToAdd = [], fieldSets = [],
    hasSelectedDimension = false,
    hasSelectedMeasure = false,
    indices = {};

  fields.forEach(function(field, index){
    //save indices for stable sort later
    indices[field.name] = index;

    if (field.selected) {
      selected.push(field);
      if (isDimension(field)) {
        hasSelectedDimension = true;
      } else {
        hasSelectedMeasure = true;
      }
    } else if (!field.excluded && !vl.field.isCount(field)) {
      if (vl.field.isDimension(field) &&
          vl.field.cardinality(field, stats, 15) > opt.maxCardinalityForAutoAddOrdinal) {
        return;
      }
      fieldsToAdd.push(field);
    }
  });

  fieldsToAdd.sort(compareFieldsToAdd(hasSelectedDimension, hasSelectedMeasure, indices));

  var setsToAdd = util.chooseKorLess(fieldsToAdd, 1);

  setsToAdd.forEach(function(setToAdd) {
    var fieldSet = selected.concat(setToAdd);
    if (fieldSet.length > 0) {
      if (opt.omitDotPlot && fieldSet.length === 1) return;
      fieldSets.push(fieldSet);
    }
  });

  fieldSets.forEach(function(fieldSet) {
      // always append projection's key to each projection returned, d3 style.
    fieldSet.key = projections.key(fieldSet);
  });

  return fieldSets;
}

function compareFieldsToAdd(hasSelectedDimension, hasSelectedMeasure, indices) {
  return function(a, b){
    var aIsDim = isDimension(a), bIsDim = isDimension(b);
    // sort by type of the data
    if (aIsDim ^ bIsDim) {
      if (!hasSelectedDimension) {
        if (!aIsDim && bIsDim) {
          return 1;
        }
        return -1;
      } else if (!hasSelectedMeasure) {
        if (aIsDim && !bIsDim) {
          return 1;
        }
        return 1;
      }
    }
    //make the sort stable
    return indices[a.name] - indices[b.name];
  };
}

projections.key = function(projection) {
  return projection.map(function(field) {
    return vl.field.isCount(field) ? 'count' : field.name;
  }).join(',');
};

@@ -1843,16 +1843,70 @@
 ets;%0A%7D%0A%0A
+var typeIsMeasureScore = %7B%0A  O: 0,%0A  T: 1,%0A  Q: 2%0A%7D;%0A%0A
 function
@@ -2106,22 +2106,24 @@
 f (a
-IsDim %5E bIsDim
+.type !== b.type
 ) %7B%0A
@@ -2169,201 +2169,182 @@
     
-if (!aIsDim && bIsDim) %7B%0A          return 1;%0A        %7D%0A        return -1;%0A      %7D else if (!hasSelectedMeasure) %7B%0A        if (aIsDim && !bIsDim) %7B%0A          return 1;%0A        %7D%0A        return 1
+return typeIsMeasureScore%5Ba.type%5D - typeIsMeasureScore%5Bb.type%5D;%0A      %7D else if (!hasSelectedMeasure) %7B%0A        return typeIsMeasureScore%5Bb.type%5D - typeIsMeasureScore%5Ba.type%5D
 ;%0A  

put time in the middle for projection ranking – fixes #71