use miden::protocol::active_note
use miden::protocol::note
use miden::protocol::output_note
use miden::standards::faucets::network_fungible->network_faucet

# CONSTANTS
# =================================================================================================

const MINT_NOTE_NUM_INPUTS_PRIVATE=12
const MINT_NOTE_MIN_NUM_INPUTS_PUBLIC=16

const OUTPUT_NOTE_TYPE_PUBLIC=1
const OUTPUT_NOTE_TYPE_PRIVATE=2

# Memory Addresses of MINT note inputs
# The attachment is at the same memory address for both private and public inputs.
const ATTACHMENT_KIND_ADDRESS=2
const ATTACHMENT_SCHEME_ADDRESS=3
const ATTACHMENT_ADDRESS=4
const OUTPUT_PUBLIC_NOTE_INPUTS_ADDR=16

# ERRORS
# =================================================================================================

const ERR_MINT_WRONG_NUMBER_OF_INPUTS="MINT script expects exactly 12 inputs for private or 16+ inputs for public output notes"

#! Network Faucet MINT script: mints assets by calling the network faucet's distribute function.
#! This note is intended to be executed against a network fungible faucet account.
#!
#! Requires that the account exposes:
#! - miden::standards::faucets::network_fungible::distribute procedure.
#!
#! Inputs:  [ARGS, pad(12)]
#! Outputs: [pad(16)]
#!
#! Note inputs support two modes. Depending on the number of note inputs,
#! a private or public note is created on consumption of the MINT note:
#!
#! Private mode (12 inputs) - creates a private note:
#! - tag: Note tag for the output note
#! - amount: The amount to mint
#! - attachment_scheme: The user-defined type of the attachment.
#! - attachment_kind: The attachment kind of the attachment.
#! - ATTACHMENT: The attachment to be set.
#! - RECIPIENT: The recipient digest (4 elements)
#!
#! Public mode (16+ inputs) - creates a public note with variable-length inputs:
#! - tag: Note tag for the output note
#! - amount: The amount to mint
#! - attachment_scheme: The user-defined type of the attachment.
#! - attachment_kind: The attachment kind of the attachment.
#! - ATTACHMENT: The attachment to be set.
#! - SCRIPT_ROOT: Script root of the output note (4 elements)
#! - SERIAL_NUM: Serial number of the output note (4 elements)
#! - [INPUTS]: Variable-length inputs for the output note (Vec<Felt>)
#!             The number of output note inputs = num_mint_note_inputs - 16
#!
#! Panics if:
#! - account does not expose distribute procedure.
#! - the number of inputs is not exactly 12 for private or less than 16 for public output notes.
pub proc main
    dropw
    # => [pad(16)]
    # Load note inputs into memory starting at address 0
    push.0 exec.active_note::get_inputs
    # => [total_inputs, inputs_ptr, pad(16)]

    dup
    # => [num_inputs, num_inputs, inputs_ptr, pad(16)]

    u32assert2.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS
    u32gte.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC
    # => [is_public_output_note, total_inputs, inputs_ptr, pad(16)]

    if.true
        # public output note creation
        # => [total_inputs, inputs_ptr, pad(16)]

        movdn.9 drop
        # => [EMPTY_WORD, EMPTY_WORD, total_inputs, pad(8)]

        mem_loadw_be.8
        # => [SCRIPT_ROOT, EMPTY_WORD, total_inputs, pad(8)]

        swapw mem_loadw_be.12
        # => [SERIAL_NUM, SCRIPT_ROOT, total_inputs, pad(8)]

        # compute variable length note inputs for the output note
        movup.8 sub.MINT_NOTE_MIN_NUM_INPUTS_PUBLIC
        # => [num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)]

        push.OUTPUT_PUBLIC_NOTE_INPUTS_ADDR
        # => [inputs_ptr, num_output_note_inputs, SERIAL_NUM, SCRIPT_ROOT, pad(8)]

        exec.note::build_recipient
        # => [RECIPIENT, pad(12)]

        # push note_type, and load tag and amount
        push.OUTPUT_NOTE_TYPE_PUBLIC
        mem_load.0 mem_load.1
        # => [amount, tag, note_type, RECIPIENT, pad(12)]
    else
        # private output note creation

        eq.MINT_NOTE_NUM_INPUTS_PRIVATE assert.err=ERR_MINT_WRONG_NUMBER_OF_INPUTS drop
        # => [inputs_ptr, pad(16)]

        drop
        # => [pad(16)]

        mem_loadw_be.8
        # => [RECIPIENT, pad(12)]

        # push note_type, and load tag and amount
        push.OUTPUT_NOTE_TYPE_PRIVATE
        mem_load.0 mem_load.1
        # => [amount, tag, note_type, RECIPIENT, pad(12)]
    end
    # => [amount, tag, note_type, RECIPIENT, pad(12)]

    # distribute expects 9 pad elements, returns 15 and 12 are provided here.
    # so the total number of pads after calling is 12 + (15-9) = 18
    call.network_faucet::distribute
    # => [note_idx, pad(18))]

    padw mem_loadw_be.ATTACHMENT_ADDRESS
    # => [ATTACHMENT, note_idx, pad(18))]

    mem_load.ATTACHMENT_KIND_ADDRESS
    mem_load.ATTACHMENT_SCHEME_ADDRESS
    movup.6
    # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT, pad(18))]

    exec.output_note::set_attachment
    # => [pad(18))]

    drop drop
    # => [pad(16)]
end
