contract TheGANSMachine is ERC721, Ownable, Pausable {
	using Counters for Counters.Counter;
	using Strings for uint256;

	address 								_address_for_withdrawal = 0x45C0728aE3aAf2fb2eB6052602d517d76B6A389E;
	Counters.Counter private				_total;
	Counters.Counter private				_reserved_total;
	mapping (address => uint256) private	_whitelist;
	uint256 private 						_price = 0.25 ether;
	uint256 private 						_mint_supply = 1250;
	uint256 private 						_mint_reserved = 20;
	uint256 private 						_mint_limit = 10;
	string private 							_base_url = 'https://2-crypto.s3.amazonaws.com/the-gans-collective/NFT_META/';
	bool private							_is_whitelist_only = true;
	bool private							_mint_limit_by_addy = false;

	//construct
	constructor() ERC721('The GANS Machine','GANS') {

	}

	//pausing of contract
	function pause() public onlyOwner {
		_pause();
	}
	function unpause() public onlyOwner {
		_unpause();
	}

	//whitelist
	function toggle_whitelist(bool change) public onlyOwner {
		_is_whitelist_only = change;
	}
	function add_to_whitelist(address[] memory addresses, uint256[] memory mint_nums) public onlyOwner {
		for(uint i=0;i<addresses.length;i++) {
			address user = addresses[i];
			uint256 mint_num = mint_nums[i];
			_whitelist[user] = mint_num;
		}
	}
	function remove_from_whitelist(address[] memory addresses) public onlyOwner {
		for(uint i=0;i<addresses.length;i++) {
			address user = addresses[i];
			if(_whitelist[user] > 0) {
				_whitelist[user] = 0;
			}
		}
	}
	function is_whitelisted(address user) public view returns (bool) {
		if(_whitelist[user] > 0) {
			return true;
		}
		return false;
	}
	function is_whitelist_on() public view returns (bool) {
		return _is_whitelist_only;
	}
	function get_whitelisted_mint_num(address user) public view returns (uint256) {
		if(_whitelist[user] > 0) {
			return _whitelist[user];
		}
		return 0;
	}

	//set
	function set_price(uint256 new_price) public onlyOwner() {
		_price = new_price;
	}
	function set_mint_supply(uint256 new_limit) public onlyOwner() {
		_mint_supply = new_limit;
	}
	function set_mint_reserve(uint256 new_limit) public onlyOwner() {
		_mint_reserved = new_limit;
	}
	function set_mint_limit(uint256 new_limit) public onlyOwner() {
		_mint_limit = new_limit;
	}
	function set_mint_limit_state(bool state) public onlyOwner() {
		_mint_limit_by_addy = state;
	}
	function set_base_url(string memory new_url) public onlyOwner() {
		_base_url = new_url;
	}

	//get
	function get_price() public view returns (uint256) {
		return _price;
	}
	function get_supply_limit() public view returns (uint256) {
		return _mint_supply;
	}
	function get_reserve_limit() public view returns (uint256) {
		return _mint_reserved;
	}
	function get_mint_limit() public view returns (uint256) {
		return _mint_limit;
	}
	function baseTokenURI() public view returns (string memory) {
		return _base_url;
	}
	function tokenURI(uint256 token_id) public view override returns (string memory) {
		require(_exists(token_id),'ERC721Metadata: URI query for nonexistent token');
		return string(abi.encodePacked(_base_url,token_id.toString()));
	}
	function get_total_minted() public view returns (uint256) {
		return _total.current();
	}
	function get_total_reserved() public view returns (uint256) {
		return _reserved_total.current();
	}
	function totalSupply() public view returns (uint256) {
		return _total.current();
	}

	//withdraw
	function withdraw() public payable onlyOwner {
		uint256 _each = address(this).balance / 1;
		require(payable(_address_for_withdrawal).send(_each));
	}

	//mint
	function mint(uint256 mint_num) public payable {

		//must mint at least one
		require(mint_num > 0, 'Need to mint at least 1 NFT.');

		//cannot mint more than allowance
		if(_mint_limit_by_addy) {
			uint256 token_count = balanceOf(msg.sender);
			require(token_count + mint_num <= _mint_limit, 'You have exceeded maximum mint limit.');
		}
		else {
			require(mint_num <= _mint_limit, 'You have exceeded maximum mint limit.');
		}

		//entire mint supply has been minted
		require(_total.current() - _reserved_total.current() + mint_num <= _mint_supply - _mint_reserved, 'You have exceeded the remaining mintable NFTs.');

		//minter did not send enough ETH
		require(msg.value >= _price * mint_num, 'Did not send enough ETH.');

		//if whitelist is on
		if(_is_whitelist_only == true) {

			//must be whitelisted
			require(is_whitelisted(msg.sender),'Not whitelisted.');

			//cannot mint more than allowed
			uint256 whitelisted_mint_num = get_whitelisted_mint_num(msg.sender);
			uint256 token_count = balanceOf(msg.sender);
			require(token_count + mint_num <= whitelisted_mint_num, 'You have exceeded maximum mint limit.');
		}

		//mint
		for(uint256 i;i<mint_num;i++) {
			_total.increment();
			_safeMint(msg.sender,_total.current());
		}
	}
	function mint_reserve(address address_to, uint256 mint_num) external onlyOwner() {

		//cannot mint more than allowance
		require(_reserved_total.current() + mint_num <= _mint_reserved, 'You have exceeded maximum reserved mint limit.');

		//mint
		for(uint256 i;i<mint_num;i++) {
			_total.increment();
			_reserved_total.increment();
			_safeMint(address_to,_total.current());
		}

	}
	function _beforeTokenTransfer(address from, address to, uint256 token_id) internal whenNotPaused override(ERC721) {
		super._beforeTokenTransfer(from, to, token_id);
	}

}
function mint(uint256 mint_num) public payable {

	//must mint at least one
	require(mint_num > 0, 'Need to mint at least 1 NFT.');

	//cannot mint more than allowance
	if(_mint_limit_by_addy) {
		uint256 token_count = balanceOf(msg.sender);
		require(token_count + mint_num <= _mint_limit, 'You have exceeded maximum mint limit.');
	}
	else {
		require(mint_num <= _mint_limit, 'You have exceeded maximum mint limit.');
	}

	//entire mint supply has been minted
	require(_total.current() - _reserved_total.current() + mint_num <= _mint_supply - _mint_reserved, 'You have exceeded the remaining mintable NFTs.');

	//minter did not send enough ETH
	require(msg.value >= _price * mint_num, 'Did not send enough ETH.');

	//if whitelist is on
	if(_is_whitelist_only == true) {

		//must be whitelisted
		require(is_whitelisted(msg.sender),'Not whitelisted.');

		//cannot mint more than allowed
		uint256 whitelisted_mint_num = get_whitelisted_mint_num(msg.sender);
		uint256 token_count = balanceOf(msg.sender);
		require(token_count + mint_num <= whitelisted_mint_num, 'You have exceeded maximum mint limit.');
	}

	//mint
	for(uint256 i;i<mint_num;i++) {
		_total.increment();
		_safeMint(msg.sender,_total.current());
	}
}

//mint