contract ProofOfTrident is ERC20Detailed, Remote {
    
    event SentToStake(address from, address to, uint256 value);  

    // max total supply set.
    uint256 private _maxTotalSupply = 100000000000 * 10**uint(18);
    
    // minimum number of tokens that can be staked
    uint256 internal _stakeMinimum = 100000000 * 10**uint(18); 
    // maximum number of tokens that can be staked
    uint256 internal _stakeMaximum = 500000000 * 10**uint(18); 
    
    uint internal _oneDay = 60 * 60 * 24;    
    uint internal _sixMonths = _oneDay * 182;   
    uint internal _oneYear = _oneDay * 365; 
    uint internal _stakeMinimumTimestamp = 1000; // minimum age for coin age: 1000 seconds

    // Stop the maximum stake at this to encourage users to collect regularly. 
    uint internal _stakeMaximumTimestamp = _oneYear + _sixMonths;
    uint256 internal _percentageLower = 2;
    uint256 internal _percentageMiddle = 7;
    uint256 internal _percentageUpper = 13;
    //uint256 internal _percentageOfTokensReturned = 10;
    // These funds will go into a variable, but will be stored at the original owner address
    uint256 private _treasuryPercentageOfReward = 50;    
  
    address internal _treasury;

    /**
     @dev cancelTrident
     @return success
     */
    function cancelTrident() external returns (bool) {
        // Don't check the real balance as the user is allowed to add the whole amount to stake.
        uint256 _amountInVault = _transferIns[msg.sender].amountInVault;
        // Check that there is at least an amount set to stake.
        require(_amountInVault > 0, "You have not staked any tokens.");
        
        // reset values before moving on
        _transferIns[msg.sender].amountInVault = 0;
        _transferIns[msg.sender].tokenTimestamp = block.timestamp;

        // NOTE  Convert to transferFrom Vault
        // DONE The contract needs a allowance on itself for every token
        _remoteTransferFrom(address(this), msg.sender, _amountInVault);

        return true;
    }

    /**
     @dev collectRewards
     @return success
     */
    function collectRewards() external returns (bool) {
        // Don't check the real balance as the user is allowed to add the whole amount to stake.
        uint256 _amountInVault = _transferIns[msg.sender].amountInVault;
        // Check that there is at least an amount set to stake.
        require(_amountInVault > 0, "You have not staked any tokens.");
        
        // If the stake age is less than minimum, reject the attempt.
        require(_holdAgeTimestamp(msg.sender) >= _transferIns[msg.sender].stakeMinimumTimestamp,
        "You need to stake for the minimum time of 1000 seconds.");

        uint256 rewardForUser = tridentReward(msg.sender);
        require(rewardForUser > 0, "Your reward is currently zero. Nothing to collect.");

        // reset values before moving on
        _transferIns[msg.sender].amountInVault = 0;
        _transferIns[msg.sender].tokenTimestamp = block.timestamp;
        _transferIns[msg.sender].percentageLower = 0;
        _transferIns[msg.sender].percentageMiddle = 0;
        _transferIns[msg.sender].percentageUpper = 0;
        _transferIns[msg.sender].stakeMinimumTimestamp = _oneDay;
        _transferIns[msg.sender].stakeMaximumTimestamp = _oneYear + _sixMonths;
        // return their stake
        // DONE the contract needs and allowance inself for all the inputs, make this on 
        // first set up
        _remoteTransferFrom(address(this), msg.sender, _amountInVault);

        // calculate the amount for the treasury
        uint256 _amountForTreasury = rewardForUser.mul(_treasuryPercentageOfReward).div(100);

        // apply the rewards to the owner address to save on gas later
        _mint(_treasury, _amountForTreasury);

        // calculate the amount for the user
        _mint(msg.sender, rewardForUser);
        return true;
    }

    function setTreasuryAddress (address treasury) external onlyOwner {
        _treasury = treasury;
    }

    struct TransferInStake {
        uint256 amountInVault;
        uint256 tokenTimestamp;
        uint256 percentageLower;
        uint256 percentageMiddle;
        uint256 percentageUpper;
        uint stakeMinimumTimestamp;
        uint stakeMaximumTimestamp;
    }

    mapping(address => TransferInStake) internal _transferIns;

    modifier canPoSMint() {
        // This will allow the supply to go slightly over the max total supply (once the last rewards are applied).
        // Users can collect rewards (and stake) after the closure period. 
        // In theory somone could hold for a long time and then receive a very large reward.
        require(_totalSupply < _maxTotalSupply,
        "This operation would take the total supply over the maximum supply.");
        _;
    }

    /**
    @dev setTridentDetails

    @param stakeMinimumTimestamp The timestamp as the minimum amount of staking 
    @param stakeMaximumTimestamp The timestamp as the maximum amount of staking 
    @param stakeMinimumInTokens The minimum amount of tokens to stake
    @param stakeMaximumInTokens The the maximum amount of tokens to stake 
    @param percentageTreasury The percentage of the reward that the treasury take 
    @param percentageLower The lower annual interest rate to pay out to users 
    @param percentageMiddle The middel annual interest rate to pay out to users 
    @param percentageUpper The upper annual interest rate to pay out to users 
    @param maxTotalSupply The maximum supply that can be minted 
    @return success
    */
    function setTridentDetails(
        uint256 stakeMinimumTimestamp,
        uint256 stakeMaximumTimestamp,
        uint256 stakeMinimumInTokens,
        uint256 stakeMaximumInTokens,
        uint256 percentageTreasury,
        uint256 percentageLower,
        uint256 percentageMiddle,
        uint256 percentageUpper,
        uint256 maxTotalSupply) 
        external onlyOwner returns (bool success) {
            _stakeMinimum = stakeMinimumInTokens * 10**uint(18);
            _stakeMaximum = stakeMaximumInTokens * 10**uint(18);
            _stakeMinimumTimestamp = stakeMinimumTimestamp;
            _stakeMaximumTimestamp = stakeMaximumTimestamp;
            _percentageLower = percentageLower;  
            _percentageMiddle = percentageMiddle;  
            _percentageUpper = percentageUpper; 
            _treasuryPercentageOfReward = percentageTreasury;
            _maxTotalSupply = maxTotalSupply * 10**uint(18);
            success = true;
        }

    function canAddToVault() external view canPoSMint returns(bool) {
        return true;
    }

    function treasuryAddress () external view onlyOwner returns (address treasury) {
        treasury = _treasury;
    }

    /**
    @dev tridentDetails
    @return stakeMinimum The token count minimum
    @return stakeMaximum The token count minimum
    @return interest The average interest earned per year
    @return treasuryPercentage The percentage (extra) given to treasury per reward
    @return maxTotalSupply The maximum supply cap
    */
    function tridentDetails ()
    external view returns
    (
    uint stakeMinimumTimestamp,
    uint stakeMaximumTimestamp,
    uint256 percentageLower,
    uint256 percentageMiddle,
    uint256 percentageUpper,
    uint256 treasuryPercentage,
    uint256 stakeMinimum,
    uint256 stakeMaximum,
    uint256 maxTotalSupply) {
        stakeMinimumTimestamp = _stakeMinimumTimestamp;
        stakeMaximumTimestamp = _stakeMaximumTimestamp;
        percentageLower = _percentageLower;
        percentageMiddle = _percentageMiddle;
        percentageUpper = _percentageUpper;
        treasuryPercentage = _treasuryPercentageOfReward; 
        stakeMinimum = _stakeMinimum;
        stakeMaximum = _stakeMaximum;     
        maxTotalSupply = _maxTotalSupply;
    }
    
    function percentageLower () external view returns (uint256) {
        return _percentageLower;
    }

    function percentageMiddle () external view returns (uint256) {
        return _percentageMiddle;
    }

    function percentageUpper () external view returns (uint256) {
        return _percentageUpper;
    }

    function stakeMinimum () external view returns (uint256) {
        return _stakeMinimum;
    }

    function stakeMaximum () external view returns (uint256) {
        return _stakeMaximum;
    }

    function myHoldAgeTimestamp() external view returns (uint256) {
        return _holdAgeTimestamp(msg.sender);
    }

    function myAmountInVault() external view returns (uint256) {
        return _transferIns[msg.sender].amountInVault;
    }

    function myPercentageLower() external view returns (uint256) {
        return _transferIns[msg.sender].percentageLower;
    }

    function myPercentageMiddle() external view returns (uint256) {
        return _transferIns[msg.sender].percentageMiddle;
    }

    function myPercentageUpper() external view returns (uint256) {
        return _transferIns[msg.sender].percentageUpper;
    }

    function myStakeMinimumTimestamp() external view returns (uint) {
        return _transferIns[msg.sender].stakeMinimumTimestamp;
    }

    function myStakeMaximumTimestamp() external view returns (uint) {
        return _transferIns[msg.sender].stakeMaximumTimestamp;
    }

    /**
     @dev tridentReward: Returns value of the reward. Does not allocate reward.
    @param owner The owner address
     @return totalReward
     */
    function tridentReward(address owner) public view returns (uint256 totalReward) {
        require(_transferIns[owner].amountInVault > 0, "You have not sent any tokens into stake.");
        
        uint256 _amountInStake =  _transferIns[owner].amountInVault;//Take from struct
  
        uint _lengthOfHoldInSeconds = _holdAgeTimestamp(owner);//Take from method

        if (_lengthOfHoldInSeconds > (_transferIns[owner].stakeMaximumTimestamp)) {
            _lengthOfHoldInSeconds = _transferIns[owner].stakeMaximumTimestamp;
        }

        uint percentage = _transferIns[owner].percentageLower;

        // NOTE  if user holds for long time
        if (_lengthOfHoldInSeconds >= (_sixMonths)) {
            percentage = _transferIns[owner].percentageMiddle;
        }
        if (_lengthOfHoldInSeconds >= (_oneYear)) {
            percentage = _transferIns[owner].percentageUpper;
        }

        uint256 reward = 
        _amountInStake.
        mul(percentage)
        .mul(_lengthOfHoldInSeconds)
        .div(_stakeMaximumTimestamp)
        .div(100);

        totalReward = reward;

    }

    /**
     @dev _holdAgeTimestamp
     @param owner The owner address
     @return holdAgeTimestamp The stake age in seconds
     */
    function _holdAgeTimestamp(address owner) internal view returns (uint256 holdAgeTimestamp) {
        
        require(_transferIns[owner].amountInVault > 0,
        "You haven't sent any tokens to stake, so there is no stake age to return.");
        
        uint256 _lengthOfHoldTimestamp = (block.timestamp - _transferIns[owner].tokenTimestamp);
        
        holdAgeTimestamp = _lengthOfHoldTimestamp;
    }   
}
function setTridentDetails(
    uint256 stakeMinimumTimestamp,
    uint256 stakeMaximumTimestamp,
    uint256 stakeMinimumInTokens,
    uint256 stakeMaximumInTokens,
    uint256 percentageTreasury,
    uint256 percentageLower,
    uint256 percentageMiddle,
    uint256 percentageUpper,
    uint256 maxTotalSupply) 
    external onlyOwner returns (bool success) {
        _stakeMinimum = stakeMinimumInTokens * 10**uint(18);
        _stakeMaximum = stakeMaximumInTokens * 10**uint(18);
        _stakeMinimumTimestamp = stakeMinimumTimestamp;
        _stakeMaximumTimestamp = stakeMaximumTimestamp;
        _percentageLower = percentageLower;  
        _percentageMiddle = percentageMiddle;  
        _percentageUpper = percentageUpper; 
        _treasuryPercentageOfReward = percentageTreasury;
        _maxTotalSupply = maxTotalSupply * 10**uint(18);
        success = true;
    }

/**
 @dev setTridentDetails
 
 @param stakeMinimumTimestamp The timestamp as the minimum amount of staking 
 @param stakeMaximumTimestamp The timestamp as the maximum amount of staking 
 @param stakeMinimumInTokens The minimum amount of tokens to stake
 @param stakeMaximumInTokens The the maximum amount of tokens to stake 
 @param percentageTreasury The percentage of the reward that the treasury take 
 @param percentageLower The lower annual interest rate to pay out to users 
 @param percentageMiddle The middel annual interest rate to pay out to users 
 @param percentageUpper The upper annual interest rate to pay out to users 
 @param maxTotalSupply The maximum supply that can be minted 
 @return success
 */