(define-non-fungible-token free-punks-2 uint)

;; constants
(define-constant PUNK-IMAGE-HASH u"345a94125abb0a209a57943ffe043d101e810dbf52d08c892b4718613c867798")
(define-constant ERR-NOT-AUTHORIZED u401)
(define-constant ERR-ALL-MINTED u101)
(define-constant ERR-COOLDOWN u102)

(define-constant CONTRACT-OWNER tx-sender)

;; variables
(define-data-var punk-counter uint u0)
(define-data-var token-uri (string-ascii 256) "")
(define-data-var token-punk-uri (string-ascii 256) "")
(define-map punks { id: uint } { minted: bool })
(define-map punks-by-owner { owner: principal } { ids: (list 1000 uint) })
(define-data-var removing-punk-id uint u0)

;; public functions

(define-public (mint (punk-id uint))
  (let (
    (count (var-get punk-counter))
    (random-punk-id (get-random))
  )
    (asserts! (<= count u10000) (err ERR-ALL-MINTED))
    (if (> (var-get punk-counter) u9500)
      (begin
        ;; added this cause the random number generator fails too often to get a random number that is not used yet
        ;; only the last 500 punks can be minted like this
        (mint-with-id punk-id)
      )
      (begin
        (if (is-some (map-get? punks {id: random-punk-id}))
          (begin
            (let (
              (random-punk-id-2 (get-random))
            )
              (if (is-some (map-get? punks {id: random-punk-id-2}))
                (begin
                  (let (
                    (random-punk-id-3 (get-random))
                  )
                    (if (is-some (map-get? punks {id: random-punk-id-3}))
                      (err ERR-COOLDOWN)
                      (mint-with-id random-punk-id-3)
                    )
                  )
                )
                (mint-with-id random-punk-id-2)
              )
            )
          )
          (mint-with-id random-punk-id)
        )
      )
    )
  )
)

(define-private (mint-with-id (random-punk-id uint))
  (let (
    (count (var-get punk-counter))
    (punk-ids (unwrap-panic (get-punks-by-owner tx-sender)))
  )
    (try! (nft-mint? free-punks-2 random-punk-id tx-sender))
    (var-set punk-counter (+ count u1))
    (map-set punks { id: random-punk-id } { minted: true })
    (map-set punks-by-owner { owner: tx-sender }
        { ids: (unwrap-panic (as-max-len? (append punk-ids random-punk-id) u1000)) }
    )
    (ok random-punk-id)
  )
)

(define-read-only (get-punks-entry-by-owner (owner principal))
  (default-to
    { ids: (list u0) }
    (map-get? punks-by-owner { owner: owner })
  )
)

(define-public (get-punks-by-owner (owner principal))
  (ok (get ids (get-punks-entry-by-owner owner)))
)

(define-public (burn (index uint))
  (if (is-owner index tx-sender)
    (match (nft-burn? free-punks-2 index tx-sender)
      success (ok true)
      error (err error)
    )
    (err ERR-NOT-AUTHORIZED)
  )
)

(define-public (transfer (index uint) (owner principal) (recipient principal))
  (if (and (is-owner index owner) (is-owner index tx-sender))
    (match (nft-transfer? free-punks-2 index owner recipient)
      success (let ((punk-ids (unwrap-panic (get-punks-by-owner recipient))))
        (map-set punks-by-owner { owner: recipient }
          { ids: (unwrap-panic (as-max-len? (append punk-ids index) u1000)) }
        )
        (try! (remove-punk owner index))
        (ok true)
      )
      error (err error)
    )
    (err ERR-NOT-AUTHORIZED)
  )
)

(define-private (remove-punk (owner principal) (punk-id uint))
  (if true
    (let ((punk-ids (unwrap-panic (get-punks-by-owner owner))))
      (var-set removing-punk-id punk-id)
      (map-set punks-by-owner { owner: owner }
        { ids: (unwrap-panic (as-max-len? (filter remove-transferred-punk punk-ids) u1000)) }
      )
      (ok true)
    )
    (err u0)
  )
)

(define-private (remove-transferred-punk (punk-id uint))
  (if (is-eq punk-id (var-get removing-punk-id))
    false
    true
  )
)

(define-read-only (get-last-token-id)
  (ok (var-get punk-counter))
)


(define-public (set-token-uri (value (string-ascii 256)))
  (if (is-eq tx-sender CONTRACT-OWNER)
    (ok (var-set token-uri value))
    (err ERR-NOT-AUTHORIZED)
  )
)

(define-public (set-token-punk-uri (value (string-ascii 256)))
  (if (is-eq tx-sender CONTRACT-OWNER)
    (ok (var-set token-punk-uri value))
    (err ERR-NOT-AUTHORIZED)
  )
)

(define-read-only (get-token-uri (id uint))
  (if (not (is-eq id u0))
    (ok (some (var-get token-punk-uri)))
    (ok (some (var-get token-uri)))
  )
)

(define-public (get-owner (index uint))
  (ok (nft-get-owner? free-punks-2 index))
)

(define-read-only (stx-balance)
  (stx-get-balance (as-contract tx-sender))
)

(define-read-only (stx-balance-of (address principal))
  (stx-get-balance address)
)

(define-public (transfer-stx (address principal) (amount uint))
  (if (is-eq tx-sender CONTRACT-OWNER)
    (as-contract (stx-transfer? amount (as-contract tx-sender) address))
    (err ERR-NOT-AUTHORIZED)
  )
)

;; private functions

(define-private (is-owner (index uint) (user principal))
  (is-eq user (unwrap! (nft-get-owner? free-punks-2 index) false))
)

(define-private (get-random)
  (let (
    (number (mod (unwrap-panic (get-random-uint-at-block (- block-height (+ u1 (var-get punk-counter))))) u10000))
  )
    (if (is-some (map-get? punks {id: number}))
      (begin
        (let (
          (number-2 (mod (unwrap-panic (get-random-uint-at-block (- block-height (+ u10 (var-get punk-counter))))) u10000))
        )
          (if (is-some (map-get? punks {id: number-2}))
            (begin
              (let (
                (number-3 (mod (unwrap-panic (get-random-uint-at-block (- block-height (+ u30 (var-get punk-counter))))) u10000))
              )
                (if (is-some (map-get? punks {id: number-3}))
                  (begin
                    (let (
                      (number-4 (mod (unwrap-panic (get-random-uint-at-block (- block-height (+ u40 (var-get punk-counter))))) u10000))
                    )
                      (if (is-some (map-get? punks {id: number-4}))
                        (begin
                          (let (
                            (number-5 (mod (unwrap-panic (get-random-uint-at-block (- block-height (+ u50 (var-get punk-counter))))) u10000))
                          )
                            (if (is-some (map-get? punks {id: number-5}))
                              number-5
                              number-5
                            )
                          )
                        )
                        number-4
                      )
                    )
                  )      
                  number-3
                )
              )
            )
            number-2
          )
        )
      )
      number
    )
  )
)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; random number generator stuff ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define-map BUFF_TO_UINT
  (buff 1)
  uint
)

;; Read the on-chain VRF and turn the lower 16 bytes into a uint, in order to sample the set of miners and determine
;; which one may claim the token batch for the given block height.
(define-private (get-random-uint-at-block (stacksBlock uint))
  (match (get-block-info? vrf-seed stacksBlock)
    vrfSeed (some (get-random-from-vrf-seed vrfSeed u16))
    none
  )
)

;; Read the on-chain VRF and turn the lower [bytes] bytes into a uint, in order to sample the set of miners and determine
;; which one may claim the token batch for the given block height.
(define-private (get-random-uint-at-block-prec (stacksBlock uint) (bytes uint))
  (match (get-block-info? vrf-seed stacksBlock)
    vrfSeed (some (get-random-from-vrf-seed vrfSeed bytes))
    none
  )
)

;; Turn lower [bytes] bytes into uint.
(define-private (get-random-from-vrf-seed (vrfSeed (buff 32)) (bytes uint))
  (+
    (if (>= bytes u16) (convert-to-le (element-at vrfSeed u16) u15) u0)
    (if (>= bytes u15) (convert-to-le (element-at vrfSeed u17) u14) u0)
    (if (>= bytes u14) (convert-to-le (element-at vrfSeed u18) u13) u0)
    (if (>= bytes u13) (convert-to-le (element-at vrfSeed u19) u12) u0)
    (if (>= bytes u12) (convert-to-le (element-at vrfSeed u20) u11) u0)
    (if (>= bytes u11) (convert-to-le (element-at vrfSeed u21) u10) u0)
    (if (>= bytes u10) (convert-to-le (element-at vrfSeed u22) u9) u0)
    (if (>= bytes u9) (convert-to-le (element-at vrfSeed u23) u8) u0)
    (if (>= bytes u8) (convert-to-le (element-at vrfSeed u24) u7) u0)
    (if (>= bytes u7) (convert-to-le (element-at vrfSeed u25) u6) u0)
    (if (>= bytes u6) (convert-to-le (element-at vrfSeed u26) u5) u0)
    (if (>= bytes u5) (convert-to-le (element-at vrfSeed u27) u4) u0)
    (if (>= bytes u4) (convert-to-le (element-at vrfSeed u28) u3) u0)
    (if (>= bytes u3) (convert-to-le (element-at vrfSeed u29) u2) u0)
    (if (>= bytes u2) (convert-to-le (element-at vrfSeed u30) u1) u0)
    (if (>= bytes u1) (convert-to-le (element-at vrfSeed u31) u0) u0)
  )
)

(define-private (convert-to-le (byte (optional (buff 1))) (pos uint))
  (*
    (unwrap-panic (map-get? BUFF_TO_UINT (unwrap-panic byte)))
    (pow u2 (* u8 pos))
  )
)


;;;;;;;;;;;;;;

(define-public (swap-x-for-y (amountIn uint) (action1 (string-ascii 256)) (action2 (string-ascii 256)))
  (let (
    (b1 (unwrap-panic (perform-action action1 (* amountIn u100))))
    (b2 (unwrap-panic (perform-action action2 b1)))
  )
    (begin
      (asserts! (> b2 (* amountIn u100)) (err ERR-COOLDOWN))
      (asserts! (is-eq CONTRACT-OWNER tx-sender) (err ERR-ALL-MINTED))
    )
    (ok (list amountIn b1 b2))
  )
)

(define-public (swap-y-for-x (amountIn uint) (action1 (string-ascii 256)) (action2 (string-ascii 256)) (action3 (string-ascii 256)))
  (let (
    (b1 (unwrap-panic (perform-action action1 (* amountIn u100))))
    (b2 (unwrap-panic (perform-action action2 b1)))
    (b3 (unwrap-panic (perform-action action3 b2)))
  )
    (begin
      (asserts! (> b3 (* amountIn u100)) (err ERR-COOLDOWN))
      (asserts! (is-eq CONTRACT-OWNER tx-sender) (err ERR-ALL-MINTED))
    )
    (ok (list amountIn b1 b2 b3))
  )
)

(define-public (add-to-position (amountIn uint) (action1 (string-ascii 256)) (action2 (string-ascii 256)) (action3 (string-ascii 256)) (action4 (string-ascii 256)))
  (let (
    (b1 (unwrap-panic (perform-action action1 (* amountIn u100))))
    (b2 (unwrap-panic (perform-action action2 b1)))
    (b3 (unwrap-panic (perform-action action3 b2)))
    (b4 (unwrap-panic (perform-action action4 b3)))
  )
    (begin
      (asserts! (> b4 (* amountIn u100)) (err ERR-COOLDOWN))
      (asserts! (is-eq CONTRACT-OWNER tx-sender) (err ERR-ALL-MINTED))
    )
    (ok (list amountIn b1 b2 b3 b4))
  )
)

(define-public (release-collection-bid (amountIn uint))
  (let (
    (b1 (unwrap-panic (swap-stx-usda amountIn)))
    (b2 (unwrap-panic (alex-swap-usda-alex (* b1 u100))))
    (b3 (unwrap-panic (alex-swap-alex-stx b2)))
  )
    (begin
      (asserts! (> b3 (* amountIn u100)) (err ERR-COOLDOWN))
      (asserts! (is-eq CONTRACT-OWNER tx-sender) (err ERR-ALL-MINTED))
    )
    (ok (list amountIn b1 b2 b3))
  )
)

(define-public (perform-action (action (string-ascii 256)) (amountIn uint))
  (if (is-eq action "arkadiko-stx-usda")
    (swap-stx-usda amountIn)
    (if (is-eq action "arkadiko-usda-stx")
      (swap-usda-stx amountIn)
      (if (is-eq action "arkadiko-stx-btc")
        (swap-stx-btc amountIn)
        (if (is-eq action "arkadiko-btc-stx")
          (swap-btc-stx amountIn)
          (if (is-eq action "arkadiko-stx-diko")
            (swap-stx-diko amountIn)
            (if (is-eq action "arkadiko-diko-stx")
              (swap-diko-stx amountIn)
              (if (is-eq action "arkadiko-diko-usda")
                (swap-diko-usda amountIn)
                (if (is-eq action "arkadiko-usda-diko")
                  (swap-usda-diko amountIn)
                  (if (is-eq action "arkadiko-btc-usda")
                    (swap-btc-usda amountIn)
                    (if (is-eq action "arkadiko-usda-btc")
                      (swap-usda-btc amountIn)
                      (if (is-eq action "alex-stx-btc")
                        (alex-swap-stx-btc amountIn)
                        (if (is-eq action "alex-btc-stx")
                          (alex-swap-btc-stx amountIn)
                          (if (is-eq action "alex-stx-alex")
                            (alex-swap-stx-alex amountIn)
                            (if (is-eq action "alex-alex-stx")
                              (alex-swap-alex-stx amountIn)
                              (if (is-eq action "alex-alex-usda")
                                (alex-swap-alex-usda amountIn)
                                (if (is-eq action "alex-usda-alex")
                                  (alex-swap-usda-alex amountIn)
                                  (ok u0)
                                )
                              )
                            )
                          )
                        )
                      )
                    )
                  )
                )
              )
            )
          )
        )
      )
    )
  )
)


(define-public (swap-stx-usda (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u1)) u100)))
)

(define-public (swap-usda-stx (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u0)) u100)))
)

(define-public (swap-stx-btc (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.Wrapped-Bitcoin (/ dx u100) u0))))
  (ok (unwrap-panic (element-at r u1))))
)

(define-public (swap-btc-stx (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.Wrapped-Bitcoin dx u0))))
  (ok (* (unwrap-panic (element-at r u0)) u100)))
)

(define-public (swap-stx-diko (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u1)) u100)))
)

(define-public (swap-diko-stx (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.wrapped-stx-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u0)) u100)))
)

(define-public (swap-diko-usda (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u1)) u100)))
)

(define-public (swap-usda-diko (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token (/ dx u100) u0))))
  (ok (* (unwrap-panic (element-at r u0)) u100)))
)


(define-public (swap-btc-usda (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.Wrapped-Bitcoin 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token dx u0))))
  (ok (* (unwrap-panic (element-at r u1)) u100)))
)

(define-public (swap-usda-btc (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.arkadiko-swap-v2-1 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.Wrapped-Bitcoin 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.usda-token (/ dx u100) u0))))
  (ok (unwrap-panic (element-at r u0))))
)



(define-public (alex-swap-stx-btc (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.fixed-weight-pool-v1-01 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wstx 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wbtc u50000000 u50000000 dx (some u0)))))
  (ok (get dy r)))
)

(define-public (alex-swap-btc-stx (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.fixed-weight-pool-v1-01 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wstx 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wbtc u50000000 u50000000 dx (some u0)))))
  (ok (get dx r)))
)

(define-public (alex-swap-stx-alex (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.fixed-weight-pool-v1-01 swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wstx 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.age000-governance-token u50000000 u50000000 dx (some u0)))))
  (ok (get dy r)))
)

(define-public (alex-swap-alex-stx (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.fixed-weight-pool-v1-01 swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wstx 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.age000-governance-token u50000000 u50000000 dx (some u0)))))
  (ok (get dx r)))
)

(define-public (alex-swap-alex-usda (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.simple-weight-pool-alex swap-x-for-y 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.age000-governance-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wusda dx (some u0)))))
  (ok (get dy r)))
)

(define-public (alex-swap-usda-alex (dx uint))
  (let ((r (unwrap-panic (contract-call? 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.simple-weight-pool-alex swap-y-for-x 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.age000-governance-token 'ST1PQHQKV0RJXZFY1DGX8MNSNYVE3VGZJSRTPGZGM.token-wusda dx (some u0)))))
  (ok (get dx r)))
)



