use miden::protocol::active_note
use miden::protocol::output_note
use miden::standards::wallets::basic->wallet

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

const SWAP_NOTE_INPUTS_NUMBER=16

const PAYBACK_NOTE_TYPE_ADDRESS=0
const PAYBACK_NOTE_TAG_ADDRESS=1
const ATTACHMENT_KIND_ADDRESS=2
const ATTACHMENT_SCHEME_ADDRESS=3
const ATTACHMENT_ADDRESS=4
const REQUESTED_ASSET_ADDRESS=8
const PAYBACK_RECIPIENT_ADDRESS=12

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

const ERR_SWAP_WRONG_NUMBER_OF_INPUTS="SWAP script expects exactly 16 note inputs"

const ERR_SWAP_WRONG_NUMBER_OF_ASSETS="SWAP script requires exactly 1 note asset"

#! Swap script: adds an asset from the note into consumers account and
#! creates a note consumable by note issuer containing requested ASSET.
#!
#! Requires that the account exposes:
#! - miden::standards::wallets::basic::receive_asset procedure.
#! - miden::standards::wallets::basic::move_asset_to_note procedure.
#!
#! Inputs:  [ARGS]
#! Outputs: []
#!
#! Note inputs are assumed to be as follows:
#! - payback_note_type
#! - payback_note_tag
#! - attachment_kind
#! - attachment_scheme
#! - ATTACHMENT
#! - REQUESTED_ASSET
#! - PAYBACK_RECIPIENT
#!
#! Panics if:
#! - account does not expose miden::standards::wallets::basic::receive_asset procedure.
#! - account does not expose miden::standards::wallets::basic::move_asset_to_note procedure.
#! - account vault does not contain the requested asset.
#! - adding a fungible asset would result in amount overflow, i.e., the total amount would be
#!   greater than 2^63.
#! - the attachment kind or scheme does not fit into a u32.
#! - the attachment kind is an unknown variant.
pub proc main
    # dropping note args
    dropw
    # => []

    # --- create a payback note with the requested asset ----------------

    # store note inputs into memory starting at address 0
    push.0 exec.active_note::get_inputs
    # => [num_inputs, inputs_ptr]

    # check number of inputs
    eq.SWAP_NOTE_INPUTS_NUMBER assert.err=ERR_SWAP_WRONG_NUMBER_OF_INPUTS
    drop
    # => []

    mem_loadw_be.REQUESTED_ASSET_ADDRESS
    # => [REQUESTED_ASSET]

    padw mem_loadw_be.PAYBACK_RECIPIENT_ADDRESS
    # => [PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET]

    # load payback P2ID details
    mem_load.PAYBACK_NOTE_TYPE_ADDRESS
    mem_load.PAYBACK_NOTE_TAG_ADDRESS
    # => [tag, note_type, PAYBACK_NOTE_RECIPIENT, REQUESTED_ASSET]

    # create payback P2ID note
    exec.output_note::create
    # => [note_idx, REQUESTED_ASSET]

    movdn.4
    # => [REQUESTED_ASSET, note_idx]

    # padding stack with 11 zeros
    repeat.11
        push.0
        movdn.5
    end
    # => [REQUESTED_ASSET, note_idx, pad(11)]

    # move asset to the note
    call.wallet::move_asset_to_note
    # => [REQUESTED_ASSET, note_idx, pad(11)]

    dropw
    # => [note_idx, pad(11)]

    mem_loadw_be.ATTACHMENT_ADDRESS
    mem_load.ATTACHMENT_KIND_ADDRESS
    mem_load.ATTACHMENT_SCHEME_ADDRESS
    movup.6
    # => [note_idx, attachment_scheme, attachment_kind, ATTACHMENT]

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

    # --- move assets from the SWAP note into the account -------------------------

    # store the number of note assets to memory starting at address 0
    push.0 exec.active_note::get_assets
    # => [num_assets, ptr, pad(12)]

    # make sure the number of assets is 1
    assert.err=ERR_SWAP_WRONG_NUMBER_OF_ASSETS
    # => [ptr, pad(12)]

    # load the ASSET
    mem_loadw_be
    # => [ASSET, pad(12)]

    # add the ASSET to the account
    call.wallet::receive_asset
    # => [pad(16)]

    # clearing the stack of padded 0s
    repeat.4
        dropw
    end
    # => []
end
