// Copyright Coase, Inc 2019

type card_pattern_id is nat
type card_pattern is record [
  coefficient : tez ;
  quantity : nat ;
]

type card_patterns is map(card_pattern_id , card_pattern)

type card_id is nat
type card is record [
  card_owner : address ;
  card_pattern : card_pattern_id ;
]
type cards is map(card_id , card)

type storage_type is record [
  cards : cards ;
  card_patterns : card_patterns ;
  next_id : nat ;
]

type action_buy_single is record [
  card_to_buy : card_pattern_id ;
]
type action_sell_single is record [
  card_to_sell : card_id ;
]
type action_transfer_single is record [
  card_to_transfer : card_id ;
  destination : address ;
]

type action is
| Buy_single of action_buy_single
| Sell_single of action_sell_single
| Transfer_single of action_transfer_single

function transfer_single(const action : action_transfer_single ; const s : storage_type) : (list(operation) * storage_type) is
  begin
    const cards : cards = s.cards ;
    const card : card = get_force(action.card_to_transfer , cards) ;
    if (card.card_owner =/= source) then failwith ("This card doesn't belong to you") else skip ;
    card.card_owner := action.destination ;
    cards[action.card_to_transfer] := card ;
    s.cards := cards ;
    const operations : list(operation) = nil ;
  end with (operations , s) ;

function sell_single(const action : action_sell_single ; const s : storage_type) : (list(operation) * storage_type) is
  begin
    const card : card = get_force(action.card_to_sell , s.cards) ;
    if (card.card_owner =/= source) then failwith ("This card doesn't belong to you") else skip ;
    const card_pattern : card_pattern = get_force(card.card_pattern , s.card_patterns) ;
    card_pattern.quantity := abs(card_pattern.quantity - 1n);
    const card_patterns : card_patterns = s.card_patterns ;
    card_patterns[card.card_pattern] := card_pattern ;
    s.card_patterns := card_patterns ;
    const cards : cards = s.cards ;
    remove action.card_to_sell from map cards ;
    s.cards := cards ;
    const price : tez = card_pattern.coefficient * card_pattern.quantity ;
    const receiver : contract(unit) = get_contract(source) ;
    const op : operation = transaction(unit , price , receiver) ;
    const operations : list(operation) = list op end ;
  end with (operations , s)

function buy_single(const action : action_buy_single ; const s : storage_type) : (list(operation) * storage_type) is
  begin
    // Check funds
    const card_pattern : card_pattern = get_force(action.card_to_buy , s.card_patterns) ;
    const price : tez = card_pattern.coefficient * (card_pattern.quantity + 1n) ;
    if (price > amount) then failwith ("Not enough money") else skip ;
    // Administrative procedure
    const operations : list(operation) = nil ;
    // Increase quantity
    card_pattern.quantity := card_pattern.quantity + 1n ;
    const card_patterns : card_patterns = s.card_patterns ;
    card_patterns[action.card_to_buy] := card_pattern ;
    s.card_patterns := card_patterns ;
    // Add card
    const cards : cards = s.cards ;
    cards[s.next_id] := record
      card_owner = source ;
      card_pattern = action.card_to_buy ;
    end ;
    s.cards := cards ;
    s.next_id := s.next_id + 1n ;
  end with (operations , s)

function main(const action : action ; const s : storage_type) : (list(operation) * storage_type) is
  block {skip} with
  case action of
  | Buy_single (bs) -> buy_single (bs , s)
  | Sell_single (as) -> sell_single (as , s)
  | Transfer_single (at) -> transfer_single (at , s)
  end