contract AlchemyMarket is AlchemyMinting {

    // Sale order struct
    struct SaleOrder {
        // Asset id to be sold
        uint64 assetId;
        // Sale amount
        uint64 amount;
        // Desired price
        uint128 desiredPrice;
        // Seller
        address seller; 
    }

    // Max number of sale orders of each account 
    uint128 public maxSaleNum = 20;

    // Cut ratio for a transaction
    // Values 0-10,000 map to 0%-100%
    uint256 public trCut = 275;

    // Next sale id
    uint256 public nextSaleId = 1;

    // Sale orders list 
    mapping (uint256 => SaleOrder) public saleOrderList;

    // Sale information of each account
    mapping (address => uint256) public accountToSaleNum;

    // events
    event PutOnSale(address account, uint256 saleId);
    event WithdrawSale(address account, uint256 saleId);
    event ChangeSale(address account, uint256 saleId);
    event BuyInMarket(address buyer, uint256 saleId, uint256 amount);
    event SaleClear(uint256 saleId);

    // functions
    function setTrCut(uint256 newCut) public onlyCOO {
        trCut = newCut;
    }

    // Put asset on sale
    function putOnSale(uint256 assetId, uint256 amount, uint256 price) external whenNotPaused {
        // One account can have no more than maxSaleNum sale orders
        require(accountToSaleNum[msg.sender] < maxSaleNum);

        // check whether zero order asset is to be sold 
        // which is not allowed
        require(assetId > 3 && assetId < 248);
        require(amount > 0 && amount < 256);

        uint256 assetFloor = assetId / 31;
        uint256 assetPos = assetId - 31 * assetFloor;
        bytes32 allAsset = assets[msg.sender][assetFloor];

        bytes32 mask = bytes32(255) << (8 * assetPos); // 0x11111111
        uint256 maskedValue;
        uint256 maskedResult;
        uint256 addAmount = amount << (8 * assetPos);

        // check whether there are enough unpending assets to sell
        maskedValue = uint256(allAsset & mask);
        require(addAmount <= maskedValue);

        // Remove assets to be sold from owner
        maskedResult = maskedValue - addAmount;
        allAsset = ((allAsset ^ mask) & allAsset) | bytes32(maskedResult);

        assets[msg.sender][assetFloor] = allAsset;

        // Put on sale
        SaleOrder memory saleorder = SaleOrder(
            uint64(assetId),
            uint64(amount),
            uint128(price),
            msg.sender
        );

        saleOrderList[nextSaleId] = saleorder;
        nextSaleId += 1;

        accountToSaleNum[msg.sender] += 1;

        // Emit the Approval event
        emit PutOnSale(msg.sender, nextSaleId-1);
    }
  
    // Withdraw an sale order
    function withdrawSale(uint256 saleId) external whenNotPaused {
        // Can only withdraw self's sale order
        require(saleOrderList[saleId].seller == msg.sender);

        uint256 assetId = uint256(saleOrderList[saleId].assetId);
        uint256 assetFloor = assetId / 31;
        uint256 assetPos = assetId - 31 * assetFloor;
        bytes32 allAsset = assets[msg.sender][assetFloor];

        bytes32 mask = bytes32(255) << (8 * assetPos); // 0x11111111
        uint256 maskedValue;
        uint256 maskedResult;
        uint256 addAmount = uint256(saleOrderList[saleId].amount) << (8 * assetPos);

        // check whether this account will have too many assets
        maskedValue = uint256(allAsset & mask);
        require(addAmount + maskedValue < 2**(8 * (assetPos + 1)));

        // Retransfer asset to be sold from owner
        maskedResult = maskedValue + addAmount;
        allAsset = ((allAsset ^ mask) & allAsset) | bytes32(maskedResult);

        assets[msg.sender][assetFloor] = allAsset;

        // Delete sale order
        delete saleOrderList[saleId];

        accountToSaleNum[msg.sender] -= 1;

        // Emit the cancel event
        emit WithdrawSale(msg.sender, saleId);
    }
 
//     // Change sale order
//     function changeSale(uint256 assetId, uint256 amount, uint256 price, uint256 saleId) external whenNotPaused {
//         // Check if msg sender is the seller
//         require(msg.sender == saleOrderList[saleId].seller);
// 
//     }
 
    // Buy assets in market
    function buyInMarket(uint256 saleId, uint256 amount) external payable whenNotPaused {
        address seller = saleOrderList[saleId].seller;
        // Check whether the saleId is a valid sale order
        require(seller != address(0));

        // Check the sender isn't the seller
        require(msg.sender != seller);

        require(saleOrderList[saleId].amount >= uint64(amount));

        // Check whether pay value is enough
        require(msg.value / saleOrderList[saleId].desiredPrice >= amount);

        uint256 totalprice = amount * saleOrderList[saleId].desiredPrice;

        uint64 assetId = saleOrderList[saleId].assetId;

        uint256 assetFloor = assetId / 31;
        uint256 assetPos = assetId - 31 * assetFloor;
        bytes32 allAsset = assets[msg.sender][assetFloor];

        bytes32 mask = bytes32(255) << (8 * assetPos); // 0x11111111
        uint256 maskedValue;
        uint256 maskedResult;
        uint256 addAmount = amount << (8 * assetPos);

        // check whether this account will have too many assets
        maskedValue = uint256(allAsset & mask);
        require(addAmount + maskedValue < 2**(8 * (assetPos + 1)));

        // Transfer assets to buyer
        maskedResult = maskedValue + addAmount;
        allAsset = ((allAsset ^ mask) & allAsset) | bytes32(maskedResult);

        assets[msg.sender][assetFloor] = allAsset;

        saleOrderList[saleId].amount -= uint64(amount);

        // Cut and then send the proceeds to seller
        uint256 sellerProceeds = totalprice - _computeCut(totalprice);

        seller.transfer(sellerProceeds);

        // Emit the buy event
        emit BuyInMarket(msg.sender, saleId, amount);

        // If the sale has complete, clear this order
        if (saleOrderList[saleId].amount == 0) {
            accountToSaleNum[seller] -= 1;
            delete saleOrderList[saleId];

            // Emit the clear event
            emit SaleClear(saleId);
        }
    }

    // Compute the marketCut
    function _computeCut(uint256 _price) internal view returns (uint256) {
        return _price / 10000 * trCut;
    }
}
function putOnSale(uint256 assetId, uint256 amount, uint256 price) external whenNotPaused {
    // One account can have no more than maxSaleNum sale orders
    require(accountToSaleNum[msg.sender] < maxSaleNum);

    // check whether zero order asset is to be sold 
    // which is not allowed
    require(assetId > 3 && assetId < 248);
    require(amount > 0 && amount < 256);

    uint256 assetFloor = assetId / 31;
    uint256 assetPos = assetId - 31 * assetFloor;
    bytes32 allAsset = assets[msg.sender][assetFloor];

    bytes32 mask = bytes32(255) << (8 * assetPos); // 0x11111111
    uint256 maskedValue;
    uint256 maskedResult;
    uint256 addAmount = amount << (8 * assetPos);

    // check whether there are enough unpending assets to sell
    maskedValue = uint256(allAsset & mask);
    require(addAmount <= maskedValue);

    // Remove assets to be sold from owner
    maskedResult = maskedValue - addAmount;
    allAsset = ((allAsset ^ mask) & allAsset) | bytes32(maskedResult);

    assets[msg.sender][assetFloor] = allAsset;

    // Put on sale
    SaleOrder memory saleorder = SaleOrder(
        uint64(assetId),
        uint64(amount),
        uint128(price),
        msg.sender
    );

    saleOrderList[nextSaleId] = saleorder;
    nextSaleId += 1;

    accountToSaleNum[msg.sender] += 1;

    // Emit the Approval event
    emit PutOnSale(msg.sender, nextSaleId-1);
}

// Put asset on sale