import { Spec } from 'vega';

export const spec: Spec = {
  $schema: 'https://vega.github.io/schema/vega/v5.json',
  description: 'Drag out a rectangular brush to highlight points.',
  autosize: 'pad',
  padding: 5,
  width: 200,
  height: 200,
  style: 'cell',
  data: [
    { name: 'brush_store' },
    { name: 'grid_store' },
    {
      name: 'source_0',
      url: 'data/cars.json',
      format: { type: 'json' },
      transform: [
        {
          type: 'filter',
          expr: 'datum["Horsepower"] != null && isFinite(datum["Horsepower"]) && datum["Miles_per_Gallon"] != null && isFinite(datum["Miles_per_Gallon"])'
        }
      ]
    }
  ],
  signals: [
    {
      name: 'unit',
      value: {},
      on: [{ events: 'mousemove', update: 'isTuple(group()) ? group() : unit' }]
    },
    {
      name: 'brush',
      update: 'vlSelectionResolve("brush_store", "union")'
    },
    { name: 'grid', update: 'vlSelectionResolve("grid_store")' },
    {
      name: 'brush_x',
      value: [],
      on: [
        {
          events: {
            source: 'scope',
            type: 'mousedown',
            filter: [
              'event.shiftKey',
              '!event.item || event.item.mark.name !== "brush_brush"',
              'inScope(event.item)'
            ]
          },
          update: '[x(unit), x(unit)]'
        },
        {
          events: {
            source: 'window',
            type: 'mousemove',
            consume: true,
            between: [
              {
                source: 'scope',
                type: 'mousedown',
                filter: [
                  'event.shiftKey',
                  '!event.item || event.item.mark.name !== "brush_brush"',
                  'inScope(event.item)'
                ]
              },
              { source: 'window', type: 'mouseup' }
            ]
          },
          update: '[brush_x[0], clamp(x(unit), 0, width)]'
        },
        {
          events: { scale: 'x' },
          update:
            'span(brush_x) ? [scale("x", brush_Horsepower[0]), scale("x", brush_Horsepower[1])] : brush_x'
        },
        {
          events: { signal: 'brush_translate_delta' },
          update:
            'clampRange(panLinear(brush_translate_anchor.extent_x, brush_translate_delta.x / span(brush_translate_anchor.extent_x)), 0, width)'
        },
        {
          events: { signal: 'brush_zoom_delta' },
          update: 'clampRange(zoomLinear(brush_x, brush_zoom_anchor.x, brush_zoom_delta), 0, width)'
        }
      ]
    },
    {
      name: 'brush_Horsepower',
      on: [
        {
          events: { signal: 'brush_x' },
          update: 'span(brush_x) ? invert("x", brush_x) : null'
        }
      ]
    },
    {
      name: 'brush_y',
      value: [],
      on: [
        {
          events: {
            source: 'scope',
            type: 'mousedown',
            filter: [
              'event.shiftKey',
              '!event.item || event.item.mark.name !== "brush_brush"',
              'inScope(event.item)'
            ]
          },
          update: '[y(unit), y(unit)]'
        },
        {
          events: {
            source: 'window',
            type: 'mousemove',
            consume: true,
            between: [
              {
                source: 'scope',
                type: 'mousedown',
                filter: [
                  'event.shiftKey',
                  '!event.item || event.item.mark.name !== "brush_brush"',
                  'inScope(event.item)'
                ]
              },
              { source: 'window', type: 'mouseup' }
            ]
          },
          update: '[brush_y[0], clamp(y(unit), 0, height)]'
        },
        {
          events: { scale: 'y' },
          update:
            'span(brush_y) ? [scale("y", brush_Miles_per_Gallon[0]), scale("y", brush_Miles_per_Gallon[1])] : brush_y'
        },
        {
          events: { signal: 'brush_translate_delta' },
          update:
            'clampRange(panLinear(brush_translate_anchor.extent_y, brush_translate_delta.y / span(brush_translate_anchor.extent_y)), 0, height)'
        },
        {
          events: { signal: 'brush_zoom_delta' },
          update:
            'clampRange(zoomLinear(brush_y, brush_zoom_anchor.y, brush_zoom_delta), 0, height)'
        }
      ]
    },
    {
      name: 'brush_Miles_per_Gallon',
      on: [
        {
          events: { signal: 'brush_y' },
          update: 'span(brush_y) ? invert("y", brush_y): null'
        }
      ]
    },
    {
      name: 'brush_tuple',
      on: [
        {
          events: [{ signal: 'brush_Horsepower' }, { signal: 'brush_Miles_per_Gallon' }],
          update:
            'brush_Horsepower && brush_Miles_per_Gallon ? {unit: "", fields: brush_tuple_fields, values: [brush_Horsepower,brush_Miles_per_Gallon]} : null'
        }
      ]
    },
    {
      name: 'brush_tuple_fields',
      value: [
        { field: 'Horsepower', channel: 'x', type: 'R' },
        { field: 'Miles_per_Gallon', channel: 'y', type: 'R' }
      ]
    },
    {
      name: 'brush_translate_anchor',
      value: {},
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'mousedown',
              filter: [
                'event.shiftKey',
                "event.item&&event.item.mark.name==='brush_brush'",
                'inScope(event.item)'
              ],
              markname: 'brush_brush'
            }
          ],
          update: '{x: x(unit), y: y(unit), extent_x: slice(brush_x), extent_y: slice(brush_y)}'
        }
      ]
    },
    {
      name: 'brush_translate_delta',
      value: {},
      on: [
        {
          events: [
            {
              source: 'window',
              type: 'mousemove',
              consume: true,
              between: [
                {
                  source: 'scope',
                  type: 'mousedown',
                  filter: [
                    'event.shiftKey',
                    "event.item&&event.item.mark.name==='brush_brush'",
                    'inScope(event.item)'
                  ],
                  markname: 'brush_brush'
                },
                { source: 'window', type: 'mouseup' }
              ]
            }
          ],
          update: '{x: brush_translate_anchor.x - x(unit), y: brush_translate_anchor.y - y(unit)}'
        }
      ]
    },
    {
      name: 'brush_zoom_anchor',
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'wheel',
              consume: true,
              filter: [
                'event.shiftKey',
                "event.item&&event.item.mark.name==='brush_brush'",
                'inScope(event.item)'
              ],
              markname: 'brush_brush'
            }
          ],
          update: '{x: x(unit), y: y(unit)}'
        }
      ]
    },
    {
      name: 'brush_zoom_delta',
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'wheel',
              consume: true,
              filter: [
                'event.shiftKey',
                "event.item&&event.item.mark.name==='brush_brush'",
                'inScope(event.item)'
              ],
              markname: 'brush_brush'
            }
          ],
          force: true,
          update: 'pow(1.001, event.deltaY * pow(16, event.deltaMode))'
        }
      ]
    },
    {
      name: 'brush_modify',
      update: 'modify("brush_store", brush_tuple, {unit: ""})'
    },
    {
      name: 'grid_Horsepower',
      on: [
        {
          events: { signal: 'grid_translate_delta' },
          update: 'panLinear(grid_translate_anchor.extent_x, -grid_translate_delta.x / width)'
        },
        {
          events: { signal: 'grid_zoom_delta' },
          update: 'zoomLinear(domain("x"), grid_zoom_anchor.x, grid_zoom_delta)'
        }
      ]
    },
    {
      name: 'grid_Miles_per_Gallon',
      on: [
        {
          events: { signal: 'grid_translate_delta' },
          update: 'panLinear(grid_translate_anchor.extent_y, grid_translate_delta.y / height)'
        },
        {
          events: { signal: 'grid_zoom_delta' },
          update: 'zoomLinear(domain("y"), grid_zoom_anchor.y, grid_zoom_delta)'
        }
      ]
    },
    {
      name: 'grid_tuple',
      on: [
        {
          events: [{ signal: 'grid_Horsepower' }, { signal: 'grid_Miles_per_Gallon' }],
          update:
            'grid_Horsepower && grid_Miles_per_Gallon ? {unit: "", fields: grid_tuple_fields, values: [grid_Horsepower,grid_Miles_per_Gallon]} : null'
        }
      ]
    },
    {
      name: 'grid_tuple_fields',
      value: [
        { field: 'Horsepower', channel: 'x', type: 'R' },
        { field: 'Miles_per_Gallon', channel: 'y', type: 'R' }
      ]
    },
    {
      name: 'grid_translate_anchor',
      value: {},
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'mousedown',
              filter: ['!event.shiftKey', 'inScope(event.item)']
            }
          ],
          update: '{x: x(unit), y: y(unit), extent_x: domain("x"), extent_y: domain("y")}'
        }
      ]
    },
    {
      name: 'grid_translate_delta',
      value: {},
      on: [
        {
          events: [
            {
              source: 'window',
              type: 'mousemove',
              consume: true,
              between: [
                {
                  source: 'scope',
                  type: 'mousedown',
                  filter: ['!event.shiftKey', 'inScope(event.item)']
                },
                { source: 'window', type: 'mouseup' }
              ]
            }
          ],
          update: '{x: grid_translate_anchor.x - x(unit), y: grid_translate_anchor.y - y(unit)}'
        }
      ]
    },
    {
      name: 'grid_zoom_anchor',
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'wheel',
              consume: true,
              filter: ['!event.shiftKey', 'inScope(event.item)']
            }
          ],
          update: '{x: invert("x", x(unit)), y: invert("y", y(unit))}'
        }
      ]
    },
    {
      name: 'grid_zoom_delta',
      on: [
        {
          events: [
            {
              source: 'scope',
              type: 'wheel',
              consume: true,
              filter: ['!event.shiftKey', 'inScope(event.item)']
            }
          ],
          force: true,
          update: 'pow(1.001, event.deltaY * pow(16, event.deltaMode))'
        }
      ]
    },
    {
      name: 'grid_modify',
      update: 'modify("grid_store", grid_tuple, true)'
    }
  ],
  marks: [
    {
      name: 'brush_brush_bg',
      type: 'rect',
      clip: true,
      encode: {
        enter: { fill: { value: '#333' }, fillOpacity: { value: 0.125 } },
        update: {
          x: { signal: 'brush_x[0]' },
          y: { signal: 'brush_y[0]' },
          x2: { signal: 'brush_x[1]' },
          y2: { signal: 'brush_y[1]' }
        }
      }
    },
    {
      name: 'marks',
      type: 'symbol',
      clip: true,
      style: ['point'],
      from: { data: 'source_0' },
      encode: {
        update: {
          opacity: { value: 0.7 },
          fill: { value: 'transparent' },
          stroke: [
            {
              test: '!(length(data("brush_store"))) || (vlSelectionTest("brush_store", datum, "union"))',
              scale: 'color',
              field: 'Cylinders'
            },
            { value: 'grey' }
          ],
          tooltip: {
            signal:
              '{"Horsepower": format(datum["Horsepower"], ""), "Miles_per_Gallon": format(datum["Miles_per_Gallon"], ""), "Cylinders": \'\'+datum["Cylinders"]}'
          },
          x: { scale: 'x', field: 'Horsepower' },
          y: { scale: 'y', field: 'Miles_per_Gallon' }
        }
      }
    },
    {
      name: 'brush_brush',
      type: 'rect',
      clip: true,
      encode: {
        enter: { fill: { value: 'transparent' } },
        update: {
          x: { signal: 'brush_x[0]' },
          y: { signal: 'brush_y[0]' },
          x2: { signal: 'brush_x[1]' },
          y2: { signal: 'brush_y[1]' },
          stroke: [
            {
              test: 'brush_x[0] !== brush_x[1] && brush_y[0] !== brush_y[1]',
              value: 'white'
            },
            { value: null }
          ]
        }
      }
    }
  ],
  scales: [
    {
      name: 'x',
      type: 'linear',
      domain: { data: 'source_0', field: 'Horsepower' },
      domainRaw: { signal: 'grid["Horsepower"]' },
      range: [0, { signal: 'width' }],
      nice: true,
      zero: true
    },
    {
      name: 'y',
      type: 'linear',
      domain: { data: 'source_0', field: 'Miles_per_Gallon' },
      domainRaw: { signal: 'grid["Miles_per_Gallon"]' },
      range: [{ signal: 'height' }, 0],
      nice: true,
      zero: true
    },
    {
      name: 'color',
      type: 'ordinal',
      domain: { data: 'source_0', field: 'Cylinders', sort: true },
      range: 'ordinal',
      interpolate: 'hcl'
    }
  ],
  axes: [
    {
      scale: 'x',
      orient: 'bottom',
      grid: false,
      title: 'Horsepower',
      labelFlush: true,
      labelOverlap: true,
      tickCount: { signal: 'ceil(width/40)' },
      zindex: 1
    },
    {
      scale: 'x',
      orient: 'bottom',
      gridScale: 'y',
      grid: true,
      tickCount: { signal: 'ceil(width/40)' },
      domain: false,
      labels: false,
      maxExtent: 0,
      minExtent: 0,
      ticks: false,
      zindex: 0
    },
    {
      scale: 'y',
      orient: 'left',
      grid: false,
      title: 'Miles_per_Gallon',
      labelOverlap: true,
      tickCount: { signal: 'ceil(height/40)' },
      zindex: 1
    },
    {
      scale: 'y',
      orient: 'left',
      gridScale: 'x',
      grid: true,
      tickCount: { signal: 'ceil(height/40)' },
      domain: false,
      labels: false,
      maxExtent: 0,
      minExtent: 0,
      ticks: false,
      zindex: 0
    }
  ],
  legends: [
    {
      stroke: 'color',
      gradientLength: { signal: 'clamp(height, 64, 200)' },
      title: 'Cylinders',
      symbolFillColor: 'transparent',
      symbolOpacity: 0.7
    }
  ]
};
