contract Token is Owned, Mutex {
    // @dev using OpenZeppelin's SafeMath library
    using SafeMath for uint256;

    Ledger public ledger;

    uint256 public lockedSupply = 0;

    string public name;
    uint8 public decimals; 
    string public symbol;

    string public version = '0.2'; 
    bool public transfersOn = true;

    // @notice Constructs a Token
    // @dev changed to comply with 0.4.17 and above syntax,
    // but later versions could use 'constructor(...)' syntax
    // @param _owner Intended owner of the Token contract
    // @param _tokenName Intended name of the Token
    // @param _decimals Intended precision of the Token
    // @param _symbol Intended symbol of the Token
    // @param _ledger Intended address of the Ledger
    constructor(address _owner, string _tokenName, uint8 _decimals,
                string _symbol, address _ledger) public {
        require(_owner != address(0), "address cannot be null");
        owner = _owner;

        name = _tokenName;
        decimals = _decimals;
        symbol = _symbol;

        ledger = Ledger(_ledger);
    }

    /*
    *   Bookkeeping and Admin Functions
    */

    // @notice Event emitted when the Ledger is updated
    // @param _from Address that updates the Ledger
    // @param _ledger Address of the Ledger
    event LedgerUpdated(address _from, address _ledger);



    // @notice Allow the owner to change the address of the Ledger
    // @param _addr Intended new address of the Ledger
    function changeLedger(address _addr) onlyOwner public {
        require(_addr != address(0), "address cannot be null");
        ledger = Ledger(_addr);
    
        emit LedgerUpdated(msg.sender, _addr);
    }

    /*
    * Locking is a feature that turns a user's balances into
    * un-issued tokens, taking them out of an account and reducing the supply.
    * Diluting is so named to remind the caller that they are changing the money supply.
    */

    // @notice Allows owner to lock the balance of an address,
    // reducing the total circulating supply by the balance of that address
    // and increasing the locked supply of Tokens
    // @param _seizeAddr Intended address whose account balance is to be frozen
    function lock(address _seizeAddr) onlyOwner mutexed public {
        require(_seizeAddr != address(0), "address cannot be null");

        uint256 myBalance = ledger.balanceOf(_seizeAddr);
        lockedSupply = lockedSupply.add(myBalance);
        ledger.setBalance(_seizeAddr, 0);
    }

    // @notice Event that marks a "dilution" to a target address and the amount
    // @param _destAddr Intended address of the Token "dilution"
    // @param _amount Intended amount to be given to _destAddr
    event Dilution(address _destAddr, uint256 _amount);

    // @notice Allows the owner to unlock some of the locked supply
    // and give it to another address, increasing the circulating Token supply
    // (not exactly a true dilution of the current Token supply)
    // @param _destAddr Intended address of the recipient of the unlocked amount
    // @param amount Intended amount to be given to _destAddr
    function dilute(address _destAddr, uint256 amount) onlyOwner public {
        require(amount <= lockedSupply, "amount greater than lockedSupply");

        lockedSupply = lockedSupply.sub(amount);

        uint256 curBalance = ledger.balanceOf(_destAddr);
        curBalance = curBalance.add(amount);
        ledger.setBalance(_destAddr, curBalance);

        emit Dilution(_destAddr, amount);
    }

    // @notice Allow the owner to pause arbitrary transfers of Tokens
    function pauseTransfers() onlyOwner public {
        transfersOn = false;
    }

    // @notice Allow the owner to resume arbitrary transfers of Tokens
    function resumeTransfers() onlyOwner public {
        transfersOn = true;
    }

    /*
    * Burning -- We allow any user to burn tokens.
    *
     */

    // @notice Allows any arbitrary user to burn their Tokens
    // @param _amount Number of Tokens a user wants to burn
    function burn(uint256 _amount) public {
        uint256 balance = ledger.balanceOf(msg.sender);
        require(_amount <= balance, "not enough balance");
        ledger.setBalance(msg.sender, balance.sub(_amount));
        emit Transfer(msg.sender, 0, _amount);
    }

    /*
    Entry
    */

    // @notice Event for transfer of Tokens
    // @param _from Address from which the Tokens were transferred
    // @param _to Address to which the Tokens were transferred
    // @param _value Amount of Tokens transferred
    event Transfer(address indexed _from, address indexed _to, uint256 _value);
    // @notice Event for approval of Tokens for some other user
    // @param _owner Owner of the Tokens
    // @param _spender Address that the owner approved for spending Tokens
    // @param _value Amount of Tokens allocated for spending
    event Approval(address indexed _owner, address indexed _spender, uint256 _value);

    // @notice Function to view the total circulating supply of Token
    // @dev Needs to interact with Ledger
    function totalSupply() public view returns(uint256) {
        return ledger.totalSupply();
    }

    // @notice Transfers Tokens to another user
    // @dev Needs to interact with Ledger
    function transfer(address _to, uint256 _value) public returns(bool) {
        require(transfersOn || msg.sender == owner, "transferring disabled");
        require(ledger.tokenTransfer(msg.sender, _to, _value), "transfer failed");

        emit Transfer(msg.sender, _to, _value);
        return true;
    }

    // @notice Transfers Tokens from one user to another via an approved third party
    // @dev Needs to interact with Ledger
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(transfersOn || msg.sender == owner, "transferring disabled");
        require(ledger.tokenTransferFrom(msg.sender, _from, _to, _value), "transferFrom failed");

        emit Transfer(_from, _to, _value);
        uint256 allowed = allowance(_from, msg.sender);
        emit Approval(_from, msg.sender, allowed);
        return true;
    }

    // @notice Views the allowance of a third party given by an owner of Tokens
    // @dev Needs to interact with Ledger
    function allowance(address _owner, address _spender) public view returns(uint256) {
        return ledger.allowance(_owner, _spender); 
    }

    // @notice Allows a user to approve another user to spend an amount of Tokens on their behalf
    // @dev Needs to interact with Ledger
    function approve(address _spender, uint256 _value) public returns (bool) {
        require(ledger.tokenApprove(msg.sender, _spender, _value), "approve failed");
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    // @notice Views the Token balance of a user
    // @dev Needs to interact with Ledger
    function balanceOf(address _addr) public view returns(uint256) {
        return ledger.balanceOf(_addr);
    }
}
function allowance(address _owner, address _spender) public view returns(uint256) {
    return ledger.allowance(_owner, _spender); 
}

// @notice Views the allowance of a third party given by an owner of Tokens
// @dev Needs to interact with Ledger