contract LiquidVestingTokenFactory is Ownable, ILiquidVestingTokenFactory {
    uint256 public constant MIN_FEE = 0;
    uint256 public constant MAX_FEE = 5000;

    address private tokenImplementation;
    address public override merkleRootSigner;
    address public override feeCollector;
    uint256 public override fee;

    event VestingTokenCreated(
        address indexed redeemToken,
        address vestingToken
    );

    mapping(address => address[]) public vestingTokensByOriginalToken;

    /**
     * @dev Creates a vesting token factory contract.
     * @param _tokenImplementation Address of LiquidVestingToken contract implementation.
     * @param _merkleRootSigner Address of Merkle root signer.
     * @param _feeCollector Address of fee collector.
     * @param _fee Fee value.
     */
    constructor(
        address _tokenImplementation,
        address _merkleRootSigner,
        address _feeCollector,
        uint256 _fee
    ) {
        require(
            Address.isContract(_tokenImplementation),
            "Implementation is not a contract"
        );
        require(
            _merkleRootSigner != address(0),
            "Merkle root signer cannot be zero address"
        );
        require(
            _feeCollector != address(0),
            "Fee collector cannot be zero address"
        );
        require(_fee >= MIN_FEE && _fee <= MAX_FEE, "Fee goes beyond rank");

        merkleRootSigner = _merkleRootSigner;
        feeCollector = _feeCollector;
        fee = _fee;
        tokenImplementation = _tokenImplementation;
    }

    /**
     * @dev Set address of Merkle root signer.
     * @param _newMerkleRootSigner Address of signer.
     */
    function setMerkleRootSigner(address _newMerkleRootSigner)
        external
        override
        onlyOwner
    {
        require(
            _newMerkleRootSigner != address(0),
            "Merkle root signer cannot be zero address"
        );

        merkleRootSigner = _newMerkleRootSigner;
    }

    /**
     * @dev Set address of fee collector.
     * @param _newFeeCollector Address of fee collector.
     */
    function setFeeCollector(address _newFeeCollector)
        external
        override
        onlyOwner
    {
        require(
            _newFeeCollector != address(0),
            "Fee collector cannot be zero address"
        );

        feeCollector = _newFeeCollector;
    }

    /**
     * @dev Set a new fee in range 0% - 5%.
     * For example, if a fee is 5% it should be 5000 (5 * 10**3).
     * @param _fee Amount of fee.
     */
    function setFee(uint256 _fee) external override {
        require(_msgSender() == feeCollector, "Caller is not fee collector");
        require(_fee >= MIN_FEE && _fee <= MAX_FEE, "Fee goes beyond rank");

        fee = _fee;
    }

    /**
     * @dev Get implementation of LiquidVestingToken contract.
     */
    function implementation() external view override returns (address) {
        return tokenImplementation;
    }

    /**
     * @dev Get min fee value.
     */
    function getMinFee() external pure override returns (uint256) {
        return MIN_FEE;
    }

    /**
     * @dev Get max fee value.
     */
    function getMaxFee() external pure override returns (uint256) {
        return MAX_FEE;
    }

    /**
     * @dev Creates new LiquidVestingToken contracts.
     * @param name Array of token names.
     * @param symbol Array of token symbols.
     * @param redeemToken Address of redeem token that will be locked by vesting period.
     * @param activationTimestamp Array of timestamps that allow starting transfers of vesting tokens.
     * @param redeemTimestamp Array of timestamps that allow redeeming tokens.
     * @param addRecipientsType Type of saving recipients of vesting tokens.
     */
    function createLiquidVestingToken(
        string[] memory name,
        string[] memory symbol,
        address redeemToken,
        uint256[] memory activationTimestamp,
        uint256[] memory redeemTimestamp,
        ILiquidVestingToken.AddType addRecipientsType
    ) public override {
        require(
            redeemToken != address(0),
            "Company token cannot be zero address"
        );
        require(
            name.length == symbol.length &&
                name.length == activationTimestamp.length &&
                name.length == redeemTimestamp.length,
            "Arrays length should be same"
        );

        uint8 decimals = ERC20(redeemToken).decimals();
        for (uint256 i = 0; i < name.length; i++) {
            require(
                activationTimestamp[i] <= redeemTimestamp[i],
                "activationTimestamp cannot be more than redeemTimestamp"
            );

            BeaconProxy token = new BeaconProxy(address(this), "");
            ILiquidVestingToken(address(token)).initialize(
                name[i],
                symbol[i],
                decimals,
                _msgSender(),
                address(this),
                redeemToken,
                activationTimestamp[i],
                redeemTimestamp[i],
                addRecipientsType
            );

            vestingTokensByOriginalToken[redeemToken].push(address(token));
            emit VestingTokenCreated(redeemToken, address(token));
        }
    }
}
/**
 * @title LiquidVestingTokenFactory
 * @dev The LiquidVestingTokenFactory contract can be used to create
 * vesting contracts for any ERC20 token.
 */
function implementation() external view override returns (address) {
    return tokenImplementation;
}

/**
 * @dev Get implementation of LiquidVestingToken contract.
 */