import * as CHANNEL from 'vega-lite/build/src/channel';
import {Channel} from 'vega-lite/build/src/channel';
import {Config} from 'vega-lite/build/src/config';
import {DEFAULT_PROP_PRECEDENCE, toKey} from './property';
import {DEFAULT_ENUM_INDEX, EnumIndex} from './wildcard';

// We name this QueryConfig to avoid confusion with Vega-Lite's Config
export interface QueryConfig {
  verbose?: boolean;

  defaultSpecConfig?: Config;

  propertyPrecedence?: string[];

  enum?: Partial<EnumIndex>;

  /** Default ratio for number fields to be considered ordinal */
  numberNominalProportion?: number;

  /** Default cutoff for not applying the numberOrdinalProportion inference */
  numberNominalLimit?: number;

  // SPECIAL MODE
  /**
   * Allow automatically adding a special count (autoCount) field for plots
   * that contain only discrete fields. In such cases, adding count make the
   * output plots way more meaningful.
   */
  autoAddCount?: boolean;

  // CONSTRAINTS
  constraintManuallySpecifiedValue?: boolean;
  // Spec Constraints

  hasAppropriateGraphicTypeForMark?: boolean;
  omitAggregate?: boolean;
  omitAggregatePlotWithDimensionOnlyOnFacet?: boolean;
  omitAggregatePlotWithoutDimension?: boolean;
  omitBarLineAreaWithOcclusion?: boolean;
  omitBarTickWithSize?: boolean;
  omitMultipleNonPositionalChannels?: boolean;
  omitRaw?: boolean;
  omitRawContinuousFieldForAggregatePlot?: boolean;
  omitRawWithXYBothOrdinalScaleOrBin?: boolean;
  omitRepeatedField?: boolean;
  omitNonPositionalOrFacetOverPositionalChannels?: boolean;
  omitTableWithOcclusionIfAutoAddCount?: boolean;
  omitVerticalDotPlot?: boolean;
  omitInvalidStackSpec?: boolean;
  omitNonSumStack?: boolean;

  preferredBinAxis?: Channel;
  preferredTemporalAxis?: Channel;
  preferredOrdinalAxis?: Channel;
  preferredNominalAxis?: Channel;
  preferredFacet?: Channel;

  // Field Encoding Constraints
  minCardinalityForBin?: number;
  maxCardinalityForCategoricalColor?: number;
  maxCardinalityForFacet?: number;
  maxCardinalityForShape?: number;
  timeUnitShouldHaveVariation?: boolean;
  typeMatchesSchemaType?: boolean;

  // STYLIZE
  stylize?: boolean;
  smallRangeStepForHighCardinalityOrFacet?: {maxCardinality: number; rangeStep: number};
  nominalColorScaleForHighCardinality?: {maxCardinality: number; palette: string};
  xAxisOnTopForHighYCardinalityWithoutColumn?: {maxCardinality: number};

  // EFFECTIVENESS PREFERENCE
  maxGoodCardinalityForColor?: number; // FIXME: revise
  maxGoodCardinalityForFacet?: number; // FIXME: revise
  // HIGH CARDINALITY STRINGS
  minPercentUniqueForKey?: number;
  minCardinalityForKey?: number;
}

export const DEFAULT_QUERY_CONFIG: QueryConfig = {
  verbose: false,
  defaultSpecConfig: {
    line: {point: true},
    scale: {useUnaggregatedDomain: true}
  },
  propertyPrecedence: DEFAULT_PROP_PRECEDENCE.map(toKey),
  enum: DEFAULT_ENUM_INDEX,

  numberNominalProportion: 0.05,
  numberNominalLimit: 40,

  // CONSTRAINTS
  constraintManuallySpecifiedValue: false,
  // Spec Constraints -- See description inside src/constraints/spec.ts
  autoAddCount: false,

  hasAppropriateGraphicTypeForMark: true,
  omitAggregate: false,
  omitAggregatePlotWithDimensionOnlyOnFacet: true,
  omitAggregatePlotWithoutDimension: false,
  omitBarLineAreaWithOcclusion: true,
  omitBarTickWithSize: true,
  omitMultipleNonPositionalChannels: true,
  omitRaw: false,
  omitRawContinuousFieldForAggregatePlot: true,
  omitRepeatedField: true,
  omitNonPositionalOrFacetOverPositionalChannels: true,
  omitTableWithOcclusionIfAutoAddCount: true,
  omitVerticalDotPlot: false,
  omitInvalidStackSpec: true,
  omitNonSumStack: true,

  preferredBinAxis: CHANNEL.X,
  preferredTemporalAxis: CHANNEL.X,
  preferredOrdinalAxis: CHANNEL.Y, // ordinal on y makes it easier to read.
  preferredNominalAxis: CHANNEL.Y, // nominal on y makes it easier to read.
  preferredFacet: CHANNEL.ROW, // row make it easier to scroll than column

  // Field Encoding Constraints -- See description inside src/constraint/field.ts
  minCardinalityForBin: 15,
  maxCardinalityForCategoricalColor: 20,
  maxCardinalityForFacet: 20,
  maxCardinalityForShape: 6,
  timeUnitShouldHaveVariation: true,
  typeMatchesSchemaType: true,

  // STYLIZE
  stylize: true,
  smallRangeStepForHighCardinalityOrFacet: {maxCardinality: 10, rangeStep: 12},
  nominalColorScaleForHighCardinality: {maxCardinality: 10, palette: 'category20'},
  xAxisOnTopForHighYCardinalityWithoutColumn: {maxCardinality: 30},

  // RANKING PREFERENCE
  maxGoodCardinalityForFacet: 5, // FIXME: revise
  maxGoodCardinalityForColor: 7, // FIXME: revise

  // HIGH CARDINALITY STRINGS
  minPercentUniqueForKey: 0.8,
  minCardinalityForKey: 50
};

export function extendConfig(opt: QueryConfig) {
  return {
    ...DEFAULT_QUERY_CONFIG,
    ...opt,
    enum: extendEnumIndex(opt.enum)
  };
}

function extendEnumIndex(enumIndex: Partial<EnumIndex>) {
  const enumOpt: EnumIndex = {
    ...DEFAULT_ENUM_INDEX,
    ...enumIndex,
    binProps: extendNestedEnumIndex(enumIndex, 'bin'),
    scaleProps: extendNestedEnumIndex(enumIndex, 'scale'),
    axisProps: extendNestedEnumIndex(enumIndex, 'axis'),
    legendProps: extendNestedEnumIndex(enumIndex, 'legend')
  };
  return enumOpt;
}

function extendNestedEnumIndex(enumIndex: Partial<EnumIndex>, prop: 'bin' | 'scale' | 'axis' | 'legend') {
  return {
    ...DEFAULT_ENUM_INDEX[prop + 'Props'],
    ...enumIndex[prop + 'Props']
  };
}
