contract RTK_SwapContract {
    
    address private owner;
    /*
    * _Token_RTKL[0] = RTKL1 is actually original RTK
    * _Token_RTKL[1...4] are RTKL2...RTKL5
    * _Token_Bullet is addy of bullet contract
    */
    mapping(uint256 => address) private _Token_RTKL;
    mapping(uint256 => uint256) private _RTKLX_ExtCirculation;
    address private _Token_AMMO;
    
    event OwnerSet(address indexed oldOwner, address indexed newOwner);
    
    // modifier to check if caller is owner
    modifier OwnerOnly() {
        require(msg.sender == owner, "Caller is not owner");
        _;
    }
    
    constructor(address RTK, address RTKL2, address RTKL3, address RTKL4, address RTKL5, address AMMO) public {
        owner = msg.sender;
        _Token_RTKL[0] = RTK;
        _Token_RTKL[1] = RTKL2;
        _Token_RTKL[2] = RTKL3;
        _Token_RTKL[3] = RTKL4;
        _Token_RTKL[4] = RTKL5;
        _Token_AMMO = AMMO;
        _RTKLX_ExtCirculation[0] = ERC20(RTK).totalSupply();
        _RTKLX_ExtCirculation[1] = 0;
        _RTKLX_ExtCirculation[2] = 0;
        _RTKLX_ExtCirculation[3] = 0;
        _RTKLX_ExtCirculation[4] = 0;
        emit OwnerSet(address(0), owner);
    }

    function changeOwner(address newOwner) public OwnerOnly {
        emit OwnerSet(owner, newOwner);
        owner = newOwner;
    }

    function getOwner() external view returns (address) {
        return owner;
    }
    
    function changeTokenAddresses(address RTK, address RTKL2, address RTKL3, address RTKL4, address RTKL5, address AMMO) public OwnerOnly {
        _Token_RTKL[0] = RTK;
        _Token_RTKL[1] = RTKL2;
        _Token_RTKL[2] = RTKL3;
        _Token_RTKL[3] = RTKL4;
        _Token_RTKL[4] = RTKL5;
        _Token_AMMO = AMMO;
    }
    
    // Function to get amount of minimum required AMMO in the contract
    function getMinimumRequiredAMMOAmount() public view OwnerOnly returns (uint256) {
        uint256 netUnburntExtCirculation = 0;
        for(uint i = 1; i <= 4; i++) {
            netUnburntExtCirculation += (_RTKLX_ExtCirculation[i] - ERC20(_Token_RTKL[i]).balanceOf(address(0)))*(i);
        }
        
        return netUnburntExtCirculation;
    }
    
    // Function to get amount of minimum required RTK in the contract
    function getMinimumRequiredRTKAmount() public view OwnerOnly returns (uint256) {
        uint256 netUnburntExtCirculation = 0;
        for(uint i = 1; i <= 4; i++) {
            netUnburntExtCirculation += (_RTKLX_ExtCirculation[i] - ERC20(_Token_RTKL[i]).balanceOf(address(0)));
        }
        
        return netUnburntExtCirculation;
    }
    
    // Function for owner to pull out EXCESS AMMO. A certain minimum amount AMMO has to stay within the contract
    // In order to facilitate conversion of RTKLX into RTK for all the UNBURNED RTKLX in the circulation.
    function pullOutExcessAMMO(address to, uint256 amount) public OwnerOnly returns (bool) {
        ERC20 AMMOToken = ERC20(_Token_AMMO);
        
        require (
            amount > 0,
            "Cannot pull out 0"
        );
        
        require (
            AMMOToken.balanceOf(address(this)) >= amount,
            "Insufficient AMMO balance in the contract"
        );
        
        require (
            AMMOToken.balanceOf(address(this)) - amount >= getMinimumRequiredAMMOAmount(),
            "Cannot withdraw an amount that leaves swap contract deficient of minimum required AMMO"
        );
        
        return AMMOToken.transfer(to, amount);
    }
    
    // Function for owner to pull out EXCESS RTK. A certain minimum amount RTK has to stay within the contract
    // In order to facilitate conversion of RTKLX into RTK for all the UNBURNED RTKLX in the circulation.
    function pullOutExcessRTK(address to, uint256 amount) public OwnerOnly returns (bool) {
        ERC20 RTKToken = ERC20(_Token_RTKL[0]);
        
        require (
            amount > 0,
            "Cannot pull out 0"
        );
        
        require (
            RTKToken.balanceOf(address(this)) >= amount,
            "Insufficient RTK balance in the contract"
        );
        
        require (
            (RTKToken.balanceOf(address(this)) - amount) >= getMinimumRequiredRTKAmount(),
            "Cannot withdraw an amount that leaves swap contract deficient of minimum required RTK"
        );
        
        return RTKToken.transfer(to, amount);
    }

    function convertRTKIntoRTKLX(address to, uint256 amount, uint256 X) public returns (bool) {
        require (
            (X >= 2 && X <= 5), 
            "Invalid value of X. X can only be 2, 3, 4, 5"
        );
        
        require (
            amount > 0,
            "Cannot convert 0"
        );
        
        ERC20 AMMOToken = ERC20(_Token_AMMO);
        ERC20 RTKToken = ERC20(_Token_RTKL[0]);
        ERC20 RTKLXToken = ERC20(_Token_RTKL[X-1]);
        
        require (
            RTKLXToken.balanceOf(address(this)) >= amount,
            "Insufficeint RTKLX Token balance in the contract for the given value of X"
        );
        
        require (
            RTKToken.allowance(msg.sender, address(this)) >= amount,
            "Allowance Lower than required for RTKToken"
        );
        
        require (
            AMMOToken.allowance(msg.sender, address(this)) >= ((X-1)*amount),
            "Allowance Lower than required for bulletToken"
        );
        
    
        if(AMMOToken.transferFrom(msg.sender, address(this), ((X-1)*amount))) {
            if(RTKToken.transferFrom(msg.sender, address(this), amount)) {
                if(RTKLXToken.transfer(to, amount)) {
                    _RTKLX_ExtCirculation[X-1] += amount;
                    return true;
                } else {
                    return false;
                }
            } else {
                AMMOToken.transfer(msg.sender, (X-1)*amount);
                return false;
            }
        } else {
            return false;
        }
    }
    
    function convertRTKLXIntoRTK(address to, uint256 amount, uint256 X) public returns (bool) {
        require (
            (X >= 2 && X <= 5), 
            "Invalid value of X. X can only be 2, 3, 4, 5"
        );
        
        require (
            amount > 0,
            "Cannot convert 0"
        );
        
        ERC20 RTKToken = ERC20(_Token_RTKL[0]);
        ERC20 AMMOToken = ERC20 (_Token_AMMO);
        ERC20 RTKLXToken = ERC20(_Token_RTKL[X-1]);
            
        require (
            RTKToken.balanceOf(address(this)) >= amount,
            "Insufficeint RTK Token balance in the contract"
        );
        
        require (
            AMMOToken.balanceOf(address(this)) >= amount*(X-1),
            "insufficient AMMO token balance in the contract"
            );
        
        require (
            RTKLXToken.allowance(msg.sender, address(this)) >= amount,
            "Allowance Lower than required for RTKToken"
        );
        
        if(RTKLXToken.transferFrom(msg.sender, address(this), amount)) {
            _RTKLX_ExtCirculation[X-1] -= amount;
            if(RTKToken.transfer(to, amount)) {
                return AMMOToken.transfer(to, (X-1)*amount);
            } else {
                return false;
            }
        } else {
            return false;
        }
    }
}
function getMinimumRequiredAMMOAmount() public view OwnerOnly returns (uint256) {
    uint256 netUnburntExtCirculation = 0;
    for(uint i = 1; i <= 4; i++) {
        netUnburntExtCirculation += (_RTKLX_ExtCirculation[i] - ERC20(_Token_RTKL[i]).balanceOf(address(0)))*(i);
    }
        
    return netUnburntExtCirculation;
}

// Function to get amount of minimum required AMMO in the contract