/* eslint-disable camelcase */

import {
    Address, BlendFactor, BlendOp, ColorMask, ComparisonFunc, CullMode, DynamicStateFlagBit,
    Filter, Format, FormatInfos, FormatType, GetTypeSize, PolygonMode, PrimitiveMode,
    ShadeModel, ShaderStageFlagBit, StencilOp, Type, DescriptorType, SamplerInfo, MemoryAccessBit, Sampler,
} from '../../cocos/gfx';
import { RenderPassStage, RenderPriority, SetIndex } from '../../cocos/rendering/define';
import { murmurhash2_32_gc } from '../../cocos/core';

const typeMap: Record<string, Type | string> = {};
typeMap[typeMap.bool = Type.BOOL] = 'bool';
typeMap[typeMap.bvec2 = Type.BOOL2] = 'bvec2';
typeMap[typeMap.bvec3 = Type.BOOL3] = 'bvec3';
typeMap[typeMap.bvec4 = Type.BOOL4] = 'bvec4';
typeMap[typeMap.int = Type.INT] = 'int';
typeMap[typeMap.ivec2 = Type.INT2] = 'ivec2';
typeMap[typeMap.ivec3 = Type.INT3] = 'ivec3';
typeMap[typeMap.ivec4 = Type.INT4] = 'ivec4';
typeMap[typeMap.uint = Type.UINT] = 'uint';
typeMap[typeMap.uvec2 = Type.UINT2] = 'uvec2';
typeMap[typeMap.uvec3 = Type.UINT3] = 'uvec3';
typeMap[typeMap.uvec4 = Type.UINT4] = 'uvec4';
typeMap[typeMap.float = Type.FLOAT] = 'float';
typeMap[typeMap.vec2 = Type.FLOAT2] = 'vec2';
typeMap[typeMap.vec3 = Type.FLOAT3] = 'vec3';
typeMap[typeMap.vec4 = Type.FLOAT4] = 'vec4';
typeMap[typeMap.mat2 = Type.MAT2] = 'mat2';
typeMap[typeMap.mat3 = Type.MAT3] = 'mat3';
typeMap[typeMap.mat4 = Type.MAT4] = 'mat4';
typeMap[typeMap.mat2x3 = Type.MAT2X3] = 'mat2x3';
typeMap[typeMap.mat2x4 = Type.MAT2X4] = 'mat2x4';
typeMap[typeMap.mat3x2 = Type.MAT3X2] = 'mat3x2';
typeMap[typeMap.mat3x4 = Type.MAT3X4] = 'mat3x4';
typeMap[typeMap.mat4x2 = Type.MAT4X2] = 'mat4x2';
typeMap[typeMap.mat4x3 = Type.MAT4X3] = 'mat4x3';
typeMap[typeMap.sampler1D = Type.SAMPLER1D] = 'sampler1D';
typeMap[typeMap.sampler1DArray = Type.SAMPLER1D_ARRAY] = 'sampler1DArray';
typeMap[typeMap.sampler2D = Type.SAMPLER2D] = 'sampler2D';
typeMap[typeMap.sampler2DArray = Type.SAMPLER2D_ARRAY] = 'sampler2DArray';
typeMap[typeMap.sampler3D = Type.SAMPLER3D] = 'sampler3D';
typeMap[typeMap.samplerCube = Type.SAMPLER_CUBE] = 'samplerCube';
typeMap[typeMap.sampler = Type.SAMPLER] = 'sampler';
typeMap[typeMap.texture1D = Type.TEXTURE1D] = 'texture1D';
typeMap[typeMap.texture1DArray = Type.TEXTURE1D_ARRAY] = 'texture1DArray';
typeMap[typeMap.texture2D = Type.TEXTURE2D] = 'texture2D';
typeMap[typeMap.texture2DArray = Type.TEXTURE2D_ARRAY] = 'texture2DArray';
typeMap[typeMap.texture3D = Type.TEXTURE3D] = 'texture3D';
typeMap[typeMap.textureCube = Type.TEXTURE_CUBE] = 'textureCube';
typeMap[typeMap.image1D = Type.IMAGE1D] = 'image1D';
typeMap[typeMap.image1DArray = Type.IMAGE1D_ARRAY] = 'image1DArray';
typeMap[typeMap.image2D = Type.IMAGE2D] = 'image2D';
typeMap[typeMap.image2DArray = Type.IMAGE2D_ARRAY] = 'image2DArray';
typeMap[typeMap.image3D = Type.IMAGE3D] = 'image3D';
typeMap[typeMap.imageCube = Type.IMAGE_CUBE] = 'imageCube';
typeMap[typeMap.subpassInput = Type.SUBPASS_INPUT] = 'subpassInput';
// variations
typeMap.int8_t = Type.INT;
typeMap.i8vec2 = Type.INT2;
typeMap.i8vec3 = Type.INT3;
typeMap.i8vec4 = Type.INT4;
typeMap.uint8_t = Type.UINT;
typeMap.u8vec2 = Type.UINT2;
typeMap.u8vec3 = Type.UINT3;
typeMap.u8vec4 = Type.UINT4;
typeMap.int16_t = Type.INT;
typeMap.i16vec2 = Type.INT2;
typeMap.i16vec3 = Type.INT3;
typeMap.i16vec4 = Type.INT4;
typeMap.uint16_t = Type.INT;
typeMap.u16vec2 = Type.UINT2;
typeMap.u16vec3 = Type.UINT3;
typeMap.u16vec4 = Type.UINT4;
typeMap.float16_t = Type.FLOAT;
typeMap.f16vec2 = Type.FLOAT2;
typeMap.f16vec3 = Type.FLOAT3;
typeMap.f16vec4 = Type.FLOAT4;
typeMap.mat2x2 = Type.MAT2;
typeMap.mat3x3 = Type.MAT3;
typeMap.mat4x4 = Type.MAT4;
typeMap.isampler1D = Type.SAMPLER1D;
typeMap.usampler1D = Type.SAMPLER1D;
typeMap.sampler1DShadow = Type.SAMPLER1D;
typeMap.isampler1DArray = Type.SAMPLER1D_ARRAY;
typeMap.usampler1DArray = Type.SAMPLER1D_ARRAY;
typeMap.sampler1DArrayShadow = Type.SAMPLER1D_ARRAY;
typeMap.isampler2D = Type.SAMPLER2D;
typeMap.usampler2D = Type.SAMPLER2D;
typeMap.sampler2DShadow = Type.SAMPLER2D;
typeMap.isampler2DArray = Type.SAMPLER2D_ARRAY;
typeMap.usampler2DArray = Type.SAMPLER2D_ARRAY;
typeMap.sampler2DArrayShadow = Type.SAMPLER2D_ARRAY;
typeMap.isampler3D = Type.SAMPLER3D;
typeMap.usampler3D = Type.SAMPLER3D;
typeMap.isamplerCube = Type.SAMPLER_CUBE;
typeMap.usamplerCube = Type.SAMPLER_CUBE;
typeMap.samplerCubeShadow = Type.SAMPLER_CUBE;
typeMap.iimage2D = Type.IMAGE2D;
typeMap.uimage2D = Type.IMAGE2D;
typeMap.usubpassInput = Type.SUBPASS_INPUT;
typeMap.isubpassInput = Type.SUBPASS_INPUT;

const isSampler = (type) => type >= Type.SAMPLER1D;
const isPaddedMatrix = (type) => type >= Type.MAT2 && type < Type.MAT4;

const formatMap = {
    bool: Format.R8,
    bvec2: Format.RG8,
    bvec3: Format.RGB8,
    bvec4: Format.RGBA8,
    int: Format.R32I,
    ivec2: Format.RG32I,
    ivec3: Format.RGB32I,
    ivec4: Format.RGBA32I,
    uint: Format.R32UI,
    uvec2: Format.RG32UI,
    uvec3: Format.RGB32UI,
    uvec4: Format.RGBA32UI,
    float: Format.R32F,
    vec2: Format.RG32F,
    vec3: Format.RGB32F,
    vec4: Format.RGBA32F,
    int8_t: Format.R8I,
    i8vec2: Format.RG8I,
    i8vec3: Format.RGB8I,
    i8vec4: Format.RGBA8I,
    uint8_t: Format.R8UI,
    u8vec2: Format.RG8UI,
    u8vec3: Format.RGB8UI,
    u8vec4: Format.RGBA8UI,
    int16_t: Format.R16I,
    i16vec2: Format.RG16I,
    i16vec3: Format.RGB16I,
    i16vec4: Format.RGBA16I,
    uint16_t: Format.R16UI,
    u16vec2: Format.RG16UI,
    u16vec3: Format.RGB16UI,
    u16vec4: Format.RGBA16UI,
    float16_t: Format.R16F,
    f16vec2: Format.RG16F,
    f16vec3: Format.RGB16F,
    f16vec4: Format.RGBA16F,
    // no suitable conversions:
    mat2: Format.RGBA32F,
    mat3: Format.RGBA32F,
    mat4: Format.RGBA32F,
    mat2x2: Format.RGBA32F,
    mat3x3: Format.RGBA32F,
    mat4x4: Format.RGBA32F,
    mat2x3: Format.RGBA32F,
    mat2x4: Format.RGBA32F,
    mat3x2: Format.RGBA32F,
    mat3x4: Format.RGBA32F,
    mat4x2: Format.RGBA32F,
    mat4x3: Format.RGBA32F,
};
const getFormat = (name: string) => Format[name.toUpperCase()];
const getShaderStage = (name: string) => ShaderStageFlagBit[name.toUpperCase()];
const getDescriptorType = (name: string) => DescriptorType[name.toUpperCase()];
const isNormalized = (format: string) => {
    const type = FormatInfos[format] && FormatInfos[format].type;
    return type === FormatType.UNORM || type === FormatType.SNORM;
};
const getMemoryAccessFlag = (access: string) => {
    if (access === 'writeonly') { return MemoryAccessBit.WRITE_ONLY; }
    if (access === 'readonly') { return MemoryAccessBit.READ_ONLY; }
    return MemoryAccessBit.READ_WRITE;
};

const passParams = {
    // color mask
    NONE: ColorMask.NONE,
    R: ColorMask.R,
    G: ColorMask.G,
    B: ColorMask.B,
    A: ColorMask.A,
    RG: ColorMask.R | ColorMask.G,
    RB: ColorMask.R | ColorMask.B,
    RA: ColorMask.R | ColorMask.A,
    GB: ColorMask.G | ColorMask.B,
    GA: ColorMask.G | ColorMask.A,
    BA: ColorMask.B | ColorMask.A,
    RGB: ColorMask.R | ColorMask.G | ColorMask.B,
    RGA: ColorMask.R | ColorMask.G | ColorMask.A,
    RBA: ColorMask.R | ColorMask.B | ColorMask.A,
    GBA: ColorMask.G | ColorMask.B | ColorMask.A,
    ALL: ColorMask.ALL,
    // blend operation
    ADD: BlendOp.ADD,
    SUB: BlendOp.SUB,
    REV_SUB: BlendOp.REV_SUB,
    MIN: BlendOp.MIN,
    MAX: BlendOp.MAX,
    // blend factor
    ZERO: BlendFactor.ZERO,
    ONE: BlendFactor.ONE,
    SRC_ALPHA: BlendFactor.SRC_ALPHA,
    DST_ALPHA: BlendFactor.DST_ALPHA,
    ONE_MINUS_SRC_ALPHA: BlendFactor.ONE_MINUS_SRC_ALPHA,
    ONE_MINUS_DST_ALPHA: BlendFactor.ONE_MINUS_DST_ALPHA,
    SRC_COLOR: BlendFactor.SRC_COLOR,
    DST_COLOR: BlendFactor.DST_COLOR,
    ONE_MINUS_SRC_COLOR: BlendFactor.ONE_MINUS_SRC_COLOR,
    ONE_MINUS_DST_COLOR: BlendFactor.ONE_MINUS_DST_COLOR,
    SRC_ALPHA_SATURATE: BlendFactor.SRC_ALPHA_SATURATE,
    CONSTANT_COLOR: BlendFactor.CONSTANT_COLOR,
    ONE_MINUS_CONSTANT_COLOR: BlendFactor.ONE_MINUS_CONSTANT_COLOR,
    CONSTANT_ALPHA: BlendFactor.CONSTANT_ALPHA,
    ONE_MINUS_CONSTANT_ALPHA: BlendFactor.ONE_MINUS_CONSTANT_ALPHA,
    // stencil operation
    // ZERO: StencilOp.ZERO, // duplicate, safely removed because enum value is(and always will be) the same
    KEEP: StencilOp.KEEP,
    REPLACE: StencilOp.REPLACE,
    INCR: StencilOp.INCR,
    DECR: StencilOp.DECR,
    INVERT: StencilOp.INVERT,
    INCR_WRAP: StencilOp.INCR_WRAP,
    DECR_WRAP: StencilOp.DECR_WRAP,
    // comparison function
    NEVER: ComparisonFunc.NEVER,
    LESS: ComparisonFunc.LESS,
    EQUAL: ComparisonFunc.EQUAL,
    LESS_EQUAL: ComparisonFunc.LESS_EQUAL,
    GREATER: ComparisonFunc.GREATER,
    NOT_EQUAL: ComparisonFunc.NOT_EQUAL,
    GREATER_EQUAL: ComparisonFunc.GREATER_EQUAL,
    ALWAYS: ComparisonFunc.ALWAYS,
    // cull mode
    // NONE: CullMode.NONE, // duplicate, safely removed because enum value is(and always will be) the same
    FRONT: CullMode.FRONT,
    BACK: CullMode.BACK,
    // shade mode
    GOURAND: ShadeModel.GOURAND,
    FLAT: ShadeModel.FLAT,
    // polygon mode
    FILL: PolygonMode.FILL,
    LINE: PolygonMode.LINE,
    POINT: PolygonMode.POINT,
    // primitive mode
    POINT_LIST: PrimitiveMode.POINT_LIST,
    LINE_LIST: PrimitiveMode.LINE_LIST,
    LINE_STRIP: PrimitiveMode.LINE_STRIP,
    LINE_LOOP: PrimitiveMode.LINE_LOOP,
    TRIANGLE_LIST: PrimitiveMode.TRIANGLE_LIST,
    TRIANGLE_STRIP: PrimitiveMode.TRIANGLE_STRIP,
    TRIANGLE_FAN: PrimitiveMode.TRIANGLE_FAN,
    LINE_LIST_ADJACENCY: PrimitiveMode.LINE_LIST_ADJACENCY,
    LINE_STRIP_ADJACENCY: PrimitiveMode.LINE_STRIP_ADJACENCY,
    TRIANGLE_LIST_ADJACENCY: PrimitiveMode.TRIANGLE_LIST_ADJACENCY,
    TRIANGLE_STRIP_ADJACENCY: PrimitiveMode.TRIANGLE_STRIP_ADJACENCY,
    TRIANGLE_PATCH_ADJACENCY: PrimitiveMode.TRIANGLE_PATCH_ADJACENCY,
    QUAD_PATCH_LIST: PrimitiveMode.QUAD_PATCH_LIST,
    ISO_LINE_LIST: PrimitiveMode.ISO_LINE_LIST,

    // POINT: Filter.POINT, // duplicate, safely removed because enum value is(and always will be) the same
    LINEAR: Filter.LINEAR,
    ANISOTROPIC: Filter.ANISOTROPIC,

    WRAP: Address.WRAP,
    MIRROR: Address.MIRROR,
    CLAMP: Address.CLAMP,
    BORDER: Address.BORDER,

    LINE_WIDTH: DynamicStateFlagBit.LINE_WIDTH,
    DEPTH_BIAS: DynamicStateFlagBit.DEPTH_BIAS,
    BLEND_CONSTANTS: DynamicStateFlagBit.BLEND_CONSTANTS,
    DEPTH_BOUNDS: DynamicStateFlagBit.DEPTH_BOUNDS,
    STENCIL_WRITE_MASK: DynamicStateFlagBit.STENCIL_WRITE_MASK,
    STENCIL_COMPARE_MASK: DynamicStateFlagBit.STENCIL_COMPARE_MASK,

    TRUE: true,
    FALSE: false,
};
Object.assign(passParams, RenderPassStage);

// for structural type checking
// an 'any' key will check against all elements defined in that object
// a key start with '$' means its essential, and can't be undefined
const effectStructure = {
    $techniques: [
        {
            $passes: [
                {
                    depthStencilState: {},
                    rasterizerState: {},
                    blendState: { targets: [{}] },
                    properties: { any: { sampler: {}, editor: {} } },
                    migrations: { properties: { any: {} }, macros: { any: {} } },
                    embeddedMacros: {},
                },
            ],
        },
    ],
};

export {
    murmurhash2_32_gc,
    Sampler,
    SamplerInfo,
    effectStructure,
    isSampler,
    typeMap,
    formatMap,
    getFormat,
    getShaderStage,
    getDescriptorType,
    isNormalized,
    isPaddedMatrix,
    getMemoryAccessFlag,
    passParams,
    SetIndex,
    RenderPriority,
    GetTypeSize,
};
