contract Travelers is ERC721, Ownable {
    using SafeMath for uint256;

    uint256 public nftPrice = 120000000000000000; //0.12 ETH
    uint public constant maxNftPurchase = 12;

    uint256 public MAX_NFTS;

    bool public saleIsActive = false;
    bool public presaleIsActive = false;

   
    mapping (address => bool) private _isWhitelisted;
    mapping (address => bool) private _isTokenAdmin;

    constructor() ERC721("Time Travelers", "TRAVELERS") {
        MAX_NFTS = 1440;
    }

    modifier OnlyTokenAdmin() {
        require(_isTokenAdmin[_msgSender()] || _msgSender() == owner(), "Sorry: You are not a Token Admin");
        _;
    }

    function setNftPrice(uint256 price) external onlyOwner {
        nftPrice = price;
    }

    function withdraw() external onlyOwner {
        uint256 balance = address(this).balance;
        payable(owner()).transfer(balance);
    }

    function setWhitelistAddress(address account, bool enabled) public onlyOwner {
        _isWhitelisted[account] = enabled;
    } 

    function setTokenAdmin(address account, bool enabled) public onlyOwner {
        _isTokenAdmin[account] = enabled;
    }

    function reserveNfts(uint256 _count) public onlyOwner {        
        uint supply = totalSupply();
        uint i;
        for (i = 0; i < _count; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

    /*
    * Pause sale if active, make active if paused
    */
    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }

    function flipPreSaleState() public onlyOwner {
        presaleIsActive = !presaleIsActive;
    }

    function isWhiteListed(address account) public view returns (bool) {
        return _isWhitelisted[account];
    }

    function isTokenAdmin(address account) public view returns (bool) {
        return _isTokenAdmin[account] || _msgSender() == owner();
    }

    function mint(uint numberOfTokens) public payable {
        require(saleIsActive || (presaleIsActive && _isWhitelisted[msg.sender]), "Sale must be active to mint");
        require(numberOfTokens <= maxNftPurchase, "Can only mint 12 at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_NFTS, "Purchase would exceed max supply");
        require(nftPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_NFTS) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }

    function preMint(uint numberOfTokens) public payable {
        require(presaleIsActive, "Presale must be active to mint");
        require(_isWhitelisted[msg.sender], "Account is not White Listed");
        require(numberOfTokens <= maxNftPurchase, "Can only mint 12 at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_NFTS, "Purchase would exceed max supply");
        require(nftPrice.mul(numberOfTokens) <= msg.value, "Ether value sent is not correct");      
        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_NFTS) {
                _safeMint(msg.sender, mintIndex);
            }
        }       
    }
}
/**
 * @dev Extends ERC721 Non-Fungible Token Standard basic implementation
 */
function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }
/*
 * Pause sale if active, make active if paused
 */