contract Forwarder is Initializable, Ownable {

  /**
   * DEVELOPER NOTES
   *   - IMPORTANT: Contracts derived from {GSNRecipient} should never use
   *     `msg.sender`, and should use {_msgSender} instead. See comments here:
   *      https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/master/contracts/GSN/Context.sol#L6
   */

  // =============================================================================================
  //                                    Storage Variables
  // =============================================================================================

  // Floatify server
  address public floatify;

  // Contract addresses and interfaces
  address public daiAddress;
  ERC20 public daiContract;

  address public chaiAddress;
  IChai public chaiContract;

  address public knpAddress;
  IKyberNetworkProxy public knpContract;

  ERC20 constant public ETH_TOKEN_ADDRESS = ERC20(0x00eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee);

  // =============================================================================================
  //                                        Events
  // =============================================================================================

  /**
   * @dev Emitted when Chai is successfully minted from Dai held by the contract
   */
  event ChaiSent(uint256 indexed amountInDai);

  /**
   * @dev Emitted when Ether is swapped for Dai
   */
  event SwapEther(uint256 indexed amountInDai, uint256 indexed amountInEther);

  /**
   * @dev Emitted when the Floatify address changes
   */
  event FloatifyAddressChanged(address indexed previosAddress, address indexed newAddress);


  // ===============================================================================================
  //                                      Constructor
  // ===============================================================================================

  /**
   * @notice Constructor, calls other constructors. Can only be called once
   * due to initializer modifier
   * @param _owner The user's account address
   * @param _floatify Floatify server address
   * @param _dai Dai contract address
   * @param _chai Chai server address
   * @param _kyber Kyber network proxy address
   */
  function initialize(
    address _owner,
    address _floatify,
    address _dai,
    address _chai,
    address _kyber
  ) public initializer {
    // Call constructors
    Ownable.initialize(_owner);
    // GSNRecipient.initialize();

    // Set Floatify address
    floatify = _floatify;

    // Set contract addresses and interfaces
    daiAddress = _dai;
    daiContract = ERC20(daiAddress);
    chaiAddress = _chai;
    chaiContract = IChai(chaiAddress);
    knpAddress = _kyber;
    knpContract = IKyberNetworkProxy(knpAddress);

    // Approve the Chai contract to spend our DAI balance
    bool approvalResult = daiContract.approve(chaiAddress, uint256(-1));
    require(approvalResult, "Failed to approve Chai contract to spend DAI");
  }


  // ===============================================================================================
  //                                    Updating Addresses
  // ===============================================================================================

  /**
   * @dev Throws if called by any account other than floatify
   */
  modifier onlyFloatify() {
    require(_msgSender() == floatify, "Forwarder: caller is not the floatify address");
    _;
  }

  /**
   * @dev Allows the floatify address to be changed
   * @param _newAddress new address
   */
  function updateFloatifyAddress(address _newAddress) public onlyFloatify {
    // Make sure only floatify can call this function
    require(_newAddress != address(0), "Forwarder: new floatify address is the zero address");
    // Update address
    emit FloatifyAddressChanged(floatify, _newAddress);
    floatify = _newAddress;
  }

  /**
   * @dev Allows the Dai contract address to be changed
   * @param _newAddress new address
   */
  function updateDaiAddress(address _newAddress) public onlyFloatify {
    daiAddress = _newAddress;
    daiContract = ERC20(daiAddress);
  }

  /**
   * @dev Allows the Chai contract address to be changed
   * @param _newAddress new address
   */
  function updateChaiAddress(address _newAddress) public onlyFloatify {
    chaiAddress = _newAddress;
    chaiContract = IChai(chaiAddress);
  }

  /**
   * @dev Allows the Kyber Proxy contract address to be changed
   * @param _newAddress new address
   */
  function updateKyberAddress(address _newAddress) public onlyFloatify {
    knpAddress = _newAddress;
    knpContract = IKyberNetworkProxy(knpAddress);
  }


  // ===============================================================================================
  //                               Handling Received Ether/Tokens
  // ===============================================================================================

  /**
   * @notice Upon receiving Dai, use this function to convert it to Chai and send
   * it to the owner
   */
  function mintAndSendChai() public {
    // Get Dai balance of this contract
    uint256 _daiBalance = daiContract.balanceOf(address(this));
    // Mint and send Chai
    emit ChaiSent(_daiBalance);
    address _owner = owner();
    chaiContract.join(_owner, _daiBalance);
  }


  /**
   * @notice Upon receiving any other token, use this function to convert it to Dai
   * and send it to the owern
   */
  function convertAndSendToken(address _srcTokenAddress) public {
    // TODO convert token to Dai
    //   Use "Loose Token Conversion" as shown here
    //   https://developer.kyber.network/docs/DappsGuide/#scenario-1-loose-token-conversion

    // Get token parameters
    ERC20 _srcTokenContract = ERC20(_srcTokenAddress);
    uint256 _srcTokenBalance = _srcTokenContract.balanceOf(address(this));

    // Use slippage rate as the minimum conversion rate
    uint256 minRate;
    (, minRate) = knpContract.getExpectedRate(_srcTokenContract, daiContract, _srcTokenBalance);

    // // Check that the token transferFrom has succeeded
    // require(_srcTokenContract.transferFrom(msg.sender, this, _srcTokenBalance));

    // Mitigate ERC20 Approve front-running attack, by initially setting allowance to 0
    require(_srcTokenContract.approve(knpAddress, 0));

    // Approve tokens so network can take them during the swap
    _srcTokenContract.approve(knpAddress, _srcTokenBalance);
    uint _destAmount = knpContract.swapTokenToToken(_srcTokenContract, _srcTokenBalance, daiContract, minRate);
    require(_destAmount == _srcTokenContract.balanceOf(address(this)), "Token balaces not equal");

    // Mint and send Chai
    mintAndSendChai();
  }

  /**
   * @notice Upon receiving Ether, convert it to Chai and send it to the owner
   */
  function() external payable {
    // TODO
    // handle slippage for larger transactions?
    //   https://developer.kyber.network/docs/Integrations-SlippageRateProtection/

    // Use slippage rate as the minimum conversion rate
    uint256 minRate;
    (, minRate) = knpContract.getExpectedRate(ETH_TOKEN_ADDRESS, daiContract, msg.value);

    // Swap Ether for Dai, and receive back tokens to this contract's address
    uint256 _destAmount = knpContract.swapEtherToToken.value(msg.value)(daiContract, minRate);
    require(_destAmount == daiContract.balanceOf(address(this)), "Balaces not equallll");

    uint256 daiBalance = daiContract.balanceOf(address(this));

    emit SwapEther(daiBalance, address(this).balance);

    // Convert to Chai and send to owner
    mintAndSendChai();
  }
}
function updateChaiAddress(address _newAddress) public onlyFloatify {
  chaiAddress = _newAddress;
  chaiContract = IChai(chaiAddress);
}

/**
 * @dev Allows the Chai contract address to be changed
 * @param _newAddress new address
 */