contract BGBPToken is ERC20, Pausable, BlackListableToken {

    string public name;
    string public symbol;
    uint8 public decimals;
    address public upgradedAddress;
    bool public deprecated;

    //  The contract can be initialized with a number of tokens
    //  All the tokens are deposited to the owner address
    //
    // @param _balance Initial supply of the contract
    // @param _name Token Name
    // @param _symbol Token symbol
    // @param _decimals Token decimals
    constructor(uint256 _initialSupply, string memory _name, string memory _symbol, uint8 _decimals) public {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;
        deprecated = false;
        super._mint(msg.sender, _initialSupply);
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function transfer(address _to, uint256 _value) public whenNotPaused returns (bool success) {
        require(!isBlackListed[msg.sender], "can't transfer token from address in black list");
        require(!isBlackListed[_to], "can't transfer token to address in black list");
        if (deprecated) {
            success = UpgradedStandardToken(upgradedAddress).transferByLegacy(msg.sender, _to, _value);
            require(success, "failed to call upgraded contract");
            return true;
        } else {
            return super.transfer(_to, _value);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool success) {
        require(!isBlackListed[_from], "can't transfer token from address in black list");
        require(!isBlackListed[_to], "can't transfer token to address in black list");
        if (deprecated) {
            success = UpgradedStandardToken(upgradedAddress).transferFromByLegacy(msg.sender, _from, _to, _value);
            require(success, "failed to call upgraded contract");
            return true;
        } else {
            return super.transferFrom(_from, _to, _value);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function balanceOf(address who) public view returns (uint256) {
        if (deprecated) {
            return UpgradedStandardToken(upgradedAddress).balanceOf(who);
        } else {
            return super.balanceOf(who);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function approve(address _spender, uint256 _value) public whenNotPaused returns (bool success) {
        if (deprecated) {
            success = UpgradedStandardToken(upgradedAddress).approveByLegacy(msg.sender, _spender, _value);
            require(success, "failed to call upgraded contract");
            return true;
        } else {
            return super.approve(_spender, _value);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function increaseAllowance(address _spender, uint256 _addedValue) public whenNotPaused returns (bool success) {
        if (deprecated) {
            success = UpgradedStandardToken(upgradedAddress).increaseAllowanceByLegacy(msg.sender, _spender, _addedValue);
            require(success, "failed to call upgraded contract");
            return true;
        } else {
            return super.increaseAllowance(_spender, _addedValue);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function decreaseAllowance(address _spender, uint256 _subtractedValue) public whenNotPaused returns (bool success) {
        if (deprecated) {
            success = UpgradedStandardToken(upgradedAddress).decreaseAllowanceByLegacy(msg.sender, _spender, _subtractedValue);
            require(success, "failed to call upgraded contract");
            return true;
        } else {
            return super.decreaseAllowance(_spender, _subtractedValue);
        }
    }

    // Forward ERC20 methods to upgraded contract if this one is deprecated
    function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
        if (deprecated) {
            return UpgradedStandardToken(upgradedAddress).allowance(_owner, _spender);
        } else {
            return super.allowance(_owner, _spender);
        }
    }

    // deprecate current contract in favour of a new one
    function deprecate(address _upgradedAddress) public onlyOwner {
        require(_upgradedAddress != address(0x0), "_upgradedAddress is a zero address");
        require(!deprecated, "this contract has been deprecated");

        deprecated = true;
        upgradedAddress = _upgradedAddress;
        emit Deprecate(_upgradedAddress);
    }

    function totalSupply() public view returns (uint256) {
        if (deprecated) {
            return UpgradedStandardToken(upgradedAddress).totalSupply();
        } else {
            return super.totalSupply();
        }
    }

    // Issue a new amount of tokens
    // these tokens are deposited into the owner address
    //
    // @param _amount Number of tokens to be issued
    function issue(uint256 amount) public onlyOwner whenNotPaused {
        require(!deprecated, "this contract has been deprecated");

        super._mint(msg.sender, amount);
        emit Issue(amount);
    }

    // Redeem tokens.
    // These tokens are withdrawn from the owner address
    // if the balance must be enough to cover the redeem
    // or the call will fail.
    // @param _amount Number of tokens to be issued
    function redeem(uint256 amount) public onlyOwner whenNotPaused {
        require(!deprecated, "this contract has been deprecated");

        super._burn(msg.sender, amount);
        emit Redeem(amount);
    }

    // Called when new token are issued
    event Issue(uint256 amount);

    // Called when tokens are redeemed
    event Redeem(uint256 amount);

    // Called when contract is deprecated
    event Deprecate(address indexed newAddress);
}
function decreaseAllowance(address _spender, uint256 _subtractedValue) public whenNotPaused returns (bool success) {
    if (deprecated) {
        success = UpgradedStandardToken(upgradedAddress).decreaseAllowanceByLegacy(msg.sender, _spender, _subtractedValue);
        require(success, "failed to call upgraded contract");
        return true;
    } else {
        return super.decreaseAllowance(_spender, _subtractedValue);
    }
}

// Forward ERC20 methods to upgraded contract if this one is deprecated