contract CharitySplitter {
    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    //                            Constructor                               //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    /// @dev Charity address, can be changed with a time-lock.
    address payable public charity;

    /// @dev Charity fee, in basis points. 1% = 100 bp.
    uint256 public charityFeeBp;

    constructor(address payable _charity, uint256 _charityFeeBp) {
        // checks: address not zero. Don't want to burn eth.
        if (_charity == address(0)) {
            revert CharitySplitter__InvalidCharityAddress();
        }

        // checks: fee > 0%. Don't want to deal with no fee edge cases.
        if (_charityFeeBp == 0) {
            revert CharitySplitter__InvalidCharityFee();
        }

        charity = _charity;
        charityFeeBp = _charityFeeBp;
    }

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    //                     Charity Address Management                       //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    /// @dev Emitted when the charity address is updated.
    event CharityUpdated(address charity);

    /// @dev Update the charity address.
    function _updateCharity(address payable _charity)
        internal
    {
        if (_charity == address(0)) {
            revert CharitySplitter__InvalidCharityAddress();
        }

        charity = _charity;
    }

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    //                         Profit Tracking                              //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    /// @dev Balance owed to the charity.
    uint256 public charityBalance;

    /// @dev Balance owed to the owner.
    uint256 public ownerBalance;

    /// @dev Denominator used when computing fee. 100% in bp.
    uint256 private constant BP_DENOMINATOR = 10000;

    /// @dev Update charity and owner balance.
    function _updateBalance(uint256 value)
        internal
    {
        // checks: if value is zero nothing to update.
        if (value == 0) {
            return;
        }

        uint256 charityValue = (value * charityFeeBp) / BP_DENOMINATOR;
        uint256 ownerValue = value - charityValue;

        // effects: update balances.
        charityBalance += charityValue;
        ownerBalance += ownerValue;
    }

    //////////////////////////////////////////////////////////////////////////
    //                                                                      //
    //                       Withdrawing Funds                              //
    //                                                                      //
    //////////////////////////////////////////////////////////////////////////

    /// @notice Withdraw funds to charity address.
    function _withdrawCharityBalance()
        internal
    {
        uint256 value = charityBalance;

        // checks: no money to withdraw.
        if (value == 0) {
            return;
        }

        // effects: reset charity balance to zero.
        charityBalance = 0;

        // interactions: send money to charity address.
        (bool sent, ) = charity.call{value: value}("");
        require(sent);
    }

    /// @notice Withdraw funds to owner address.
    /// @param destination the address that receives the funds.
    function _withdrawOwnerBalance(address payable destination)
        internal
    {
        uint256 value = ownerBalance;

        // checks: no money to withdraw.
        if (value == 0) {
            return;
        }

        // effects: reset owner balance to zero.
        ownerBalance = 0;

        // interactions: send money to destination address.
        (bool sent, ) = destination.call{value: value}("");
        require(sent);
    }
}
/// @notice Tracks splitting profits with a charity.
/// @dev This module essentialy does two things:
///  * Tracks how much money goes to the charity and how much to someone else.
///  * Implements a simple time-lock to avoid the contract owner changing the
///    charity address to one they own.
///
/// Anyone can call the method to send the funds to the charity.
function _updateBalance(uint256 value)
    internal
{
    // checks: if value is zero nothing to update.
    if (value == 0) {
        return;
    }

    uint256 charityValue = (value * charityFeeBp) / BP_DENOMINATOR;
    uint256 ownerValue = value - charityValue;

    // effects: update balances.
    charityBalance += charityValue;
    ownerBalance += ownerValue;
}

/// @dev Update charity and owner balance.