// Contract: Duorice
// Target function: function calcStakeCount(address _address) public view returns (uint256)
256 duration
    );

    event UserStakeCollect(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event UserLobby(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event UserLobbyCollect(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount
    );

    event stake_sell_request(
        address indexed addr,
        uint256 timestamp,
        uint256 price,
        uint256 rawAmount,
        uint256 stakeId
    );

    event stake_loan_request(
        address indexed addr,
        uint256 timestamp,
        uint256 rawAmount,
        uint256 duration,
        uint256 stakeId
    );

    event stake_lend(
        address indexed addr,
        uint256 timestamp,
        uint256 stakeId
    );

    event stake_loan(
        address indexed addr,
        uint256 timestamp,
        uint256 stakeId,
        uint256 value
    );

    event day_lobby_entry(
        uint256 timestamp,
        uint256 day,
        uint256 value
    );

				   
										  
	 
	

    /* Address of flush accs */
    address internal constant avariceTeam_addr = 0x8136046f7eCbaAEB675A658772Aa2594A8D4b254; // 
																									 
																																																 
    address internal marketing_addr = 0x7D8C5B47F8227568Be66237031E0b398fF702c48;
    address internal buyBack_addr = 0x90F85C51CCE45b5bcE6A8036e1D23ce9BBEdea81;

																															  
												  

    /* % from every day's lobby entry dedicated to avarice team, marketing and buy back */
    uint256 internal constant DM_avariceTeam_percentage = 5;      
    uint256 internal constant DM_marketing_percentage = 1;      
    uint256 internal constant DM_buyBack_percentage = 1;      

															   
																   

														   
													   

    /* Max staking days */
    uint256 internal constant max_stake_days = 300;  

    /* Ref bonus NR, ex. 20 = 5% */
    uint256 internal constant ref_bonus_NR = 20;  

    /* Refered person bonus NR, ex. 100 = 1% */
    uint256 internal constant ref_bonus_NRR = 100;  

    /* dividends pool caps at 60 days, meaning that the lobby entery of days > 60 will only devide for next 60 days and no more */
    uint256 internal constant dividendsPoolCapDays = 60;  

    /* Allowing the dev to flush the first lobby day */
    bool public firstDayFlushed = false;  

    /* Loaning feature is paused? */
    bool public loaningIsPaused = true; 

    /* Stake selling feature is paused? */
    bool public stakeSellingIsPaused = true; 

    uint256 public constant defaultLobby = 3000000 * 1e18;

    /* ------------------ for the sake of UI statistics ------------------ */
    // lobby memebrs overall data 
    struct memberLobby_overallData{
        uint256 overall_collectedTokens;
        uint256 overall_lobbyEnteries;
        uint256 overall_stakedTokens;
        uint256 overall_collectedDivs;
    }
    // new map for every user's overall data  
    mapping(address => memberLobby_overallData) public mapMemberLobby_overallData;
    // total lobby entry  
    uint256 public overall_lobbyEntry;
    // total staked tokens  
    uint256 public overall_stakedTokens;
    // total lobby token collected  
    uint256 public overall_collectedTokens;
    // total stake divs collected  
    uint256 public overall_collectedDivs;
    // total bonus token collected  
    uint256 public overall_collectedBonusTokens;
    // total referrer bonus paid to an address  
    mapping(address => uint256) public referrerBonusesPaid;
    // counting unique (unique for every day only) lobby enteries for each day
    mapping(uint256 => uint256) public usersCountDaily;
    // counting unique (unique for every day only) users
    uint256 public usersCount = 0;
    /* Total ever entered as stake tokens */ 
    uint256 public saveTotalToken;
    /* ------------------ for the sake of UI statistics ------------------ */


    /* lobby memebrs data */ 
    struct memberLobby{
        uint256 memberLobbyValue;
									
        bool hasCollected;
        address referrer;
    }

    /* new map for every entry (users are allowed to enter multiple times a day) */ 
    mapping(address => mapping(uint256 => memberLobby)) public mapMemberLobby;

    /* day's total lobby entry */ 
    mapping(uint256 => uint256) public lobbyEntry;          


    /* User stakes struct */ 
    struct memberStake {
        address userAddress;
        uint256 tokenValue;
        uint256 startDay;
        uint256 endDay;
        uint256 stakeId;
        uint256 price; // use: sell stake
        uint256 loansReturnAmount; // total of the loans return amount that have been taken on this stake 
        bool stakeCollected;
        bool stake_hasSold; // stake been sold ?
        bool stake_forSell; // currently asking to sell stake ?
        bool stake_hasLoan; // is there an active loan on stake ?
        bool stake_forLoan; // currently asking for a loan on the stake ?
    }

    /* A map for each user */ 
    mapping(address => mapping(uint256 => memberStake)) public mapMemberStake;

    /* Total active tokens in stake for a day */ 
    mapping(uint256 => uint256) public daysActiveInStakeTokens;
    mapping(uint256 => uint256) public daysActiveInStakeTokensIncrese;
    mapping(uint256 => uint256) public daysActiveInStakeTokensDecrase;

    /* Time of contract launch */
    uint256 public LAUNCH_TIME;   
    uint256 currentDay;
    bool public launched;

    constructor() {
        _mint(msg.sender, defaultLobby);
        LAUNCH_TIME = block.timestamp.add(180 days);
        launched = false;
    }

    function launch() public onlyOwner(){
        require(launched == false,"contract already launched!");
		LAUNCH_TIME = block.timestamp.sub(1 days);
        launched = true;
    } 

    /* Owner switching the loaning feature status */
    function switchLoaningStatus() external onlyOwner() {
        if (loaningIsPaused == true) {
            loaningIsPaused = false;
        } 
        else if (loaningIsPaused == false) {
            loaningIsPaused = true;
        }
    }
    
    /* Owner switching the stake selling feature status */
    function switchStakeSellingStatus() external onlyOwner() {
        if (stakeSellingIsPaused == true) {
            stakeSellingIsPaused = false;
        } 
        else if (stakeSellingIsPaused == false) {
            stakeSellingIsPaused = true;
        }
    }

    /* Flushed the 1st day's lobby entry to dev address (since there could be no users with tokens on day 1 and therefor no active stakes) */ 
    function flushFirstDayLobbyEntry(uint256 at) external onlyOwner() nonReentrant {
        require(firstDayFlushed == false);
        firstDayFlushed = true;

        payable(avariceTeam_addr).transfer((lobbyEntry[at] * 93) /100);  
    }

    /* turning off the first day flush functionality */ 
      function flushFirstDayLobbyEntrySwitch() external onlyOwner() {
        if (firstDayFlushed == true) {
            firstDayFlushed = false;
        } 
        else if (firstDayFlushed == false) {
            firstDayFlushed = true;
        }
    }
      
    
    /* change marketing wallet address % */ 
    function do_changeMarketingAddress(address adr) external onlyOwner() {
        marketing_addr = adr;
    }

    /**
     * @dev flushes the dev share from stake sells
     */
    function flushdevShareOfStakeSells() external onlyOwner() nonReentrant {
        require(devShareOfStakeSellsAndLoanFee > 0);
        
        payable(marketing_addr).transfer(devShareOfStakeSellsAndLoanFee);
        devShareOfStakeSellsAndLoanFee = 0;
    }


								 
														  
					   

    function _clcDay() public view returns (uint256) {
        return (block.timestamp - LAUNCH_TIME) / (1 days);
    }

    function _updateDaily() public {
        if(currentDay == _clcDay()){
            return;
        }
        // this is true once a day
        
        if (currentDay < dividendsPoolCapDays) {
            for(uint256 _day = currentDay + 1 ; _day <= currentDay * 2 ; _day++){
                dayBNBPool[_day] += (lobbyEntry[currentDay] * 93 ) / (currentDay * 100);
            }

													
																					 
																							
				 

        } else {
            for(uint256 _day = currentDay + 1 ; _day <= currentDay + dividendsPoolCapDays ; _day++){
                dayBNBPool[_day] += (lobbyEntry[currentDay] * 93 ) / (dividendsPoolCapDays * 100);
				 
            }
        }
								   
							   

        currentDay = _clcDay();
							
								  
								  
											   
									

        // total of 7% from every day's lobby entry goes to:
        _sendDevShare();
        // 1% marketing share 
        _sendMarketingShare();
        // 1% buy back to current lobby day
        _buyLobbyBuybackShare();
	 

        emit day_lobby_entry(
            block.timestamp,
            currentDay,
            lobbyEntry[currentDay -1]
        );
        
    }

    /* Gets called once a day and withdraws avarice team's share for the privious day of lobby */ 
    function _sendDevShare() internal nonReentrant {
        require(currentDay > 0);

        // avariceTeamPercentage = 5% of every day's lobby entry
        uint256 avariceTeamPercentage = (lobbyEntry[currentDay - 1] * DM_avariceTeam_percentage) /100;
    
								
        payable(avariceTeam_addr).transfer(avariceTeamPercentage);
																				
																				
																				
    }

    /* Gets called once a day and withdraws marketing's share for the privious day of lobby */ 
    function _sendMarketingShare() internal nonReentrant {
        require(currentDay > 0);

        // marketing share = 1% of every day's lobby entry
        payable(marketing_addr).transfer((lobbyEntry[currentDay - 1] * DM_marketing_percentage) /100);
    }

    /* Gets called once a day and withdraws buy back share for the privious day of lobby */ 
    function _buyLobbyBuybackShare() internal nonReentrant {
        require(currentDay > 0);

        // buyback share = 1% of every day's lobby entry
        payable(buyBack_addr).transfer((lobbyEntry[currentDay - 1] * DM_buyBack_percentage) /100);
    }




    /**
     * @dev External function for entering the auction lobby for the current day
     * @param referrerAddr address of referring user (optional; 0x0 for no referrer)
     */
    function EnterLobby(address referrerAddr) external payable {
        uint256 rawAmount = msg.value;
        require(rawAmount > 0, "ERR: Amount required");

        _updateDaily();
        require(currentDay > 0);
    
        if (mapMemberLobby[msg.sender][currentDay].memberLobbyValue == 0) {
            usersCount++;
            usersCountDaily[currentDay]++;
        }

        mapMemberLobby_overallData[msg.sender].overall_lobbyEnteries += rawAmount;
        lobbyEntry[currentDay] += rawAmount;
        overall_lobbyEntry += rawAmount;

																				  
        mapMemberLobby[msg.sender][currentDay].memberLobbyValue += rawAmount; 
																				
        mapMemberLobby[msg.sender][currentDay].hasCollected = false;

        if (referrerAddr != msg.sender) {
            /* No Self-referred */
            mapMemberLobby[msg.sender][currentDay].referrer = referrerAddr;
        } else {
            mapMemberLobby[msg.sender][currentDay].referrer = address(0);
        }
        
        emit UserLobby(
            msg.sender, 
            block.timestamp,
            rawAmount
        );
    }
    

    /**
     * @dev External function for leaving the lobby / collecting the tokens
     * @param targetDay Target day of lobby to collect 
     */
    function ExitLobby(uint256 targetDay) external {
        require(mapMemberLobby[msg.sender][targetDay].hasCollected == false, "ERR: Already collected");
        _updateDaily();
        require(targetDay < currentDay);

        uint256 tokensToPay = _clcTokenValue(msg.sender, targetDay);

        _mint(msg.sender, tokensToPay);
        mapMemberLobby[msg.sender][targetDay].hasCollected = true;
        
        overall_collectedTokens += tokensToPay;
        mapMemberLobby_overallData[msg.sender].overall_collectedTokens += tokensToPay;

        address referrerAddress = mapMemberLobby[msg.sender][targetDay].referrer;
        if (referrerAddress != address(0)) {
            /* there is a referrer, pay their % ref bonus of tokens */ 
            uint256 refBonus = tokensToPay /ref_bonus_NR;

            _mint(referrerAddress, refBonus);  
            referrerBonusesPaid[referrerAddress] += refBonus;

            /* pay the referred user bonus */
            _mint(msg.sender, tokensToPay /ref_bonus_NRR); 
        }

        emit UserLobbyCollect(
            msg.sender, 
            block.timestamp,
            tokensToPay
        );
    }


    /**
     * @dev Calculating user's share from lobby based on their entry value
     * @param _Day The lobby day
     */
    function _clcTokenValue (address _address, uint256 _Day) public view returns (uint256) {
        require(_Day != 0, "ERR");
        uint256 _tokenVlaue;
																			  


        if(_Day != 0 && _Day < currentDay) {
            _tokenVlaue = (tokenForDay(_Day) / lobbyEntry[_Day]) * mapMemberLobby[_address][_Day].memberLobbyValue; 
        }else{
            _tokenVlaue = 0;
        }
        
        return _tokenVlaue;
    }

    function tokenForDay(uint256 _day) public view returns (uint256 value_) {
        if(_day < 1){
            value_ = 0;
        } else{
            _day = _day - 1;
            if(_day > max_stake_days){
                _day = max_stake_days;
            }
            value_ = defaultLobby;
            for(uint i=0;i<_day;i++){
                value_ = value_.add(value_.mul(2).div(100));
            }
        }
    }

    mapping(uint256 => uint256)public dayBNBPool;
    mapping(uint256 => uint256)public enterytokenMath;
    mapping(uint256 => uint256)public totalTokensInActiveStake;
   
    /**
     * @dev External function for users to create a stake
     * @param amount Amount of AVC tokens to stake
     * @param stakingDays Stake duration in days
     */

    
    function EnterStake(uint256 amount, uint256 stakingDays) external {
        require(stakingDays >= 1, 'Staking: Staking days < 1');
        require(stakingDays <= max_stake_days, 'Staking: Staking days > max_stake_days');
        require(amount > 0, "Staking: Amount required");
        require(balanceOf(msg.sender) >= amount, 'Not enough balance');
        
        _updateDaily();
        uint256 stakeId = calcStakeCount(msg.sender);

        overall_stakedTokens += amount;
        mapMemberLobby_overallData[msg.sender].overall_stakedTokens += amount;

        mapMemberStake[msg.sender][stakeId].stakeId = stakeId;
        mapMemberStake[msg.sender][stakeId].userAddress = msg.sender;
        mapMemberStake[msg.sender][stakeId].tokenValue = amount;
        mapMemberStake[msg.sender][stakeId].startDay = currentDay + 1 ;
        mapMemberStake[msg.sender][stakeId].endDay = currentDay + 1 + stakingDays;
        mapMemberStake[msg.sender][stakeId].stakeCollected = false;
        mapMemberStake[msg.sender][stakeId].stake_hasSold = false;
        mapMemberStake[msg.sender][stakeId].stake_hasLoan = false;
        mapMemberStake[msg.sender][stakeId].stake_forSell = false;
        mapMemberStake[msg.sender][stakeId].stake_forLoan = false;
        // stake calcs for days: X >= startDay && X < endDay
        // startDay included / endDay not included

        for (uint256 i = currentDay + 1; i <= currentDay + stakingDays; i++) {
            totalTokensInActiveStake[i] += amount;
        }

        saveTotalToken += amount;
        daysActiveInStakeTokensIncrese[currentDay + 1] += amount;
        daysActiveInStakeTokensDecrase[currentDay + stakingDays + 1] += amount;

        /* On stake AVC tokens get burned */
        _burn(msg.sender, amount);

        emit UserStake (
            msg.sender, 
            block.timestamp,
            amount, 
            stakingDays
        );
    }