contract Oracle{

  using SafeMath
    for uint256;


  address payable public owner;
  address burnaddress  = address(0x0);
  mapping (string => address) farmDirectoryByName;
  mapping (address => mapping(address =>uint256)) farmManuallyEnteredAPYs;
  mapping (address => mapping (address  => address )) farmOracleObtainedAPYs;
  string [] public farmTokenPlusFarmNames;
  address [] public farmAddresses;
  address [] public farmTokens;
  address uniswapAddress = 0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D;
  IUniswapV2RouterLite uniswap = IUniswapV2RouterLite(uniswapAddress);
  address usdcCoinAddress = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
  address rewardAddress;
  Reward reward;
  address tvlOracleAddress;
  TVLOracle tvlOracle;
  //core contract adress that users interact with
  address public coreAddress;
  mapping (string  => address) public platformDirectory;


  modifier onlyOwner {
         require(
             msg.sender == owner,
             "Only owner can call this function."
         );
         _;
 }


  constructor() public payable {
        owner= msg.sender;
  }


    function getTotalValueLockedInternalByToken(address tokenAddress, address tier2Address) external view returns (uint256){
        uint256 result = tvlOracle.getTotalValueLockedInternalByToken(tokenAddress, tier2Address);
        return result;
    }

    function getTotalValueLockedAggregated(uint256 optionIndex) external view returns (uint256){
      uint256 result = tvlOracle.getTotalValueLockedAggregated(optionIndex);
      return result;
    }

    function getStakableTokens() view external  returns (address[] memory, string[] memory){
      address[] memory stakableAddrs = farmAddresses;
      string[] memory stakableNames = farmTokenPlusFarmNames;
      return (stakableAddrs, stakableNames);
    }

    function getAPR(address farmAddress, address farmToken)public view returns(uint256){
      uint obtainedAPY = farmManuallyEnteredAPYs[farmAddress][farmToken];

      if(obtainedAPY ==0){
        externalPlatformContract exContract = externalPlatformContract(farmOracleObtainedAPYs[farmAddress][farmToken]);
        try exContract.getAPR(farmAddress, farmToken) returns (uint apy) {
          return apy;
        }
        catch (bytes memory ) {
          return (0);
        }

      }

      else{
        return obtainedAPY;
      }
    }

    function getAmountStakedByUser(address tokenAddress, address userAddress, address tier2Address) external view returns(uint256){
      externalPlatformContract exContract = externalPlatformContract(tier2Address);
      return exContract.getStakedPoolBalanceByUser(userAddress, tokenAddress);
    }

    function getUserCurrentReward(address userAddress, address tokenAddress, address tier2FarmAddress) view external returns(uint256){
        uint256 userStartTime = reward.depositBalancesDelegated(userAddress, tokenAddress,0);

        uint256 principalAmount = reward.depositBalancesDelegated(userAddress, tokenAddress,1);
        uint256 apr = reward.tokenAPRs(tokenAddress);
        uint256 result = reward.calculateRewards( userStartTime, block.timestamp,  principalAmount, apr);
        return result;
    }

    function getTokenPrice(address tokenAddress, uint256 amount) view external returns(uint256){
      address [] memory addresses = new address[](2);
      addresses[0] = tokenAddress;
      addresses[1] = usdcCoinAddress;
      uint256 [] memory amounts = getUniswapPrice(addresses, amount );
      uint256 resultingTokens = amounts[1];
      return resultingTokens;
    }

    function getUserWalletBalance(address userAddress, address tokenAddress) external view returns (uint256){
      ERC20 token = ERC20(tokenAddress);
      return token.balanceOf(userAddress);
    }


    function getAddress(string memory component) public view returns (address){
          return platformDirectory[component];
    }





//BELOW is other (admin and otherwise)

    function updateTVLAddress(address theAddress) onlyOwner public returns(bool){
    tvlOracleAddress = theAddress;
    tvlOracle = TVLOracle(theAddress);
    updateDirectory("TVLORACLE", theAddress);
    return true;
  }

  function updatePriceOracleAddress(address theAddress) onlyOwner public returns(bool){
    uniswapAddress = theAddress;
    uniswap = IUniswapV2RouterLite(theAddress);
    updateDirectory("UNISWAP", theAddress);
    return true;
  }

  function updateUSD(address theAddress) onlyOwner public returns(bool){
    usdcCoinAddress = theAddress;
    updateDirectory("USD", theAddress);
    return true;
  }

  function updateRewardAddress(address theAddress) onlyOwner public returns(bool){
    rewardAddress = theAddress;
    reward = Reward(theAddress);
    updateDirectory("REWARDS", theAddress);
    return true;
  }

  function updateCoreAddress(address theAddress) onlyOwner public returns(bool){
    coreAddress = theAddress;
    updateDirectory("CORE", theAddress);
    return true;
  }

  function updateDirectory(string memory name, address theAddress) onlyOwner public returns(bool){
    platformDirectory[name] = theAddress;
    return true;
  }






  function setPlatformContract(string memory name, address farmAddress, address farmToken, address platformAddress) public onlyOwner returns(bool){
    farmTokenPlusFarmNames.push(name);
    farmAddresses.push(farmAddress);
    farmTokens.push(farmToken);

    farmOracleObtainedAPYs[farmAddress][farmToken] = platformAddress;
    farmDirectoryByName[name] = platformAddress;

    return true;

  }


  function calculateCommission(uint256 amount, uint256 commission) view public returns(uint256){
    uint256 commissionForDAO = (amount.mul(1000).mul(commission)).div(10000000);
    return commissionForDAO;
  }


  function getCommissionByContract(address platformContract) public view returns (uint256){
    externalPlatformContract exContract = externalPlatformContract(platformContract);
    return exContract.commission();

  }


  function getTotalStakedByContract(address platformContract, address tokenAddress) public view returns (uint256){
    externalPlatformContract exContract = externalPlatformContract(platformContract);
    return exContract.totalAmountStaked(tokenAddress);

  }

  function getAmountCurrentlyDepositedByContract(address platformContract, address tokenAddress, address userAddress) public view returns (uint256){

    externalPlatformContract exContract = externalPlatformContract(platformContract);
    return exContract.depositBalances(userAddress, tokenAddress);

  }

  function replaceAllStakableDirectory (string [] memory theNames, address[] memory theFarmAddresses, address[] memory theFarmTokens) onlyOwner public returns (bool){
    farmTokenPlusFarmNames = theNames;
    farmAddresses = theFarmAddresses;
    farmTokens = theFarmTokens;
    return true;

  }

  function getAmountCurrentlyFarmStakedByContract(address platformContract, address tokenAddress, address userAddress) public view returns (uint256){

    externalPlatformContract exContract = externalPlatformContract(platformContract);
    return exContract.getStakedPoolBalanceByUser(userAddress, tokenAddress);
  }

  function getUserTokenBalance(address userAddress, address tokenAddress) public view returns (uint256){
    ERC20 token = ERC20(tokenAddress);
    return token.balanceOf(userAddress);

  }
  function getUniswapPrice(address  [] memory theAddresses, uint amount) internal view returns (uint256[] memory amounts1){
        uint256 [] memory amounts = uniswap.getAmountsOut(amount,theAddresses );

        return amounts;

    }




}
function updateTVLAddress(address theAddress) onlyOwner public returns(bool){
    tvlOracleAddress = theAddress;
    tvlOracle = TVLOracle(theAddress);
    updateDirectory("TVLORACLE", theAddress);
    return true;
  }
//BELOW is other (admin and otherwise)