contract TokenTrader is Owned {

    address public asset;       // address of token
    uint256 public buyPrice;    // contract buys lots of token at this price
    uint256 public sellPrice;   // contract sells lots at this price
    uint256 public units;       // lot size (token-wei)

    bool public buysTokens;     // is contract buying
    bool public sellsTokens;    // is contract selling

    event ActivatedEvent(bool buys, bool sells);
    event MakerDepositedEther(uint256 amount);
    event MakerWithdrewAsset(uint256 tokens);
    event MakerTransferredAsset(address toTokenTrader, uint256 tokens);
    event MakerWithdrewERC20Token(address tokenAddress, uint256 tokens);
    event MakerWithdrewEther(uint256 ethers);
    event MakerTransferredEther(address toTokenTrader, uint256 ethers);
    event TakerBoughtAsset(address indexed buyer, uint256 ethersSent,
        uint256 ethersReturned, uint256 tokensBought);
    event TakerSoldAsset(address indexed seller, uint256 etherValueOfTokensToSell,
        uint256 tokensSold, uint256 etherValueOfTokensSold);

    // Constructor - only to be called by the TokenTraderFactory contract
    function TokenTrader (
        address _asset,
        uint256 _buyPrice,
        uint256 _sellPrice,
        uint256 _units,
        bool    _buysTokens,
        bool    _sellsTokens
    ) {
        asset       = _asset;
        buyPrice    = _buyPrice;
        sellPrice   = _sellPrice;
        units       = _units;
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can activate or deactivate this contract's buying and
    // selling status
    //
    // The ActivatedEvent() event is logged with the following
    // parameter:
    //   buysTokens   this contract can buy asset tokens
    //   sellsTokens  this contract can sell asset tokens
    //
    function activate (
        bool _buysTokens,
        bool _sellsTokens
    ) onlyOwner {
        buysTokens  = _buysTokens;
        sellsTokens = _sellsTokens;
        ActivatedEvent(buysTokens, sellsTokens);
    }

    // Maker can deposit ethers to this contract so this contract
    // can buy asset tokens.
    //
    // Maker deposits asset tokens to this contract by calling the
    // asset's transfer() method with the following parameters
    //   _to     is the address of THIS contract
    //   _value  is the number of asset tokens to be transferred
    //
    // Taker MUST NOT send tokens directly to this contract. Takers
    // MUST use the takerSellAsset() method to sell asset tokens
    // to this contract
    //
    // Maker can also transfer ethers from one TokenTrader contract
    // to another TokenTrader contract, both owned by the Maker
    //
    // The MakerDepositedEther() event is logged with the following
    // parameter:
    //   ethers  is the number of ethers deposited by the maker
    //
    // This method was called deposit() in the old version
    //
    function makerDepositEther() payable onlyOwnerOrTokenTraderWithSameOwner {
        MakerDepositedEther(msg.value);
    }

    // Maker can withdraw asset tokens from this contract, with the
    // following parameter:
    //   tokens  is the number of asset tokens to be withdrawn
    //
    // The MakerWithdrewAsset() event is logged with the following
    // parameter:
    //   tokens  is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawAsset() in the old version
    //
    function makerWithdrawAsset(uint256 tokens) onlyOwner returns (bool ok) {
        MakerWithdrewAsset(tokens);
        return ERC20(asset).transfer(owner, tokens);
    }

    // Maker can transfer asset tokens from this contract to another
    // TokenTrader contract, with the following parameter:
    //   toTokenTrader  Another TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   tokens         is the number of asset tokens to be moved
    //
    // The MakerTransferredAsset() event is logged with the following
    // parameters:
    //   toTokenTrader  The other TokenTrader contract owned by
    //                  the same owner and with the same asset
    //   tokens         is the number of tokens transferred
    //
    // The asset Transfer() event is also logged from this contract
    // to the other contract
    //
    function makerTransferAsset(
        TokenTrader toTokenTrader,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        if (owner != toTokenTrader.owner() || asset != toTokenTrader.asset()) {
            throw;
        }
        MakerTransferredAsset(toTokenTrader, tokens);
        return ERC20(asset).transfer(toTokenTrader, tokens);
    }

    // Maker can withdraw any ERC20 asset tokens from this contract
    //
    // This method is included in the case where this contract receives
    // the wrong tokens
    //
    // The MakerWithdrewERC20Token() event is logged with the following
    // parameter:
    //   tokenAddress  is the address of the tokens withdrawn by the maker
    //   tokens        is the number of tokens withdrawn by the maker
    //
    // This method was called withdrawToken() in the old version
    //
    function makerWithdrawERC20Token(
        address tokenAddress,
        uint256 tokens
    ) onlyOwner returns (bool ok) {
        MakerWithdrewERC20Token(tokenAddress, tokens);
        return ERC20(tokenAddress).transfer(owner, tokens);
    }

    // Maker can withdraw ethers from this contract
    //
    // The MakerWithdrewEther() event is logged with the following parameter
    //   ethers  is the number of ethers withdrawn by the maker
    //
    // This method was called withdraw() in the old version
    //
    function makerWithdrawEther(uint256 ethers) onlyOwner returns (bool ok) {
        if (this.balance >= ethers) {
            MakerWithdrewEther(ethers);
            return owner.send(ethers);
        }
    }

    // Maker can transfer ethers from this contract to another TokenTrader
    // contract, with the following parameters:
    //   toTokenTrader  Another TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   ethers         is the number of ethers to be moved
    //
    // The MakerTransferredEther() event is logged with the following parameter
    //   toTokenTrader  The other TokenTrader contract owned by the
    //                  same owner and with the same asset
    //   ethers         is the number of ethers transferred
    //
    // The MakerDepositedEther() event is logged on the other
    // contract with the following parameter:
    //   ethers  is the number of ethers deposited by the maker
    //
    function makerTransferEther(
        TokenTrader toTokenTrader,
        uint256 ethers
    ) onlyOwner returns (bool ok) {
        if (owner != toTokenTrader.owner() || asset != toTokenTrader.asset()) {
            throw;
        }
        if (this.balance >= ethers) {
            MakerTransferredEther(toTokenTrader, ethers);
            toTokenTrader.makerDepositEther.value(ethers)();
        }
    }

    // Taker buys asset tokens by sending ethers
    //
    // The TakerBoughtAsset() event is logged with the following parameters
    //   buyer           is the buyer's address
    //   ethersSent      is the number of ethers sent by the buyer
    //   ethersReturned  is the number of ethers sent back to the buyer as
    //                   change
    //   tokensBought    is the number of asset tokens sent to the buyer
    //
    // This method was called buy() in the old version
    //
    function takerBuyAsset() payable {
        if (sellsTokens || msg.sender == owner) {
            // Note that sellPrice has already been validated as > 0
            uint order    = msg.value / sellPrice;
            // Note that units has already been validated as > 0
            uint can_sell = ERC20(asset).balanceOf(address(this)) / units;
            uint256 change = 0;
            if (msg.value > (can_sell * sellPrice)) {
                change  = msg.value - (can_sell * sellPrice);
                order = can_sell;
            }
            if (change > 0) {
                if (!msg.sender.send(change)) throw;
            }
            if (order > 0) {
                if (!ERC20(asset).transfer(msg.sender, order * units)) throw;
            }
            TakerBoughtAsset(msg.sender, msg.value, change, order * units);
        }
        // Return user funds if the contract is not selling
        else if (!msg.sender.send(msg.value)) throw;
    }

    // Taker sells asset tokens for ethers by:
    // 1. Calling the asset's approve() method with the following parameters
    //    _spender            is the address of this contract
    //    _value              is the number of tokens to be sold
    // 2. Calling this takerSellAsset() method with the following parameter
    //    etherValueOfTokens  is the ether value of the asset tokens to be sold
    //                        by the taker
    //
    // The TakerSoldAsset() event is logged with the following parameters
    //   seller                    is the seller's address
    //   etherValueOfTokensToSell  is the ether value of the asset tokens being
    //                             sold by the taker
    //   tokensSold                is the number of the asset tokens sold
    //   etherValueOfTokensSold    is the ether value of the asset tokens sold
    //
    // This method was called sell() in the old version
    //
    function takerSellAsset(uint256 etherValueOfTokensToSell) {
        if (buysTokens || msg.sender == owner) {
            // Maximum number of token the contract can buy
            // Note that buyPrice has already been validated as > 0
            uint256 can_buy = this.balance / buyPrice;
            // Token lots available
            // Note that units has already been validated as > 0
            uint256 order = etherValueOfTokensToSell / units;
            // Adjust order for funds available
            if (order > can_buy) order = can_buy;
            if (order > 0) {
                // Extract user tokens
                if (!ERC20(asset).transferFrom(msg.sender, address(this), order * units)) throw;
                // Pay user
                if (!msg.sender.send(order * buyPrice)) throw;
            }
            TakerSoldAsset(msg.sender, etherValueOfTokensToSell, order * units, order * buyPrice);
        }
    }

    // Taker buys tokens by sending ethers
    function () payable {
        takerBuyAsset();
    }
}
// contract can buy or sell tokens for ETH
// prices are in amount of wei per batch of token units
function makerWithdrawEther(uint256 ethers) onlyOwner returns (bool ok) {
    if (this.balance >= ethers) {
        MakerWithdrewEther(ethers);
        return owner.send(ethers);
    }
}

// Maker can withdraw ethers from this contract
//
// The MakerWithdrewEther() event is logged with the following parameter
//   ethers  is the number of ethers withdrawn by the maker
//
// This method was called withdraw() in the old version
//