// File: Context.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

// File: LandSale.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./SafeMath.sol";
import "./Ownable.sol";
import "./ReentrancyGuard.sol";


interface ILand {
  function totalSupply() external view returns (uint256);
  function maximumSupply() external view returns (uint256);
  function mintToken(address account, uint256 count) external;
  function burnLastToken(address account) external;
}

contract LandSale is Ownable, ReentrancyGuard {
  using SafeMath for uint256;

  struct Purchase {
    uint256 count;
    uint256 price;
  }

  // Land-DAO token contract interface
  ILand public tokenContract;

  // Stores the allowed minting count and token price for each whitelisted address
  mapping (address => Purchase) private _allowances;
  // Stores the list of purchases along with the pricing
  mapping (address => Purchase[]) private _purchases;

  // Indicates the number of fund addresses (including treasury)
  uint8 constant _fundsAddressCount = 5;
  // Stores the total amount of owed (unlocked) funds for the founders
  uint256 public unlockedFunds;
  // Stores the total amount of owed (locked) funds for the founders
  uint256 public lockedFunds;
  // Stores the total amount of owed funds for the treasury
  uint256 public reserveFunds;
  // Stores the list of addresses owned by the reserve (at 0-index) and founders
  address[] public fundsAddresses;
  // Stores the timestamp on which the locked funds can be withdrawn
  uint256 public fundsUnlockTimestamp;

  constructor() {
    // By default, all founder addresses are set to the owner
    for (uint8 i = 0; i < _fundsAddressCount; i++) {
      fundsAddresses.push(msg.sender);
    }
  }

  // Add this modifier to all functions which are only accessible by the finance related addresses
  modifier onlyFinance() {
    require(msg.sender == fundsAddresses[0] ||
    msg.sender == fundsAddresses[1] ||
    msg.sender == fundsAddresses[2] ||
    msg.sender == fundsAddresses[3] ||
    msg.sender == fundsAddresses[4], "Unauthorized Access");
    _;
  }


  function setTokenContract(address _newTokenContract) external onlyOwner {
    require(_newTokenContract != address(0), "Invalid Address");
    tokenContract = ILand(_newTokenContract);
  }

  function setFundsAddress(uint8 _index, address _address) external onlyOwner {
    require(_address != address(0), "Invalid Address");
    require(_index >= 0 && _index < _fundsAddressCount, "Invalid Index");
    fundsAddresses[_index] = _address;
  }

  // Set the allowance for the specified address
  function setAllowance(address _address, uint256 _count, uint256 _price) public onlyOwner {
    require(_address != address(0), "Invalid Address");
    _allowances[_address] = Purchase(_count, _price);
  }

  // Set the allowance for the specified address
  function batchSetAllowances(
    address[] calldata _addresses,
    uint256[] calldata _counts,
    uint256[] calldata _prices
  ) external onlyOwner {
    uint256 count = _addresses.length;

    for (uint8 i = 0; i < count; i++) {
      setAllowance(_addresses[i], _counts[i], _prices[i]);
    }
  }

  // Get the allowance for the specified address
  function allowance(address _address) public view returns (
    uint256 count,
    uint256 price
  ) {
    Purchase memory _allowance = _allowances[_address];
    count = _allowance.count;
    price = _allowance.price;
  }

  // Set the UNIX timestamp for the funds unlock time
  function setFundsUnlockTimestamp(uint256 _unlock) external onlyOwner {     
    fundsUnlockTimestamp = _unlock;
  }

  // Handles token purchases
  receive() external payable nonReentrant {
    // Check if tokens are still available for sale
    uint256 remainingTokenCount = tokenContract.maximumSupply() - tokenContract.totalSupply();
    require(remainingTokenCount > 0, "Sold Out");

    // Check if sufficient funds are sent, and that the address is whitelisted (has valid allowance)
    // with enough funds to purchase at least 1 token
    uint256 accountLimit;
    uint256 tokenPrice;
    (accountLimit, tokenPrice) = allowance(msg.sender);
    require(accountLimit > 0, "Not Whitelisted For The Sale Or Insufficient Allowance");
    require(msg.value >= tokenPrice, "Insufficient Funds");

    // Calculate the actual amount of tokens to be minted, which must be within the set limits
    uint256 specifiedAmount = (tokenPrice == 0 ? accountLimit : msg.value.div(tokenPrice));
    uint256 actualAmount = (specifiedAmount > accountLimit ? accountLimit : specifiedAmount);
    actualAmount = (remainingTokenCount < actualAmount ? remainingTokenCount : actualAmount);
    _allowances[msg.sender].count -= actualAmount;
    tokenContract.mintToken(msg.sender, actualAmount);

    uint256 totalSpent = actualAmount.mul(tokenPrice);
    if (totalSpent > 0) {
      // Update the total received funds for the founders' share (95%)
      // Half of which are locked for 30 days after the end of the sale
      uint256 totalFounderShare = totalSpent.mul(95).div(100);
      uint256 lockedShare = totalFounderShare.div(2);
      uint256 unlockedShare = totalFounderShare.sub(lockedShare);
      lockedFunds = lockedFunds.add(lockedShare);
      unlockedFunds = unlockedFunds.add(unlockedShare);

      // 0-index is reserved for the treasury (5%) fully unlocked
      reserveFunds = reserveFunds.add(totalSpent.sub(totalFounderShare));

      _purchases[msg.sender].push(Purchase(actualAmount, tokenPrice));
    }

    // Calculate any excess/unspent funds and transfer it back to the buyer
    uint256 unspent = msg.value.sub(totalSpent);
    if (unspent > 0) {
      payable(msg.sender).transfer(unspent);
    }
  }

  // Handles refund requests which would send back 50% of the price at the time of purchase
  // and also subsequently burn the last token minted for the address
  function refund() external nonReentrant {
    require(_purchases[msg.sender].length > 0, "No Refund Available");
    Purchase memory purchase = _purchases[msg.sender][_purchases[msg.sender].length - 1];
    uint256 refundAmount = purchase.price.div(2);
    require(refundAmount <= lockedFunds, "Insufficient Funds Available");

    // Update the purchase records and burn the token
    if (purchase.count > 1) {
      _purchases[msg.sender][_purchases[msg.sender].length - 1].count -= 1;
    } else {
      _purchases[msg.sender].pop();
    }

    // Deduct from the locked funds
    lockedFunds = lockedFunds.sub(refundAmount);

    tokenContract.burnLastToken(msg.sender);

    payable(msg.sender).transfer(refundAmount);
  }

  // Used by the fund addresses to withdraw any owed funds
  function withdraw() external onlyFinance {
    // Calculate total owed funds based on the timing of the withdrawal
    uint256 totalOwed;
    if (block.timestamp >= fundsUnlockTimestamp) {
      totalOwed = unlockedFunds.add(lockedFunds);
      unlockedFunds = 0;
      lockedFunds = 0;
    } else {
      totalOwed = unlockedFunds;
      unlockedFunds = 0;
    }

    require(totalOwed > 0, "Withdrawal Not Available");

    // Starting from 1, as 0 is for the treasury
    uint256 individualShare = totalOwed.div(_fundsAddressCount - 1);
    for (uint8 i = 1; i < _fundsAddressCount; i++) {
      payable(fundsAddresses[i]).transfer(individualShare);
    }

    // Doing the same for the treasury
    if (reserveFunds > 0) {
      uint256 owed = reserveFunds;
      reserveFunds = 0;
      payable(fundsAddresses[0]).transfer(owed);
    }
  }
}

// File: Ownable.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    constructor() {
        _setOwner(_msgSender());
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        require(owner() == _msgSender(), "Ownable: caller is not the owner");
        _;
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _setOwner(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        require(newOwner != address(0), "Ownable: new owner is the zero address");
        _setOwner(newOwner);
    }

    function _setOwner(address newOwner) private {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: ReentrancyGuard.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and make it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;

        _;

        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

// File: SafeMath.sol



// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is no longer needed starting with Solidity 0.8. The compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the substraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}