export const description = `
Execution Tests for unary address-of and indirection (dereference)
`;

import { makeTestGroup } from '../../../../../common/framework/test_group.js';
import { keysOf } from '../../../../../common/util/data_tables.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../../gpu_test.js';
import { ScalarKind, scalarType } from '../../../../util/conversion.js';
import { sparseScalarF32Range } from '../../../../util/math.js';
import {
  allButConstInputSource,
  basicExpressionWithPredeclarationBuilder,
  run,
} from '../expression.js';

export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

// All the ways to deref an expression
const kDerefCases = {
  deref_address_of_identifier: {
    wgsl: '(*(&a))',
    requires_pointer_composite_access: false,
  },
  deref_pointer: {
    wgsl: '(*p)',
    requires_pointer_composite_access: false,
  },
  address_of_identifier: {
    wgsl: '(&a)',
    requires_pointer_composite_access: true,
  },
  pointer: {
    wgsl: 'p',
    requires_pointer_composite_access: true,
  },
};

g.test('deref')
  .specURL('https://www.w3.org/TR/WGSL/#indirection')
  .desc(
    `
Expression: *e

Pointer expression dereference.
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['bool', 'u32', 'i32', 'f32', 'f16'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
      .filter(p => !kDerefCases[p.derefType].requires_pointer_composite_access)
  )
  .fn(async t => {
    if (t.params.scalarType === 'f16') {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }
    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = value;
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl};
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

g.test('deref_index')
  .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
  .desc(
    `
Expression: (*e)[index]

Pointer expression dereference as lhs of index accessor expression
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['bool', 'u32', 'i32', 'f32', 'f16'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
  )
  .beforeAllSubcases(t => {
    t.skipIf(
      kDerefCases[t.params.derefType].requires_pointer_composite_access &&
        !t.hasLanguageFeature('pointer_composite_access')
    );
  })
  .fn(async t => {
    if (t.params.scalarType === 'f16') {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }
    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = array<${type}, 1>(value);
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl}[0];
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

g.test('deref_member')
  .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
  .desc(
    `
Expression: (*e).member

Pointer expression dereference as lhs of member accessor expression
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [undefined, 2, 3, 4] as const)
      .combine('scalarType', ['bool', 'u32', 'i32', 'f32', 'f16'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
  )
  .beforeAllSubcases(t => {
    t.skipIf(
      kDerefCases[t.params.derefType].requires_pointer_composite_access &&
        !t.hasLanguageFeature('pointer_composite_access')
    );
  })
  .fn(async t => {
    if (t.params.scalarType === 'f16') {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }
    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = t.params.vectorize ? `vec${t.params.vectorize}<${elemType}>` : elemType;
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `struct S {
        m : ${type}
      }
      fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = S(value);
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl}.m;
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });

g.test('deref_swizzle')
  .specURL('https://www.w3.org/TR/WGSL/#logical-expr')
  .desc(
    `
Expression: (*e).swizzle

Pointer expression dereference as lhs of swizzle expression
`
  )
  .params(u =>
    u
      .combine('inputSource', allButConstInputSource)
      .combine('vectorize', [2, 3, 4] as const)
      .combine('scalarType', ['bool', 'u32', 'i32', 'f32', 'f16'] as ScalarKind[])
      .combine('derefType', keysOf(kDerefCases))
  )
  .beforeAllSubcases(t => {
    t.skipIf(
      kDerefCases[t.params.derefType].requires_pointer_composite_access &&
        !t.hasLanguageFeature('pointer_composite_access')
    );
  })
  .fn(async t => {
    if (t.params.scalarType === 'f16') {
      t.skipIfDeviceDoesNotHaveFeature('shader-f16');
    }
    const ty = scalarType(t.params.scalarType);
    const cases = sparseScalarF32Range().map(e => {
      return { input: ty.create(e), expected: ty.create(e) };
    });
    const elemType = ty.kind;
    const type = `vec${t.params.vectorize}<${elemType}>`;
    const swizzle = 'xyzw'.slice(0, t.params.vectorize);
    const shaderBuilder = basicExpressionWithPredeclarationBuilder(
      value => `get_dereferenced_value(${value})`,
      `fn get_dereferenced_value(value: ${type}) -> ${type} {
        var a = value;
        let p = &a;
        return ${kDerefCases[t.params.derefType].wgsl}.${swizzle};
      }`
    );
    await run(t, shaderBuilder, [ty], ty, t.params, cases);
  });
