library Order {
    using SafeMathUint256 for uint256;

    enum Types {Bid, Ask}

    enum TradeDirections {Long, Short}

    struct Data {
        // Contracts
        IMarket market;
        IAugur augur;
        IAugurTrading augurTrading;
        IShareToken shareToken;
        ICash cash;
        // Order
        bytes32 id;
        address creator;
        uint256 outcome;
        Order.Types orderType;
        uint256 amount;
        uint256 price;
        uint256 sharesEscrowed;
        uint256 moneyEscrowed;
        bytes32 betterOrderId;
        bytes32 worseOrderId;
    }

    function create(
        IAugur _augur,
        IAugurTrading _augurTrading,
        address _creator,
        uint256 _outcome,
        Order.Types _type,
        uint256 _attoshares,
        uint256 _price,
        IMarket _market,
        bytes32 _betterOrderId,
        bytes32 _worseOrderId
    ) internal view returns (Data memory) {
        require(
            _outcome < _market.getNumberOfOutcomes(),
            'Order.create: Outcome is not within market range'
        );
        require(_price != 0, 'Order.create: Price may not be 0');
        require(
            _price < _market.getNumTicks(),
            'Order.create: Price is outside of market range'
        );
        require(_attoshares > 0, 'Order.create: Cannot use amount of 0');
        require(_creator != address(0), 'Order.create: Creator is 0x0');

        IShareToken _shareToken = IShareToken(_augur.lookup('ShareToken'));

        return
            Data({
                market: _market,
                augur: _augur,
                augurTrading: _augurTrading,
                shareToken: _shareToken,
                cash: ICash(_augur.lookup('Cash')),
                id: 0,
                creator: _creator,
                outcome: _outcome,
                orderType: _type,
                amount: _attoshares,
                price: _price,
                sharesEscrowed: 0,
                moneyEscrowed: 0,
                betterOrderId: _betterOrderId,
                worseOrderId: _worseOrderId
            });
    }

    //
    // "public" functions
    //

    function getOrderId(Order.Data memory _orderData, IOrders _orders)
        internal
        view
        returns (bytes32)
    {
        if (_orderData.id == bytes32(0)) {
            bytes32 _orderId =
                calculateOrderId(
                    _orderData.orderType,
                    _orderData.market,
                    _orderData.amount,
                    _orderData.price,
                    _orderData.creator,
                    block.number,
                    _orderData.outcome,
                    _orderData.moneyEscrowed,
                    _orderData.sharesEscrowed
                );
            require(
                _orders.getAmount(_orderId) == 0,
                'Order.getOrderId: New order had amount. This should not be possible'
            );
            _orderData.id = _orderId;
        }
        return _orderData.id;
    }

    function calculateOrderId(
        Order.Types _type,
        IMarket _market,
        uint256 _amount,
        uint256 _price,
        address _sender,
        uint256 _blockNumber,
        uint256 _outcome,
        uint256 _moneyEscrowed,
        uint256 _sharesEscrowed
    ) internal pure returns (bytes32) {
        return
            sha256(
                abi.encodePacked(
                    _type,
                    _market,
                    _amount,
                    _price,
                    _sender,
                    _blockNumber,
                    _outcome,
                    _moneyEscrowed,
                    _sharesEscrowed
                )
            );
    }

    function getOrderTradingTypeFromMakerDirection(
        Order.TradeDirections _creatorDirection
    ) internal pure returns (Order.Types) {
        return
            (_creatorDirection == Order.TradeDirections.Long)
                ? Order.Types.Bid
                : Order.Types.Ask;
    }

    function getOrderTradingTypeFromFillerDirection(
        Order.TradeDirections _fillerDirection
    ) internal pure returns (Order.Types) {
        return
            (_fillerDirection == Order.TradeDirections.Long)
                ? Order.Types.Ask
                : Order.Types.Bid;
    }

    function saveOrder(
        Order.Data memory _orderData,
        bytes32 _tradeGroupId,
        IOrders _orders
    ) internal returns (bytes32) {
        getOrderId(_orderData, _orders);
        uint256[] memory _uints = new uint256[](5);
        _uints[0] = _orderData.amount;
        _uints[1] = _orderData.price;
        _uints[2] = _orderData.outcome;
        _uints[3] = _orderData.moneyEscrowed;
        _uints[4] = _orderData.sharesEscrowed;
        bytes32[] memory _bytes32s = new bytes32[](4);
        _bytes32s[0] = _orderData.betterOrderId;
        _bytes32s[1] = _orderData.worseOrderId;
        _bytes32s[2] = _tradeGroupId;
        _bytes32s[3] = _orderData.id;
        return
            _orders.saveOrder(
                _uints,
                _bytes32s,
                _orderData.orderType,
                _orderData.market,
                _orderData.creator
            );
    }
}
// Copyright (C) 2015 Forecast Foundation OU, full GPL notice in LICENSE
// Bid / Ask actions: puts orders on the book
// price is denominated by the specific market's numTicks
// amount is the number of attoshares the order is for (either to buy or to sell).
// price is the exact price you want to buy/sell at [which may not be the cost, for example to short a yesNo market it'll cost numTicks-price, to go long it'll cost price]
// CONSIDER: Is `price` the most appropriate name for the value being used? It does correspond 1:1 with the attoCASH per share, but the range might be considered unusual?
function getOrderId(Order.Data memory _orderData, IOrders _orders)
    internal
    view
    returns (bytes32)
{
    if (_orderData.id == bytes32(0)) {
        bytes32 _orderId =
            calculateOrderId(
                _orderData.orderType,
                _orderData.market,
                _orderData.amount,
                _orderData.price,
                _orderData.creator,
                block.number,
                _orderData.outcome,
                _orderData.moneyEscrowed,
                _orderData.sharesEscrowed
            );
        require(
            _orders.getAmount(_orderId) == 0,
            'Order.getOrderId: New order had amount. This should not be possible'
        );
        _orderData.id = _orderId;
    }
    return _orderData.id;
}

//
// "public" functions
//