contract EthernetPeeps is ERC721Enumerable, Ownable {
    using SafeMath for uint;
    using Strings for uint;
    using Address for address;

    uint public price;
    uint public supplyCap; // Limited to 9900
    uint public buyLimit;
    uint public presalereservedLimit;
    uint public saleState = 0; // 0 = paused, 1 = presale, 2 = live
    uint public constant max_peeps = 10000; // 100 - reserved for community actions, givaways and team
    mapping (address => uint256) public preSaleReserved; 

    string private _baseURIPrefix;

    constructor (
        string memory _name, 
        string memory _symbol, 
        uint _supplyCap, 
        uint _price, 
        uint _buyLimit, 
        uint _presalereservedLimit, 
        string memory _uri
        
    ) ERC721(_name, _symbol) {
        supplyCap = _supplyCap;
        price = _price;
        buyLimit = _buyLimit;
        presalereservedLimit = _presalereservedLimit;
        _baseURIPrefix = _uri;
    }

    //URIs
    function _baseURI() internal view override returns (string memory) {
        return _baseURIPrefix;
    }

    function setBaseURI(string memory newUri) external onlyOwner {
        _baseURIPrefix = newUri;
    }

    //Whitelist_v2 - Y
    function setPreSaleWhitelist(address[] memory _a) public onlyOwner {
        for(uint256 i; i < _a.length; i++){
            preSaleReserved[_a[i]] = presalereservedLimit; 
        }
    }

    //Distribution - Y
    function setPrice(uint256 newPrice) external onlyOwner {
        price = newPrice;
    }

    function setBuyLimit(uint256 newBuyLimit) external onlyOwner {
        buyLimit = newBuyLimit;
    }

    function setPresaleReserve(uint256 newpresalereserved) external onlyOwner {
        presalereservedLimit = newpresalereserved;
    }

    function setSupplyCap(uint256 newSupplyCap) external onlyOwner {
        supplyCap = newSupplyCap;
    }

    function setSaleState(uint256 _saleState) public onlyOwner {
        saleState = _saleState;    
    }
    
    function mintPeeps(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( saleState > 1,             "Sale isn't active" );
        require( num > 0,                   "Invalid quantity");
        require( num <= buyLimit,           "Exceeds maximum Buy Limit per single mint" );
        require( supply + num <= supplyCap, "Exceeds maximum Supply Cap" );
        require( supply + num <= max_peeps, "Exceeds maximum Peeps supply" );
        require( msg.value >= price * num,  "Ether sent is not correct" );

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
    }

    function preSaleMintPeeps(uint256 num) public payable {
        uint256 supply = totalSupply();
        uint256 reservedAmt = preSaleReserved[msg.sender];
        require( saleState > 0,             "Presale isn't active" );
        require( num > 0,                   "Invalid quantity");
        require( reservedAmt > 0,           "No tokens reserved for address" );
        require( num <= reservedAmt,        "Can't mint more than reserved" );
        require( supply + num <= supplyCap, "Exceeds maximum Supply cap" );
        require( supply + num <= max_peeps, "Exceeds maximum Peeps supply" );
        require( msg.value >= price * num,  "Ether sent is not correct" );
        preSaleReserved[msg.sender] = reservedAmt - num;
        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
    }

    //giveAway - Y
    function giveAway(address _to, uint256 _num) external onlyOwner() {
        uint256 supply = totalSupply();
        for(uint256 i; i < _num; i++){
            _safeMint( _to, supply + i );
        }
    }
    
    //Withdraw 
    function withdraw() external onlyOwner {
        payable(owner()).transfer(address(this).balance);
    }
    
    function walletOfOwner(address _owner) public view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for(uint256 i; i < tokenCount; i++){
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokensId;
    }    
    
}
function giveAway(address _to, uint256 _num) external onlyOwner() {
    uint256 supply = totalSupply();
    for(uint256 i; i < _num; i++){
        _safeMint( _to, supply + i );
    }
}

//giveAway - Y