contract PubToken is ERC20("PUB.finance","PUBS"), Ownable {
    using BasisPoints for uint;
    using SafeMath for uint;

    uint public burnBP;
    uint public taxBP;
    Bartender private bartender;


    mapping(address => bool) public taxExempt;
    mapping(address => bool) public fromOnlyTaxExempt;
    mapping(address => bool) public toOnlyTaxExempt;


    constructor(uint _taxBP, uint _burnBP, address _bartender, address owner) public { 
        bartender = Bartender(_bartender);
        taxBP = _taxBP;
        burnBP = _burnBP;
        setTaxExemptStatus(address(bartender), true);
        transferOwnership(owner); 
    }
    modifier onlyBartender {
        require(msg.sender == address(bartender), "Can only be called by Bartender contract.");
        _;
    }

    /// @notice Creates `_amount` token to `_to`. Must only be called by the owner (Bartender).
    function mint(address _to, uint256 _amount) public onlyBartender {
        _mint(_to, _amount);
    }


    function setFromOnlyTaxExemptStatus(address account, bool status) external onlyOwner {
        fromOnlyTaxExempt[account] = status;
    }

    function setToOnlyTaxExemptStatus(address account, bool status) external onlyOwner {
        fromOnlyTaxExempt[account] = status;
    }

    function setTaxExemptStatus(address account, bool status) public onlyOwner {
        taxExempt[account] = status;
    }


    function transfer(address recipient, uint amount) public override returns (bool) {
        (
        !taxExempt[msg.sender] && !taxExempt[recipient] &&
        !toOnlyTaxExempt[recipient] && !fromOnlyTaxExempt[msg.sender]
        ) ?
        _transferWithTax(msg.sender, recipient, amount) :
        _transfer(msg.sender, recipient, amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint amount) public override returns (bool) {
        (
        !taxExempt[sender] && !taxExempt[recipient] &&
        !toOnlyTaxExempt[recipient] && !fromOnlyTaxExempt[sender]
        ) ?
        _transferWithTax(sender, recipient, amount) :
        _transfer(sender, recipient, amount);

        approve(
            msg.sender,
            allowance(
                sender,
                msg.sender
            ).sub(amount, "Transfer amount exceeds allowance")
        );
        return true;
    }

    function findTaxAmount(uint value) public view returns (uint tax, uint devTax) {
        tax = value.mulBP(taxBP);
        devTax = value.mulBP(burnBP);
    }

    function _transferWithTax(address sender, address recipient, uint amount) internal {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");

        (uint tax, uint devTax) = findTaxAmount(amount);
        uint tokensToTransfer = amount.sub(tax).sub(devTax);

        _transfer(sender, address(bartender), tax);
        _transfer(sender, address(bartender), devTax);

        _transfer(sender, recipient, tokensToTransfer);
        bartender.handleTaxDistribution(tax, devTax);
    }

}
// PUBS
function mint(address _to, uint256 _amount) public onlyBartender {
    _mint(_to, _amount);
}

/// @notice Creates `_amount` token to `_to`. Must only be called by the owner (Bartender).