//------------------------------------------------------------------------------
// GB_macrofy_query: construct GB_jit_query function for a kernel
//------------------------------------------------------------------------------

// SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2025, All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

//------------------------------------------------------------------------------

#include "GB.h"
#include "jitifyer/GB_stringify.h"

void GB_macrofy_query
(
    FILE *fp,
    const bool builtin, // true if method is all builtin
    GrB_Monoid monoid,  // monoid for reduce or semiring; NULL otherwise
    GB_Operator op0,    // monoid op, select op, unary op, etc
    GB_Operator op1,    // binaryop for a semring
    GrB_Type type0,
    GrB_Type type1,
    GrB_Type type2,
    uint64_t hash,      // hash code for the kernel
    GB_jit_kcode kcode
)
{

    //--------------------------------------------------------------------------
    // create the function header, and query the version
    //--------------------------------------------------------------------------

    if (kcode >= GB_JIT_CUDA_KERNEL)
    {
        // ensure the query function can be called from a C function
        fprintf (fp, "extern \"C\"\n{\n") ;
    }

    fprintf (fp,
        "GB_JIT_GLOBAL GB_JIT_QUERY_PROTO (GB_jit_query) ;\n") ;

    if (kcode >= GB_JIT_CUDA_KERNEL)
    {
        fprintf (fp, "}\n") ;
    }

    fprintf (fp,
        "GB_JIT_GLOBAL GB_JIT_QUERY_PROTO (GB_jit_query)\n"
        "{\n"
        "    (*hash) = 0x%016" PRIx64 " ;\n"
        "    v [0] = %d ; v [1] = %d ; v [2] = %d ;\n",
            hash,
            GxB_IMPLEMENTATION_MAJOR,
            GxB_IMPLEMENTATION_MINOR,
            GxB_IMPLEMENTATION_SUB) ;

    //--------------------------------------------------------------------------
    // query the operators
    //--------------------------------------------------------------------------

    // create the definition string for op0
    if (builtin || op0 == NULL || op0->defn == NULL)
    { 
        // op0 does not appear, or is builtin
        fprintf (fp, "    defn [0] = NULL ;\n") ;
    }
    else
    { 
        // op0 is user-defined
        fprintf (fp, "    defn [0] = GB_%s_USER_DEFN ;\n", op0->name) ;
    }

    // create the definition string for op1
    if (builtin || op1 == NULL || op1->defn == NULL)
    { 
        // op1 does not appear, or is builtin
        fprintf (fp, "    defn [1] = NULL ;\n") ;
    }
    else if (op0 == op1)
    { 
        // op1 is user-defined, but the same as op0
        fprintf (fp, "    defn [1] = defn [0] ;\n") ;
    }
    else
    { 
        // op1 is user-defined, and differs from op0
        fprintf (fp, "    defn [1] = GB_%s_USER_DEFN ;\n", op1->name) ;
    }

    //--------------------------------------------------------------------------
    // query the three types
    //--------------------------------------------------------------------------

    GrB_Type types [3] ;
    types [0] = type0 ;
    types [1] = type1 ;
    types [2] = type2 ;
    for (int k = 0 ; k <= 2 ; k++)
    {
        GrB_Type type = types [k] ;
        if (builtin || type == NULL || type->defn == NULL)
        { 
            // types [k] does not appear, or is builtin
            fprintf (fp, "    defn [%d] = NULL ;\n", k+2) ;
        }
        else
        { 
            // see if the type definition already appears
            bool is_unique = true ;
            for (int j = 0 ; j < k ; j++)
            {
                if (type == types [j])
                { 
                    is_unique = false ;
                    fprintf (fp, "    defn [%d] = defn [%d] ;\n", k+2, j+2) ;
                    break ;
                }
            }
            if (is_unique)
            { 
                // this type is unique, and user-defined
                fprintf (fp, "    defn [%d] = GB_%s_USER_DEFN ;\n", k+2,
                    type->name) ;
            }
        }
    }

    //--------------------------------------------------------------------------
    // query the monoid identity and terminal values
    //--------------------------------------------------------------------------

    if (monoid != NULL && monoid->hash != 0)
    { 
        // only create the query_monoid method if the monoid is not builtin
        bool has_terminal = (monoid->terminal != NULL) ;
        int zsize = (int) monoid->op->ztype->size ;
        int tsize = (has_terminal) ? zsize : 0 ;
        fprintf (fp,
            "    if (id_memsize != %d || term_memsize != %d) return (false) ;\n"
            "    GB_DECLARE_IDENTITY_CONST (zidentity) ;\n"
            "    if (id == NULL || memcmp (id, &zidentity, %d) != 0) "
                     "return (false) ;\n", zsize, tsize, zsize) ;
        if (has_terminal)
        { 
            fprintf (fp,
            "    GB_DECLARE_TERMINAL_CONST (zterminal) ;\n"
            "    if (term == NULL || memcmp (term, &zterminal, %d) != 0) "
                    "return (false) ;\n", tsize) ;
        }
    }
    fprintf (fp, "    return (true) ;\n}\n") ;
}

