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

	address 								_address_for_withdrawal = 0x5144E324bE090F8b22e56476E6e5ed624037f925;
	Counters.Counter private				_total;
	Counters.Counter private				_reserved_total;
	mapping (address => bool) private		_whitelist;
	mapping (address => uint256) private	_addr_balance;
	uint256 private 						_price = 0.075 ether;
	uint256 private 						_mint_supply = 10000;
	uint256 private 						_mint_reserved = 50;
	uint256 private 						_mint_limit = 3;
	uint256 private 						_mint_limit_whitelist = 3;
	string private 							_base_url = 'https://2-crypto.s3.amazonaws.com/the-beeings/NFT_META/';
	bool private							_is_whitelist_only = true;
	bool private							_mint_limit_by_addy = true;

	//construct
	constructor() ERC721('Beeings','BEE') {

	}

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

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

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

	//get
	function get_price() external view returns (uint256) {
		return _price;
	}
	function get_supply_limit() external view returns (uint256) {
		return _mint_supply;
	}
	function get_reserve_limit() external view returns (uint256) {
		return _mint_reserved;
	}
	function get_mint_limit() external view returns (uint256) {
		return _mint_limit;
	}
	function get_whitelisted_mint_limit() external view returns (uint256) {
		return _mint_limit_whitelist;
	}
	function get_addr_minted_balance(address user) public view returns (uint256) {
		return _addr_balance[user];
	}
	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),'missing token');
		return string(abi.encodePacked(_base_url,token_id.toString()));
	}
	function get_total_reserved() external view returns (uint256) {
		return _reserved_total.current();
	}
	function totalSupply() public view returns (uint256) {
		return _total.current();
	}

	//withdraw
	function withdraw() external payable onlyOwner {
		require(payable(_address_for_withdrawal).send(address(this).balance-1));
	}

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

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

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

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

		//whitelist is on
		if(_is_whitelist_only) {

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

			//cannot mint more than allowed
			require(get_addr_minted_balance(msg.sender) + mint_num <= _mint_limit_whitelist, 'exceeded max limit');
		}

		//cannot mint, per address, more than allowance
		else if(_mint_limit_by_addy) {
			require(get_addr_minted_balance(msg.sender) + mint_num <= _mint_limit, 'exceeded max limit');
		}

		//cannot mint, per transaction, more than allowance
		else {
			require(mint_num <= _mint_limit, 'exceeded max mint');
		}

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

		//cannot mint more than what has been reserved
		require(_reserved_total.current() + mint_num <= _mint_reserved, 'exceeded max 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 get_price() external view returns (uint256) {
	return _price;
}

//get