"use strict";

var vl = require('vega-lite'),
  isDimension = vl.encDef.isDimension,
  isOrdinalScale = vl.encDef.isOrdinalScale;

var vlmarktypes = module.exports = getMarktypes;

var marksRule = vlmarktypes.rule = {
  point:  pointRule,
  bar:    barRule,
  line:   lineRule,
  area:   areaRule, // area is similar to line
  text:   textRule,
  tick:   tickRule
};

function getMarktypes(encoding, stats, opt) {
  return opt.marktypeList.filter(function(markType){
    return vlmarktypes.satisfyRules(encoding, markType, stats, opt);
  });
}

vlmarktypes.satisfyRules = function (encoding, markType, stats, opt) {
  var mark = vl.compiler.marks[markType],
    reqs = mark.requiredEncoding,
    support = mark.supportedEncoding;

  for (var i in reqs) { // all required encodings in enc
    if (!(reqs[i] in encoding)) return false;
  }

  for (var encType in encoding) { // all encodings in enc are supported
    if (!support[encType]) return false;
  }

  return !marksRule[markType] || marksRule[markType](encoding, stats, opt);
};

function facetRule(fieldDef, stats, opt) {
  return vl.encDef.cardinality(fieldDef, stats) <= opt.maxCardinalityForFacets;
}

function facetsRule(encoding, stats, opt) {
  if(encoding.row && !facetRule(encoding.row, stats, opt)) return false;
  if(encoding.col && !facetRule(encoding.col, stats, opt)) return false;
  return true;
}

function pointRule(encoding, stats, opt) {
  if(!facetsRule(encoding, stats, opt)) return false;
  if (encoding.x && encoding.y) {
    // have both x & y ==> scatter plot / bubble plot

    var xIsDim = isDimension(encoding.x),
      yIsDim = isDimension(encoding.y);

    // For OxO
    if (xIsDim && yIsDim) {
      // shape doesn't work with both x, y as ordinal
      if (encoding.shape) {
        return false;
      }

      // TODO(kanitw): check that there is quant at least ...
      if (encoding.color && isDimension(encoding.color)) {
        return false;
      }
    }

  } else { // plot with one axis = dot plot
    if (opt.omitDotPlot) return false;

    // Dot plot should always be horizontal
    if (opt.omitTranpose && encoding.y) return false;

    // dot plot shouldn't have other encoding
    if (opt.omitDotPlotWithExtraEncoding && vl.keys(encoding).length > 1) return false;

    // dot plot with shape is non-sense
    if (encoding.shape) return false;
  }
  return true;
}

function tickRule(encoding, stats, opt) {
  // jshint unused:false
  if (encoding.x || encoding.y) {
    if(vl.enc.isAggregate(encoding)) return false;

    var xIsDim = isDimension(encoding.x),
      yIsDim = isDimension(encoding.y);

    return (!xIsDim && (!encoding.y || isOrdinalScale(encoding.y))) ||
      (!yIsDim && (!encoding.x || isOrdinalScale(encoding.x)));
  }
  return false;
}

function barRule(encoding, stats, opt) {
  if(!facetsRule(encoding, stats, opt)) return false;

  // bar requires at least x or y
  if (!encoding.x && !encoding.y) return false;

  if (opt.omitSizeOnBar && encoding.size !== undefined) return false;

  // FIXME actually check if there would be occlusion #90
  // need to aggregate on either x or y
  var aggEitherXorY =
    (!encoding.x || encoding.x.aggregate === undefined) ^
    (!encoding.y || encoding.y.aggregate === undefined);


  if (aggEitherXorY) {
    var eitherXorYisDimOrNull =
      (!encoding.x || isDimension(encoding.x)) ^
      (!encoding.y || isDimension(encoding.y));

    if (eitherXorYisDimOrNull) {
      var aggregate = encoding.x.aggregate || encoding.y.aggregate;
      return !(opt.omitStackedAverage && aggregate ==='mean' && encoding.color);
    }
  }

  return false;
}

function lineRule(encoding, stats, opt) {
  if(!facetsRule(encoding, stats, opt)) return false;

  // TODO(kanitw): add omitVerticalLine as config

  // FIXME truly ordinal data is fine here too.
  // Line chart should be only horizontal
  // and use only temporal data
  return encoding.x.type == 'T' && encoding.x.timeUnit && encoding.y.type == 'Q' && encoding.y.aggregate;
}

function areaRule(encoding, stats, opt) {
  if(!facetsRule(encoding, stats, opt)) return false;

  if(!lineRule(encoding, stats, opt)) return false;

  return !(opt.omitStackedAverage && encoding.y.aggregate ==='mean' && encoding.color);
}

function textRule(encoding, stats, opt) {
  // at least must have row or col and aggregated text values
  return (encoding.row || encoding.col) && encoding.text && encoding.text.aggregate && !encoding.x && !encoding.y && !encoding.size &&
    (!opt.alwaysGenerateTableAsHeatmap || !encoding.color);
}

@@ -14,16 +14,19 @@
 %0A%0Avar vl
+Enc
  = requi
@@ -42,13 +42,124 @@
 lite
-'),%0A 
+/src/enc');%0Avar vlEncDef = require('vega-lite/src/encdef');%0Avar vlValidate = require('vega-lite/src/validate');%0A%0Avar
  isD
@@ -167,26 +167,25 @@
 mension = vl
-.e
+E
 ncDef.isDime
@@ -189,19 +189,21 @@
 imension
-,%0A 
+;%0Avar
  isOrdin
@@ -214,18 +214,17 @@
 ale = vl
-.e
+E
 ncDef.is
@@ -236,16 +236,47 @@
 alScale;
+%0Avar util = require('../util');
 %0A%0Avar vl
@@ -760,356 +760,127 @@
 %7B%0A  
-var mark = vl.compiler.marks%5BmarkType%5D,%0A    reqs = mark.requiredEncoding,%0A    support = mark.supportedEncoding;%0A%0A  for (var i in reqs) %7B // all required encodings in enc%0A    if (!(reqs%5Bi%5D in encoding)) return false;%0A  %7D%0A%0A  for (var encType in encoding) %7B // all encodings in enc are supported%0A    if (!support%5BencType%5D) return false;%0A  %7D%0A%0A  return 
+return vlValidate.getEncodingMappingError(%7B%0A      marktype: markType,%0A      encoding: encoding%0A    %7D) === null &&%0A    (
 !mar
@@ -940,16 +940,17 @@
 ts, opt)
+)
 ;%0A%7D;%0A%0Afu
@@ -1001,18 +1001,17 @@
 eturn vl
-.e
+E
 ncDef.ca
@@ -2136,17 +2136,19 @@
 ding && 
-v
+uti
 l.keys(e
@@ -2393,18 +2393,17 @@
    if(vl
-.e
+E
 nc.isAgg

use vl.validate in marktypes