contract Brewer is Ownable {
    using SafeMath for uint256;
    using SafeERC20 for IERC20;

    // Airdrop list
    address[] public airdropList;
    // Only deposit user can get airdrop.
    mapping(address => bool) addressAvailable;
    mapping(address => bool) addressAvailableHistory;

    // Claimable wine of user.
    struct UserWineInfo {
        uint256 amount;
    }
    // Info of each user that claimable wine.
    mapping (address => mapping (uint256 => UserWineInfo)) public userWineInfo;

    // Ticket of users
    mapping(address => uint256) ticketBalances;
    // Info of each wine.
    struct WineInfo {
        uint256 wineID;            // Wine's ID. 
        uint256 amount;            // Distribution amount.
        uint256 fixedPrice;        // Claim the wine need pay some wETH.
    }
    // Info of each wine.
    WineInfo[] public wineInfo;
    // Total wine amount.
    uint256 public totalWineAmount = 0;
    // Original total wine amount.
    uint256 public originalTotalWineAmount = 0;
    // Draw consumption
    uint256 public ticketsConsumed = 1000 * (10 ** 18);
    // Base number
    uint256 public base = 10 ** 6;
    // Claim fee is 3%.
    // Pool's fee 1%. Artist's fee 2%.
    uint256 public totalFee = 3 * (base) / 100;

    // Wine token.
    IGRAPWine GRAPWine;

    event Reward(address indexed user, uint256 indexed wineID);
    event AirDrop(address indexed user, uint256 indexed wineID);

    function wineLength() public view returns (uint256) {
        return wineInfo.length;
    }

    function ticketBalanceOf(address tokenOwner) public view returns (uint256) {
        return ticketBalances[tokenOwner];
    }

    function userWineBalanceOf(address tokenOwner, uint256 _wineID) public view returns (uint256) {
        return userWineInfo[tokenOwner][_wineID].amount;
    }

    function userUnclaimWine(address tokenOwner) public view returns (uint256[] memory) {
        uint256[] memory userWine = new uint256[](wineInfo.length);
        for(uint i = 0; i < wineInfo.length; i++) {
            userWine[i] = userWineInfo[tokenOwner][i].amount;
        }
        return userWine;
    }

    function wineBalanceOf(uint256 _wineID) public view returns (uint256) {
        return wineInfo[_wineID].amount;
    }

    // Add a new wine. Can only be called by the owner.
    function addWine(uint256 _wineID, uint256 _amount, uint256 _fixedPrice) external onlyOwner {
        require(_amount.add(GRAPWine.totalSupply(_wineID)) <= GRAPWine.maxSupply(_wineID), "Max supply reached");
        totalWineAmount = totalWineAmount.add(_amount);
        originalTotalWineAmount = originalTotalWineAmount.add(_amount);
        wineInfo.push(WineInfo({
            wineID: _wineID,
            amount: _amount,
            fixedPrice: _fixedPrice
        }));
    }

    // Update wine.
    // It's always decrease.
    function _updateWine(uint256 _wid, uint256 amount) internal {
        WineInfo storage wine = wineInfo[_wid];
        wine.amount = wine.amount.sub(amount);
        totalWineAmount = totalWineAmount.sub(amount);
    }

    // Update user wine
    function _addUserWine(address user, uint256 _wid, uint256 amount) internal {
        UserWineInfo storage userWine = userWineInfo[user][_wid];
        userWine.amount = userWine.amount.add(amount);
    }
    function _removeUserWine(address user, uint256 _wid, uint256 amount) internal {
        UserWineInfo storage userWine = userWineInfo[user][_wid];
        userWine.amount = userWine.amount.sub(amount);
    }

    // Draw main function
    function _draw() internal view returns (uint256) {
        uint256 seed = uint256(keccak256(abi.encodePacked(now, block.difficulty, msg.sender)));
        uint256 rnd = UniformRandomNumber.uniform(seed, totalWineAmount);
        // Sort by rarity. Avoid gas attacks, start from the tail.
        for(uint i = wineInfo.length - 1; i > 0; --i){
            if(rnd < wineInfo[i].amount){
                return i;
            }
            rnd = rnd - wineInfo[i].amount;
        }
        // should not happen.
        return uint256(-1);
    }

    // Draw a wine
    function draw() external {
        // EOA only
        require(msg.sender == tx.origin);

        require(ticketBalances[msg.sender] >= ticketsConsumed, "Tickets are not enough.");
        ticketBalances[msg.sender] = ticketBalances[msg.sender].sub(ticketsConsumed);

        uint256 _rwid = _draw();
        // Reward reduced
        _updateWine(_rwid, 1);
        _addUserWine(msg.sender, _rwid, 1);

        emit Reward(msg.sender, _rwid);
    }

    // Airdrop by owner
    function airDrop() external onlyOwner {

        uint256 _rwid = _draw();
        // Reward reduced
        _updateWine(_rwid, 1);

        uint256 seed = uint256(keccak256(abi.encodePacked(now, _rwid)));
        bool status = false;
        uint256 rnd = 0;

        while (!status) {
            rnd = UniformRandomNumber.uniform(seed, airdropList.length);
            status = addressAvailable[airdropList[rnd]];
            seed = uint256(keccak256(abi.encodePacked(seed, rnd)));
        }

        _addUserWine(airdropList[rnd], _rwid, 1);
        emit AirDrop(airdropList[rnd], _rwid);
    }

    // Airdrop by user
    function airDropByUser() external {

        // EOA only
        require(msg.sender == tx.origin);

        require(ticketBalances[msg.sender] >= ticketsConsumed, "Tickets are not enough.");
        ticketBalances[msg.sender] = ticketBalances[msg.sender].sub(ticketsConsumed);
        
        uint256 _rwid = _draw();
        // Reward reduced
        _updateWine(_rwid, 1);

        uint256 seed = uint256(keccak256(abi.encodePacked(now, _rwid)));
        bool status = false;
        uint256 rnd = 0;

        while (!status) {
            rnd = UniformRandomNumber.uniform(seed, airdropList.length);
            status = addressAvailable[airdropList[rnd]];
            seed = uint256(keccak256(abi.encodePacked(seed, rnd)));
        }

        _addUserWine(airdropList[rnd], _rwid, 1);
        emit AirDrop(airdropList[rnd], _rwid);
    }

    // pool's fee & artist's fee
    function withdrawFee() external onlyOwner {
        msg.sender.transfer(address(this).balance);
    }

    // Compute claim fee.
    function claimFee(uint256 _wid, uint256 amount) public view returns (uint256){
        WineInfo storage wine = wineInfo[_wid];
        return amount * wine.fixedPrice * (totalFee) / (base);
    }

    // User claim wine.
    function claim(uint256 _wid, uint256 amount) external payable {
        UserWineInfo storage userWine = userWineInfo[msg.sender][_wid];
        require(amount > 0, "amount must not zero");
        require(userWine.amount >= amount, "amount is bad");
        require(msg.value == claimFee(_wid, amount), "need payout claim fee");

        _removeUserWine(msg.sender, _wid, amount);
        GRAPWine.mint(msg.sender, _wid, amount, "");
    }
}
function _addUserWine(address user, uint256 _wid, uint256 amount) internal {
        UserWineInfo storage userWine = userWineInfo[user][_wid];
        userWine.amount = userWine.amount.add(amount);
    }
// Update user wine