contract RewardManagerForVault  {
    using SafeMath for uint256;
    using SafeDecimalMath for uint;


    uint256 public mRewardPerBlock;

    struct RewardData{
        uint mShareInfo;
        uint mShareAmount;
        uint mReward;
    }

    // mapping (uint => uint) public mShareInfo;
    // mapping (uint => uint) public mShareAmount;
    mapping (uint => RewardData ) public mDatas;

    uint256 public mAmount;           
    uint256 public mLastRewardBlock;  
    uint256 public mAccRewardPerShare;

    uint256 public mStartBlock;
    uint256 public mRemainReward;
    uint256 public mAccReward;


    constructor( uint256 rewardPerBlock, uint256 startBlock ) public {
        mRewardPerBlock = rewardPerBlock;
        mStartBlock = startBlock;
        mLastRewardBlock = mStartBlock;
    }


    function calcReward( uint256 curBlock, uint key ) public view returns (uint256 reward ) {
        uint256 accRewardPerShare = mAccRewardPerShare;
        if (curBlock > mLastRewardBlock && mAmount != 0) {
            uint256 multiplier = curBlock.sub( mLastRewardBlock, "cr curBlock sub overflow" );
            uint256 curReward = multiplier.mul(mRewardPerBlock);
            accRewardPerShare = mAccRewardPerShare.add(curReward.divideDecimalRoundPrecise(mAmount));
        }
        RewardData memory data = mDatas[ key ];
        uint shareStart = data.mShareInfo;
        reward = data.mShareAmount.multiplyDecimalRoundPrecise( accRewardPerShare.sub(shareStart,"reward share")).add( data.mReward);
    }


    function totalRemainReward() public view returns( uint256 ){
        return mRemainReward;
    } 

    function totalDepositAmount( ) public view returns( uint256 ){
        return mAmount;
    }

    function getShareStart(uint key) public view returns(uint ) {
        RewardData memory data = mDatas[ key ];
        return data.mShareInfo;
    }


    function getDepositAmount(uint key) public view returns(uint ) {
        RewardData memory data = mDatas[ key ];
        return data.mShareAmount;
    }

    //event LogDebug( uint256 u1, uint256 u2, uint256 u3, uint256 u4 );
    function _update( uint256 curBlock ) internal {
        if (curBlock <= mLastRewardBlock) {
            return;
        }
        if (mAmount == 0) {
            mLastRewardBlock = curBlock;
            return;
        }
        uint256 multiplier = curBlock.sub( mLastRewardBlock, "_update curBlock sub overflow" );
        uint256 curReward = multiplier.mul(mRewardPerBlock);
        mRemainReward = mRemainReward.add( curReward );
        mAccReward = mAccReward.add( curReward );

        mAccRewardPerShare = mAccRewardPerShare.add(curReward.divideDecimalRoundPrecise(mAmount));
        mLastRewardBlock = curBlock;
        //emit LogDebug( multiplier, curReward, mAmount, curReward.divideDecimalRoundPrecise(mAmount) );        
    }

    function _deposit( uint256 curBlock, uint key, uint256 amount) internal {
        _update( curBlock );
        RewardData storage data = mDatas[ key ];
        uint reward = 0;
        if( data.mShareAmount > 0 ){
            // reward
            uint shareStart = data.mShareInfo;
            uint shareAmount = data.mShareAmount;
            reward = shareAmount.multiplyDecimalRoundPrecise( mAccRewardPerShare.sub(shareStart,"reward share"));
            data.mShareAmount = shareAmount.add( amount );
        }
        else{
            data.mShareAmount = amount;
        }
        data.mShareInfo = mAccRewardPerShare;

        if(amount > 0) {
            mAmount = mAmount.add(amount);
        }
        data.mReward = data.mReward.add( reward );
    }

    function _withdraw( uint256 curBlock, uint key, uint256 amount) internal {
        RewardData storage data = mDatas[ key ];
        require( data.mShareAmount >= amount, "_withdraw amount error" );
        _update( curBlock );
        uint reward = 0;
        if( data.mShareAmount > 0 ){
            // reward
            uint shareStart = data.mShareInfo;
            reward = data.mShareAmount.multiplyDecimalRoundPrecise( mAccRewardPerShare.sub(shareStart,"reward share"));
            data.mShareAmount = data.mShareAmount.sub( amount, "sub share amount");
        }
        data.mShareInfo = mAccRewardPerShare;
        data.mReward = data.mReward.add( reward );
 
        if(amount > 0) {
            mAmount = mAmount.sub(amount, "_withdraw mAmount sub overflow");
        }
    }

    function _claim( uint256 curBlock, uint key ) internal returns( uint reward ) {
        RewardData storage data = mDatas[ key ];
        _update( curBlock );
        if( data.mShareAmount > 0 ){
            // reward
            uint shareStart = data.mShareInfo;
            reward = data.mShareAmount.multiplyDecimalRoundPrecise( mAccRewardPerShare.sub(shareStart,"reward share"));
        }
        data.mShareInfo = mAccRewardPerShare;
        reward = reward.add( data.mReward);
        data.mReward = 0;
        mRemainReward = mRemainReward.sub( reward, "_claim remain reward");
    }

}
function _update( uint256 curBlock ) internal {
    if (curBlock <= mLastRewardBlock) {
        return;
    }
    if (mAmount == 0) {
        mLastRewardBlock = curBlock;
        return;
    }
    uint256 multiplier = curBlock.sub( mLastRewardBlock, "_update curBlock sub overflow" );
    uint256 curReward = multiplier.mul(mRewardPerBlock);
    mRemainReward = mRemainReward.add( curReward );
    mAccReward = mAccReward.add( curReward );

    mAccRewardPerShare = mAccRewardPerShare.add(curReward.divideDecimalRoundPrecise(mAmount));
    mLastRewardBlock = curBlock;
    //emit LogDebug( multiplier, curReward, mAmount, curReward.divideDecimalRoundPrecise(mAmount) );        
}

//event LogDebug( uint256 u1, uint256 u2, uint256 u3, uint256 u4 );