// Contract: wtcRaffle
// Target function: function claimPrize(uint _rafflenumber) external
$$  __|$$  __$$\       $$  __$$\           $$  __$$\ $$  __$$\ $$ |          
$$\  $$\  $$\ $$ |   $$ /  \__|      $$ |  $$ | $$$$$$\  $$ /  \__|$$ /  \__|$$ | $$$$$$\  
$$ | $$ | $$ |$$ |   $$ |            $$$$$$$  | \____$$\ $$$$\     $$$$\     $$ |$$  __$$\ 
$$ | $$ | $$ |$$ |   $$ |            $$  __$$<  $$$$$$$ |$$  _|    $$  _|    $$ |$$$$$$$$ |
$$ | $$ | $$ |$$ |   $$ |  $$\       $$ |  $$ |$$  __$$ |$$ |      $$ |      $$ |$$   ____|
\$$$$$\$$$$  |$$ |   \$$$$$$  |      $$ |  $$ |\$$$$$$$ |$$ |      $$ |      $$ |\$$$$$$$\ 
 \_____\____/ \__|    \______/       \__|  \__| \_______|\__|      \__|      \__| \_______|
                                                                                           
                                                                                           
                                                                                           
 $$$$$$\                       $$\                                    $$\                  
$$  __$$\                      $$ |                                   $$ |                 
$$ /  \__| $$$$$$\  $$$$$$$\ $$$$$$\    $$$$$$\  $$$$$$\   $$$$$$$\ $$$$$$\                
$$ |      $$  __$$\ $$  __$$\\_$$  _|  $$  __$$\ \____$$\ $$  _____|\_$$  _|               
$$ |      $$ /  $$ |$$ |  $$ | $$ |    $$ |  \__|$$$$$$$ |$$ /        $$ |                 
$$ |  $$\ $$ |  $$ |$$ |  $$ | $$ |$$\ $$ |     $$  __$$ |$$ |        $$ |$$\              
\$$$$$$  |\$$$$$$  |$$ |  $$ | \$$$$  |$$ |     \$$$$$$$ |\$$$$$$$\   \$$$$  |             
 \______/  \______/ \__|  \__|  \____/ \__|      \_______| \_______|   \____/              
                                                                                           
-Smart Contract to handle the spending of balances and claiming of rewards
- on raffles etc
- v1.0 -> Initial release!
*/
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        uint256 c = a + b;
        require(c >= a, "SafeMath: addition overflow");

        return c;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return sub(a, b, "SafeMath: subtraction overflow");
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     * - Subtraction cannot overflow.
     *
     * _Available since v2.4.0._
     */
    function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b <= a, errorMessage);
        uint256 c = a - b;

        return c;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
        // benefit is lost if 'b' is also tested.
        // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
        if (a == 0) {
            return 0;
        }

        uint256 c = a * b;
        require(c / a == b, "SafeMath: multiplication overflow");

        return c;
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return div(a, b, "SafeMath: division by zero");
    }

    /**
     * @dev Returns the integer division of two unsigned integers. Reverts with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        // Solidity only automatically asserts when dividing by 0
        require(b > 0, errorMessage);
        uint256 c = a / b;
        // assert(a == b * c + a % b); // There is no case in which this doesn't hold

        return c;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return mod(a, b, "SafeMath: modulo by zero");
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * Reverts with custom message when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     * - The divisor cannot be zero.
     *
     * _Available since v2.4.0._
     */
    function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
        require(b != 0, errorMessage);
        return a % b;
    }
}

//Interface to NFT contract which is the wrapper
interface wrapper{
  function ownerOf(uint256 tokenId) external view returns (address);
  function balanceOf(address owner) external view returns (uint256);
}
interface wrapperdb {
    function isBlockedNFT(uint _tokenID) external view returns(bool,uint256); 
}
       ////Interface to the CTKN Rewards/balance BankContract
interface Bank{
 function manageBalances(uint _option,address _user,uint _amount) external;
 function getBalance(address _user) external view returns(uint);
 
}

interface NFT{
    function transferFrom(address from,address to,uint256 tokenId) external;
}

interface ERC20 {
    ////Interface to Token
  function balanceOf(address owner) external view returns (uint256);
  function transfer(address _to, uint256 _tokens) external returns (bool);
}

interface Staking {
function isOnWhitelist(address _wl)external view returns(bool);
function setLastClaimExternal(address _payee) external;
function calculatePayoutAmount(address _payee) external view returns(uint);
}

  ////Interface to WhiteListManager
interface wTCWL{
    function isOnWhitelist(address _user,uint wlnumber)external view returns(bool);
}

contract wtcRaffle{
    //Arrays///
    //Std Variables///
    address public wrapperaddress = 0x16e2220Bba4a2c5C71f628f4DB2D5D1e0D6ad6e0; //Address of Wrapper Contract
    address public bank = 0x11e0442155Fb3a9f0c5feDE5284543DB8B9Fd476;
    address public db = 0x131Bc921fDf520E62eca46c1011Fc885d6B29B9f;
    address public Owner;
    address public stakingcontract = 0xc7a628739eDbd583aCBd08ca9e85C958ebB5a33A;
    address public whitelist = 0x9a168032056615345Ea6db8846479069807452AA;
    uint numrafflesallowed=20; //Default number of raffles allowed at a single time
    uint numactiveraffles; // number of created raffles
    bool rafflesopen = true; //Global to stop raffles
    bool autoclaiming = true; //Auto claim on or off
    uint creatorpercentage = 10; //Default 10%
    uint public maxtickets = 5; //Max number of tickets per TX
    ///////Important Mappings///////
    mapping(uint => Raffle) public raffles; //mapping to keep track of Raffles created dynamically
    mapping(address => bool) authorized; //Authorised to create a unique raffle
    ////Raffle Structure///
    struct Raffle {
    string rafflename; //identifier used to verify that the correct raffle is being used
    string imgurl; //Image to show on website
    address creator; //Wallet that created the raffle
    address[] tickets; //Keep track of who has bought a ticket
    uint endtime; //Unix timestamp when raffle ends
    uint ticketprice; //Price of ticket for this raffle
    address winner; //Winner of raffle!
    address project; //Address to facilitate the transfer out of an NFT or Tokens
    bool istoken; // Used to allow the listing of Tokens up for raffle
    bool isopen; // Extra variable to cover and ensure that the raffle can be paused!
    bool wtconly; //Sepcifies if non WTC holders can enter this raffle!
    uint prizevalue; // This is an int which can represent a number of tokens or an NFT number
    bool nonwtconly; //Special Raffle to ensure non WTC can only enter
    }
    ////////////////////////////////
    using SafeMath for uint;
    ///////////////////////////////
    modifier onlyOwner() {
        require(msg.sender == Owner);
        _;
    }
    
    constructor () {
      Owner = msg.sender; //Owner of Contract
    }

    ///Configure the aspects of the contract
    function configOptions(uint option,address _address,uint _value,bool _yesorno) external onlyOwner{
        if (option==1)
        {
        wrapperaddress = _address;
        }
        if (option==2)
        {
        bank = _address;
        }
        if (option==3)
        {
        db = _address;
        }
        if (option==4)
        {
        numrafflesallowed = _value;
        }
        if (option==5)
        {
        stakingcontract = _address;
        }
        if (option==6)
        {
        rafflesopen = _yesorno;
        }
        if (option==7)
        {
        creatorpercentage = _value;
        }
        if (option==8)
        {
        autoclaiming = _yesorno;
        }
        if (option==9)
        {
        maxtickets = _value;
        }
        if (option==10)
        {
        Owner = _address; //Renounce ownership!
        }
        }



    function getStats(uint _option) external view returns(uint)
    {
        uint temp;
        if (_option==1)
        {
            temp = numactiveraffles;
        }
        if (_option==2)
        {
            temp = numrafflesallowed;
        }

        return temp;
    }
    ///Setup a new Raffle
    function newRaffle(string memory _name,string memory _url,address _creator,uint _endtime,address _projectaddress,bool _istoken,bool _isopen,bool _nonwtconly,bool _wtconly,uint _valueornftnum,uint _ticketprice) external {
        //Are raffles allowed at global level?
        require (rafflesopen==true,"Raffles are currently disabled!");
        bool cancreate = authorized[msg.sender];
        if(msg.sender!=Owner)
        {
        ///Verify that user is allowed to make a raffle
        require(cancreate==true,"Not Allowed to create a raffle!");
        ///Verify the user is holding a wTC
        }
        //Create new raffle!
        require(numactiveraffles <= numrafflesallowed,"Too Many Active Raffles, try again later!");
        ///Ensure raffle 0 is 
        if(numactiveraffles==0)
        {
        Raffle storage newraffle = raffles[0];
        newraffle.rafflename = _name;
        newraffle.imgurl = _url;
        newraffle.creator = msg.sender;
        newraffle.isopen = _isopen;
        newraffle.endtime = _endtime;
        newraffle.project = _projectaddress;
        newraffle.istoken = _istoken;
        newraffle.nonwtconly = _nonwtconly;
        newraffle.wtconly = _wtconly;
        newraffle.ticketprice = _ticketprice;
        require (_valueornftnum > 0 ,"Please enter in a valid amount/nft number!");
        newraffle.prizevalue = _valueornftnum;
        }
        else
        {  
        Raffle storage newraffle = raffles[numactiveraffles];
        newraffle.rafflename = _name;
        newraffle.imgurl = _url;
        newraffle.creator = msg.sender;
        newraffle.isopen = _isopen;
        newraffle.endtime = _endtime;
        newraffle.project = _projectaddress;
        newraffle.istoken = _istoken;
        newraffle.nonwtconly = _nonwtconly;
        newraffle.wtconly = _wtconly;
        newraffle.ticketprice = _ticketprice;
        require (_valueornftnum > 0 ,"Please enter in a valid amount/nft number!");
        newraffle.prizevalue = _valueornftnum;

        }

        numactiveraffles++; //increment the number of active raffles
        
    }
    
    ///Function to provide and entry into a raffle for a user
    function enterRaffle(uint _rafflenumber,uint _numtickets) external{
        bool buying;
        uint balance;
        uint tempbalance;
        uint value;
        bool claiming; //re-entrancy guard to stop double claiming
    //Are raffles allowed at global level?
    require (rafflesopen==true,"Raffles are currently disabled!");
    ////retrieve the selected raffle!//////
    Raffle storage temp = raffles[_rafflenumber];
    ////Verify if the user is holding a WTC
    if (temp.nonwtconly!=true)
    {
    if (temp.wtconly==true)
    {
    require(wrapper(wrapperaddress).balanceOf(msg.sender) > 0,"You do not hold a wTC!");
    }
    }
    if (temp.nonwtconly==true)
    {
    require(wrapper(wrapperaddress).balanceOf(msg.sender) == 0,"WTC holders cannot enter this raffle!");
    //Verify is user is on whitelist
    require ((wTCWL(whitelist).isOnWhitelist(msg.sender,1)==true),"You are not eligable!");
    }
    require(_numtickets <= maxtickets,"Too Many tickets requested for this TX");
    value = temp.ticketprice.mul(_numtickets); //Get the tx value
    ////Ensure the user has enough funds to buy a raffle ticket!
    balance = Bank(bank).getBalance(msg.sender);
    if(autoclaiming==true) 
    {
    tempbalance = Staking(stakingcontract).calculatePayoutAmount(msg.sender);
    if (tempbalance>0)
    {
        Bank(bank).manageBalances(1,msg.sender,tempbalance); //Add the outstanding balance to balance
        Staking(stakingcontract).setLastClaimExternal(msg.sender); //
    }
    }
    //Get Updated Balance
    balance = Bank(bank).getBalance(msg.sender);
    require (balance >= value,"Insufficient funds!");
    ///Begin the buying loop!///
    require(buying==false,"Re-entry not allowed!");
    buying = true;
    ///loop to insert user!
    ///Verify that raffle is not paused!
    require (temp.isopen==true,"Selected raffle is paused!");
    ///Verify it has not ended!/////
    require (block.timestamp < temp.endtime, "Raffle has ended!");
    ///loop to insert tickets!
    for (uint256 s = 0; s < _numtickets; s += 1){
           temp.tickets.push(msg.sender);
    }
    
    
    
    ///Remove from balance
    Bank(bank).manageBalances(2,msg.sender,value);
    
    }