contract Exchange is IExchange, Ownable {
    enum State {INACTIVE, ACTIVE}

    IERC20 public erc20Token; // address of ERC-20 token allowed to be exchanged.
    IAllocationToken public allocationToken; // address of Allocation Token Smart Contract.
    IKYC public kyc; // KYC contract

    uint256 public allocationTokensPerErc20Token; // exchange rate of ERC-20 tokens to AT tokens.

    State exchangeState; // current state of the exchange contract

    /**
    @dev constructor for the Exchange contract
    @param _erc20Token address of ERC-20 token allowed to be exchanged.
    @param _allocationToken address of Allocation Token Smart Contract.
    @param _allocationTokensPerErc20Token exchange rate of ERC-20 tokens to AT tokens.
     */
    constructor(
        IERC20 _erc20Token,
        IAllocationToken _allocationToken,
        IKYC _kyc,
        uint256 _allocationTokensPerErc20Token
    ) public {
        erc20Token = _erc20Token;
        allocationToken = _allocationToken;
        allocationTokensPerErc20Token = _allocationTokensPerErc20Token;
        kyc = _kyc;

        exchangeState = State.ACTIVE;

        emit AllocationTokenRateSet(allocationTokensPerErc20Token);
        emit StateChanged(uint256(exchangeState));
    }

    /**
    @notice call is only allowed to pass when exchange is in ACTIVE state
     */
    modifier isStateActive() {
        require(exchangeState == State.ACTIVE, "Exchange state is INACTIVE.");
        _;
    }

    /**
    @notice function exchanges chellecoin tokens to allocation tokens
    @param tokens the amount of chellecoin tokens to be exchanged for allocation tokens
     */
    function exchange(uint256 tokens) public isStateActive returns (bool) {
        require(tokens > 0, "tokens amount is not valid.");
        require(
            kyc.getAddressStatus(msg.sender),
            "msg.sender is not whiteliisted in KYC"
        );

        uint256 AT_tokens_to_mint = calculateAllocationTokens(tokens);

        require(
            erc20Token.transferFrom(msg.sender, address(0x0), tokens),
            "transferFrom failed."
        );
        allocationToken.mint(msg.sender, AT_tokens_to_mint);

        emit TokensExchanged(
            0,
            msg.sender,
            address(erc20Token),
            address(allocationToken),
            tokens,
            AT_tokens_to_mint
        );
    }

    /**
    @notice this function sets/changes state of this smart contract and only exchange manager/owner can call it
    @param state it can be either 0 (Inactive) or 1 (Active)
     */
    function setState(uint256 state) external onlyOwner {
        require(state == 0 || state == 1, "Provided state is invalid.");
        require(
            state != uint256(exchangeState),
            "Provided state is already set."
        );

        exchangeState = State(state);
        emit StateChanged(uint256(exchangeState));
    }

    /**
    @dev it updates  update exchange rate of ERC-20 tokens to AT tokens
    @param  _allocationTokensPerErc20Token the exchange rate
    */
    function setExchangeRate(uint256 _allocationTokensPerErc20Token)
        external
        onlyOwner
    {
        require(
            _allocationTokensPerErc20Token > 0,
            "_allocationTokensPerErc20Token should be greater than 0."
        );

        allocationTokensPerErc20Token = _allocationTokensPerErc20Token;
        emit AllocationTokenRateSet(allocationTokensPerErc20Token);
    }

    /**
    @notice function to get the current state of the exchange contract
    @return current state
    */
    function getCurrentState() public view returns (uint256) {
        return uint256(exchangeState);
    }

    /**
    @dev an internal function to calculate allocation rate
    @param tokens the amount of chellecoin tokens
     */
    function calculateAllocationTokens(uint256 tokens)
        internal
        view
        returns (uint256)
    {
        return tokens * allocationTokensPerErc20Token;
    }
}
/**
 @notice Exchange smart contract for ChelleCoin to AllocationTokens
 */
function exchange(uint256 tokens) public isStateActive returns (bool) {
    require(tokens > 0, "tokens amount is not valid.");
    require(
        kyc.getAddressStatus(msg.sender),
        "msg.sender is not whiteliisted in KYC"
    );

    uint256 AT_tokens_to_mint = calculateAllocationTokens(tokens);

    require(
        erc20Token.transferFrom(msg.sender, address(0x0), tokens),
        "transferFrom failed."
    );
    allocationToken.mint(msg.sender, AT_tokens_to_mint);

    emit TokensExchanged(
        0,
        msg.sender,
        address(erc20Token),
        address(allocationToken),
        tokens,
        AT_tokens_to_mint
    );
}

/**
 @notice function exchanges chellecoin tokens to allocation tokens
 @param tokens the amount of chellecoin tokens to be exchanged for allocation tokens
 */