contract WizarDAO is ERC721 {
    address private _owner = 0x5c2B89CBeC1a4996Aa5e81f3dFf8505ABeC6B34c;
    uint256 public tokenCounter = 0;
    bool public claimStatus = false;
    bool public pixavotingopen = false;
    bool public ethvotingopen = false;
    bool public daoLock = true; // if false, PixaLyfe still has control.
    bool public emergLock = true; // shuts down all functionality with DAO. Only works if daoLock is true. false = locked down.
    address payable wallet = 0x5c2B89CBeC1a4996Aa5e81f3dFf8505ABeC6B34c;
    address public wizardaddress = 0xc6b0B290176AaaB958441Dcb0c64ad057cBc39a0;
    address public pixaaddress = 0xeaf211cD484118a23AD71C3F9073189C43d1311c;
    address public rewardsContract = 0x717e63BF905fF0f433333B6f0a289b66462Cd8a1;
    address public pixarecipient;
    address payable pixainitiator;
    address payable ethinitiator;
    address payable ethrecipient;
    uint256 public pixaamount;
    uint256 public ethamount;
    string public pixareason;
    string public ethreason;
    uint256 public pixavotecount = 0;
    uint256 public ethvotecount = 0;
    uint256 public pixavoteamount = 10000;
    uint256 public ethvoteamount = 10000;
    string public wizardaoURI = "ipfs://QmTx79NMtkvK1HDnQT8noe1gZAHffvK24jT6MaUg6DPTx4";
    uint256 public burnAmount = 500; // this is the amount of $PIXA initially burned weekly from the WizarDAO wallet. DAO token owners can adjust this once per week.
    uint256 public rewardAmount = 2000; // amount of $PIXA leaving the WizarDAO wallet to the rewards contract. DAO token owners can adjust this once per week.
    uint256 public burnCap = 21;
    uint256 public rewardCap = 21;
    uint256 public txnReward = 1000;
    uint256 public newTimeFrame = 0;
    uint256 public newRewardsTimeFrame = 0;
    uint256 public pixatimestamp = 0;
    uint256 public ethtimestamp = 0;
    uint256 public pixacooldown = 0;
    uint256 public ethcooldown = 0;
    uint256 public weekly = 604800;
    mapping(uint256 => bool) public wizlist;
    mapping(uint256 => uint256) public burnvote;
    mapping(uint256 => uint256) public transvote;
    mapping(uint256 => uint256) public ethtransvote;
    mapping(uint256 => uint256) public rewardvote;
    constructor () public ERC721 ("The WizarDAO", "WAO"){
    }

    function claimTokenLoop() public {
        require(claimStatus);
        PixaWizardsContract nfts = PixaWizardsContract(wizardaddress);
        uint256 numNFTs = nfts.balanceOf(msg.sender);
        require(numNFTs > 0);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 wizId = nfts.tokenOfOwnerByIndex(msg.sender, i);
            if (wizlist[wizId]) {
                continue;
            }
            wizlist[wizId] = true;
            _safeMint(msg.sender, tokenCounter);
            _setTokenURI(tokenCounter, wizardaoURI);
            tokenCounter = tokenCounter + 1;
        }
    }

    function claimToken() public {
        require(claimStatus);
        PixaWizardsContract nfts = PixaWizardsContract(wizardaddress);
        uint256 numNFTs = nfts.balanceOf(msg.sender);
        require(numNFTs > 0);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 wizId = nfts.tokenOfOwnerByIndex(msg.sender, i);
            if (wizlist[wizId]) {
                continue;
           }
            wizlist[wizId] = true;
            i = i+numNFTs;
            _safeMint(msg.sender, tokenCounter);
            _setTokenURI(tokenCounter, wizardaoURI);
            tokenCounter = tokenCounter + 1;
        }
    }


    // burn rate //////////////////////////////////////////

    function weeklyBurn() public {
        require(block.timestamp > newTimeFrame);
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        PixaTokenContract pixa = PixaTokenContract(pixaaddress);
        pixa.burn(burnAmount);
        pixa.transfer(msg.sender, txnReward);
        newTimeFrame = block.timestamp + weekly;
    }

    function burnVoting(uint256 personalBurn) public {
        require(personalBurn > 0);
        require(personalBurn < burnCap);
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        uint256 numNFTs = balanceOf(msg.sender);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 daoId = tokenOfOwnerByIndex(msg.sender, i);
            if (burnvote[daoId] > block.timestamp) {
                continue;
            }
            burnvote[daoId] = block.timestamp + weekly;

            if (burnAmount > 200) {
                burnAmount = personalBurn + burnAmount - 10;
            } else {
                burnAmount = burnAmount + 10;
            }
        }
    }

    // Rewards rate //////////////////////////////////////////////

    function weeklyRewards() public {
        require(block.timestamp > newRewardsTimeFrame);
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        PixaTokenContract pixa = PixaTokenContract(pixaaddress);
        pixa.transfer(rewardsContract, rewardAmount);
        pixa.transfer(msg.sender, txnReward);
        newRewardsTimeFrame = block.timestamp + weekly;
    }

    function rewardsVoting(uint256 personalRewards) public {
        require(personalRewards > 0);
        require(personalRewards < rewardCap);
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        uint256 numNFTs = balanceOf(msg.sender);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 daoId = tokenOfOwnerByIndex(msg.sender, i);
            if (rewardvote[daoId] > block.timestamp) {
                continue;
            }
            rewardvote[daoId] = block.timestamp + weekly;

            if (rewardAmount > 200) {
                rewardAmount = personalRewards + rewardAmount - 10;
            } else {
                rewardAmount = rewardAmount + 10;
            }
        }
    }

    // PIXA transfer voting /////////////////////////////////////

    function proposeTransferPixa(address payable recipient, uint256 amount, string memory reason) public payable {
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        require(msg.value == 100000000000000000);
        require(pixacooldown < block.timestamp);

        pixaamount = amount;
        pixarecipient = recipient;
        pixareason = reason;
        pixainitiator = msg.sender;
        pixatimestamp = block.timestamp + weekly; // 1 week minimum voting period
        pixavotingopen = true; // flip toggle to open voting
    }

    function votingTransferPixa(bool tvote) public {
        require(emergLock);
        require(pixavotingopen);
        require(balanceOf(msg.sender) > 0);
        uint256 numNFTs = balanceOf(msg.sender);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 daoId = tokenOfOwnerByIndex(msg.sender, i);
            if (transvote[daoId] > block.timestamp) {
                continue;
            }
            transvote[daoId] = block.timestamp + weekly;
            pixavotecount = pixavotecount + 1;

            if (tvote) {
                pixavoteamount = pixavoteamount + 1;
            }
            else {
                pixavoteamount = pixavoteamount - 1;
            }
        }
    }

    function initiateTransferPixa() public {
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        require(pixavotecount > 100);
        require(block.timestamp > pixatimestamp);
        require(100000000000000000 <= getBalance()); // make sure enough eth to pay back initiator
        
        if (pixavoteamount > 10000) {
            PixaTokenContract pixa = PixaTokenContract(pixaaddress);
            pixa.transfer(pixarecipient, pixaamount);
            pixa.transfer(pixainitiator, 500);//bonus pixa for successful vote
            pixa.transfer(msg.sender, 100) ; //bonus for ending the vote   
            pixainitiator.transfer(100000000000000000); //return eth for successful vote                

            pixavoteamount = 10000;
            pixavotecount = 0;
            pixavotingopen = false;
            pixacooldown = block.timestamp + weekly; // week long cool down between votes
            pixareason = "blank";

        }
        else {
            PixaTokenContract pixa = PixaTokenContract(pixaaddress);
            pixa.transfer(msg.sender, 100); // bonus for ending the vote

            pixavoteamount = 10000;
            pixavotecount = 0;
            pixavotingopen = false;
            pixacooldown = block.timestamp + weekly; // week long cool down between votes
            pixareason = "blank";
        } 

    }

    // ETH transfer voting /////////////////////////////////////////////////

    function proposeTransferEth(address payable recipient, uint256 amount, string memory reason) public payable {
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        require(msg.value == 100000000000000000);
        require(ethcooldown < block.timestamp);

        ethamount = amount;
        ethrecipient = recipient;
        ethreason = reason;
        ethinitiator = msg.sender;
        ethtimestamp = block.timestamp + weekly; // 1 week minimum voting period
        ethvotingopen = true; // flip toggle to open voting
    }

    function votingTransferEth(bool tvote) public {
        require(emergLock);
        require(ethvotingopen);
        require(balanceOf(msg.sender) > 0);
        uint256 numNFTs = balanceOf(msg.sender);
        for (uint256 i = 0; i < numNFTs; i++) {
            uint256 daoId = tokenOfOwnerByIndex(msg.sender, i);
            if (ethtransvote[daoId] > block.timestamp) {
                continue;
            }
            ethtransvote[daoId] = block.timestamp + weekly;
            ethvotecount = ethvotecount + 1;

            if (tvote) {
                ethvoteamount = ethvoteamount + 1;
            }
            else {
                ethvoteamount = ethvoteamount - 1;
            }
        }
    }

    function initiateTransferEth() public {
        require(emergLock);
        require(balanceOf(msg.sender) > 0);
        require(ethvotecount > 100);
        require(block.timestamp > ethtimestamp);
        require(100000000000000000 <= getBalance()); // make sure enough eth to pay back initiator
        
        if (ethvoteamount > 10000) {
            PixaTokenContract pixa = PixaTokenContract(pixaaddress);
            ethrecipient.transfer(ethamount);
            pixa.transfer(ethinitiator, 500);//bonus pixa for successful vote
            pixa.transfer(msg.sender, 100) ; //bonus for ending the vote   
            ethinitiator.transfer(100000000000000000); //return eth for successful vote                

            ethvoteamount = 10000;
            ethvotecount = 0;
            ethvotingopen = false;
            ethcooldown = block.timestamp + weekly; // week long cool down between votes
            ethreason = "blank";

        }
        else {
            PixaTokenContract pixa = PixaTokenContract(pixaaddress);
            pixa.transfer(msg.sender, 100); // bonus for ending the vote

            ethvoteamount = 10000;
            ethvotecount = 0;
            ethvotingopen = false;
            ethcooldown = block.timestamp + weekly; // week long cool down between votes
            ethreason = "blank";
        } 

    }

    // Allow access to other WizarDAO contracts to DAO eth and $PIXA supply /////////////////////////////////

    function setRewardsContract(address newcontract) public {
        require(daoLock);
        require(msg.sender == wallet);
        rewardsContract = newcontract;
    }

    function changeDaoURI(string memory inputURI) public {
        require(daoLock);
        require(msg.sender == wallet);
        wizardaoURI = inputURI;
    }

    function startClaim() public {
        require(daoLock);
        require(msg.sender == wallet);
        claimStatus = true;
    }

    function stopClaim() public {
        require(daoLock);
        require(msg.sender == wallet);
        claimStatus = false;
    }
        
    function engageEmergLock() public {
        require(daoLock);
        require(msg.sender == wallet);
        emergLock = false;
    }

    function stopEmergLock() public {
        require(daoLock);
        require(msg.sender == wallet);
        emergLock = true;
    }

    function lockDAO() public {
        require(msg.sender == wallet);
        daoLock = false;
    }

    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }

    function withdrawAmount(uint256 amount, uint256 transferpamount) public {
        require(daoLock);
        require(emergLock);
        require(msg.sender == wallet);
        require(amount <= getBalance());
        msg.sender.transfer(amount);
        PixaTokenContract pixa = PixaTokenContract(pixaaddress);
        pixa.transfer(msg.sender, transferpamount);
    }    

    function owner() public view virtual returns (address) {
        return _owner;
    }

    function _setOwner(address newOwner) private {
        require(msg.sender == wallet);
        address oldOwner = _owner;
        _owner = newOwner;
    }

    receive() external payable {
    }

}
function proposeTransferPixa(address payable recipient, uint256 amount, string memory reason) public payable {
    require(emergLock);
    require(balanceOf(msg.sender) > 0);
    require(msg.value == 100000000000000000);
    require(pixacooldown < block.timestamp);

    pixaamount = amount;
    pixarecipient = recipient;
    pixareason = reason;
    pixainitiator = msg.sender;
    pixatimestamp = block.timestamp + weekly; // 1 week minimum voting period
    pixavotingopen = true; // flip toggle to open voting
}

// PIXA transfer voting /////////////////////////////////////