contract CowMarket is Whitelist {
    using SafeMath for uint256;

    enum SaleStatus {
        DISABLED,
        WHITELIST,
        GENERAL
    }

    uint256 public tokenPrice = 80000000000000000; // 0.08 ETH
    uint256 public maxTokenPurchase = 5;
    uint256 public constant maxTokens = 3333;

    address public fund;
    SaleStatus public saleStatus;

    ICollectible public cows;

    event TokenPriceChanged(uint256 price);
    event MaxPurchaseChanged(uint256 value);
    event FundSet(address bankAccount);
    event RolledOver(SaleStatus status);

    constructor(ICollectible _cows, address _fund) {
        require(
            address(_cows) != address(0) &&
            _fund != address(0),
            "Unacceptable address set"
        );

        cows = _cows;
        fund = _fund;
    }

    receive() external payable {
        uint256 deposit = msg.value;
        uint256 amount = deposit.div(tokenPrice);

        require(
            SaleStatus.DISABLED != saleStatus,
            "Collection: sale is not active"
        );
        
        if (SaleStatus.WHITELIST == saleStatus) {
            require(isMember(_msgSender()), "Whitelist: not in the list");
        }
 
        require(
            amount <= maxTokenPurchase,
            "Collection: exceeds max number of Tokens in one transaction"
        );
        require(
            cows.totalSupply().add(amount) <= maxTokens,
            "Collection: purchase would exceed max supply of Tokens"
        );
        require(tokenPrice.mul(amount) == deposit,
            "Collection: ether value sent is not correct"
        );

        if (fund != address(this)) {
            Address.sendValue(payable(fund), deposit);
        }

        cows.mint(msg.sender, amount);
    }

    function buy(uint _amount) external payable {
        require(SaleStatus.DISABLED != saleStatus, "Collection: sale is not active");

        if (SaleStatus.WHITELIST == saleStatus) {
            require(isMember(_msgSender()), "Whitelist: not in the list");
        }

        require(
            _amount <= maxTokenPurchase,
            "Collection: exceeds max number of Tokens in one transaction"
        );
        require(
            cows.totalSupply().add(_amount) <= maxTokens,
            "Collection: purchase would exceed max supply of Tokens"
        );

        uint256 deposit = msg.value;

        require(tokenPrice.mul(_amount) == deposit,
            "Collection: ether value sent is not correct"
        );

        if (fund != address(this)) {
            Address.sendValue(payable(fund), deposit);
        }

        cows.mint(msg.sender, _amount);
    }

    // @dev Changes sale status.
    // Permission: only owner
    function setSaleStatus(SaleStatus _status) public onlyOwner {
        saleStatus = _status;
        emit RolledOver(_status);
    }


    // @dev Setup new token sale price.
    // Permission: only owner
    function setPrice(uint256 _price) external onlyOwner {
        require(_price != 0, "Zero price");

        tokenPrice = _price;
        emit TokenPriceChanged(_price);
    }

    // @dev Setup max tokens purchase per transaction.
    // Permission: only owner
    function setMaxPurchase(uint256 _value) external onlyOwner {
        require(_value != 0, "Very low value");

        maxTokenPurchase = _value;
        emit MaxPurchaseChanged(_value);
    }

    // @dev Setup new bank account.
    // Permission: only owner
    function setFund(address _account) external onlyOwner {
        require(_account != address(0), "Zero address set");

        fund = _account;
        emit FundSet(_account);
    }

    // @dev Withdraw ethereum from contract balance.
    // Permission: only owner
    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        Address.sendValue(payable(_msgSender()), balance);
    }
}
// import "contracts/interfaces/ICollectible.sol";
// import "contracts/interfaces/ICowRegistry.sol";
// import "contracts/access/Whitelist.sol";
function withdraw() public onlyOwner {
    uint balance = address(this).balance;
    Address.sendValue(payable(_msgSender()), balance);
}

// @dev Withdraw ethereum from contract balance.
// Permission: only owner