// File: /contracts/BeFootballOriginalsPreSale.sol



// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import "./Ownable.sol";
import "./IBeFootBallOriginalsNFT.sol";
import "./IBeFootballOriginalsSale.sol";

contract BeFootballOriginalsPreSale is IBeFootballOriginalsSale, Ownable {
    uint16 private startId;
    uint16 private endId;
    
    uint256 public price = 200000000000000000; //0.2 ETH
    address public immutable befootballNFT;
    address private wallet;
    bool public saleIsActive;

    event OriginalsBuy(address indexed _buyer, uint256 _amount, uint256 _copies);
    event Withdrawn(uint256 _amount, address _wallet);

    constructor (address _befootballNFT, address _wallet, uint16 _startId, uint16 _endId) {
        befootballNFT = _befootballNFT;
        wallet = _wallet;
        startId = _startId;
        endId = _endId;
    }

    function setSaleInitialId(uint256 _id) external onlyOwner {
        IBeFootBallOriginalsNFT(befootballNFT).setId(_id);
    }

    function flipSaleState() public onlyOwner {
        saleIsActive = !saleIsActive;
    }

    function changeIds(uint16 _startId, uint16 _endId) external onlyOwner {
        startId = _startId;
        endId = _endId;
    }

    function changeWallet(address _wallet) external onlyOwner {
        wallet = _wallet;
    }

    function buy(uint256 _quantity, address _to) external payable {
        uint256 copies = msg.value / price;
        require (copies == _quantity, "BadQuantity");
        
        _buy(_to);
    }

    function _buy(address _to) internal {
        require(saleIsActive, "SaleNoActive");

        uint256 copies = msg.value / price;
        require (copies > 0, "BadCopies");
        require(IBeFootBallOriginalsNFT(befootballNFT).id() + copies <= endId, "MaxSale");

        IBeFootBallOriginalsNFT(befootballNFT).mint(_to, copies);

        emit OriginalsBuy(_to, msg.value, copies);
    }

    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        (bool success, ) = wallet.call{value: balance}("");
        require(success, "BadTransfer");

        emit Withdrawn(balance, wallet);
    }

    receive() external payable {
        _buy(msg.sender);
    }
}

// File: /contracts/Ownable.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/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 public owner;
    address public ownerPendingClaim;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
    event NewOwnershipProposed(address indexed previousOwner, address indexed newOwner);

    constructor() {
        _transferOwnership(_msgSender());
    }

    modifier onlyOwner() {
        require(owner == _msgSender(), "OnlyOwner");
        _;
    }

    function proposeChangeOwner(address newOwner) external onlyOwner {
        require(newOwner != address(0), "ZeroAddress");
        ownerPendingClaim = newOwner;

        emit NewOwnershipProposed(_msgSender(), newOwner);
    }

    function claimOwnership() external {
        require(_msgSender() == ownerPendingClaim, "OnlyProposedOwner");

        ownerPendingClaim = address(0);
        _transferOwnership(_msgSender());
    }

    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = owner;
        owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

// File: /contracts/IBeFootballOriginalsSale.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IBeFootballOriginalsSale {
    function buy(uint256 _quantity, address _to) external payable;
}

// File: /contracts/IBeFootBallOriginalsNFT.sol



// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

interface IBeFootBallOriginalsNFT {
    function id() external returns (uint256);
    function setId(uint256 _id) external;
    function mint(address _to, uint256 _copies) external;
}

// File: @openzeppelin/contracts/utils/Context.sol



// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

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;
    }
}