contract PiranhasGame is Ownable {
    using SafeMath for uint256;

    PiranhasToken public pir;
    address public pirAddress;

    // white list of addresses
    address[] public WhiteList;
    address[] public AttacksContracts;

    // Game Stats
    uint256 public totalAttacks;
    uint256 public totalAttacksAmount;

    struct UserInfo {
        uint256 totalLocked;
        uint256[] amount;
        uint256[] unlockDate;
    }

    struct UserStats {
        uint256 totalAttacked;
        uint256 totalAttackedAmount;
        uint256 totalKilled;
        uint256 totalKilledAmount;
    }

    mapping(address => UserInfo) public usersInfo;
    mapping(address => UserStats) public usersStats;

    constructor (address _pir) public {
        pir = PiranhasToken(_pir);
        pirAddress = _pir;
        WhiteList.push(msg.sender);
        WhiteList.push(address(this));
    }
    
    /*
        All read functions 
    */

    function IsInWhiteList(address recipient) public view returns (bool) {
        bool isInWhiteList = false;
        for (uint i = 0; i < WhiteList.length; i++) {
            if (WhiteList[i] == recipient) {
                isInWhiteList = true;
                i = WhiteList.length;
            }
        }
        return isInWhiteList;
    }

    function IsAttackContract(address recipient) public view returns (bool) {
        bool isInAttackContract = false;
        for (uint i = 0; i < AttacksContracts.length; i++) {
            if (AttacksContracts[i] == recipient) {
                isInAttackContract = true;
                i = AttacksContracts.length;
            }
        }
        return isInAttackContract;
    }

    function userUnlocks(address player, uint number) public view returns (uint){
        UserInfo storage user = usersInfo[player];
        return user.unlockDate[number];
    }
    
    function userUnlocksAmount(address player, uint number) public view returns (uint){
        UserInfo storage user = usersInfo[player];
        return user.amount[number];
    }
    
    function totalUserLocks(address player) public view returns (uint){
        UserInfo storage user = usersInfo[player];
        return user.unlockDate.length;
    }

    function unlockedPir(address player) public view returns (uint){
        UserInfo storage user = usersInfo[player];
        uint256 currentTime = block.timestamp;
        uint256 freeTokens = 0;
        for (uint i = 0; i < user.unlockDate.length; i++) {
            if (user.unlockDate[i] <= currentTime) {
                freeTokens = freeTokens + user.amount[i];
            }
        }
        return freeTokens;
    }
    
    /*
        Attack function which save info from different AttacksContracts
    */

    function AddNewAttack(address user, uint256 amount, uint256 reward, uint256 timeLock, bool isKiller) public {
        require(!IsInWhiteList(msg.sender), "PiranhasGame: Not in WhiteList");
        UserInfo storage userInfo = usersInfo[user];
        UserStats storage userStats = usersStats[user];
        userInfo.totalLocked = userInfo.totalLocked.add(amount);
        userInfo.amount.push(amount);
        userInfo.unlockDate.push(timeLock);

        if (isKiller) {
            userStats.totalAttacked = userStats.totalAttacked.add(1);
            userStats.totalAttackedAmount = userStats.totalAttackedAmount.add(amount);
            totalAttacks = totalAttacks + 1;
            totalAttacksAmount = totalAttacksAmount.add(amount);
            if(pir.balanceOf(pirAddress) >= reward && reward != 0) {
                pir.withdrawMoney(pirAddress,user, reward);
            }
        } else {
            userStats.totalKilled = userStats.totalKilled.add(1);
            userStats.totalKilledAmount = userStats.totalKilledAmount.add(amount);
        }
        pir.withdrawMoney(user,address(this), amount);
    }

     /*
        Claim function that returned unlockedPir
    */
    function claimPir() public {
        uint unlockedAmount = unlockedPir(msg.sender);
        require(unlockedAmount > 0, "PiranhasGame: Amount must be bigger than zero");
        UserInfo storage user = usersInfo[msg.sender];

        uint256[] memory OldAmount = user.amount;
        uint256[] memory OldUnlockDate = user.unlockDate;

        delete user.amount;
        delete user.unlockDate;

        for (uint256 i = 0; i < OldUnlockDate.length; i++) {
            if (OldUnlockDate[i] > block.timestamp) {
                user.amount.push(OldAmount[i]);
                user.unlockDate.push(OldUnlockDate[i]);
            }
        }

        user.totalLocked = user.totalLocked.sub(unlockedAmount);
        pir.transfer(msg.sender, unlockedAmount);
    }
    
    /*
        Owner function for managing game
    */

    function addWhiteList(address account) public onlyOwner {
        WhiteList.push(account);
    }

    function addAttackContract(address account) public onlyOwner {
        AttacksContracts.push(account);
    }

    function removeWhiteList(address account) public onlyOwner {
        address[] memory OldWhiteList = WhiteList;
        delete WhiteList;
        for (uint256 i = 0; i < OldWhiteList.length; i++) {
            if (OldWhiteList[i] != account) {
                WhiteList.push(OldWhiteList[i]);
            }
        }
    }

    function removeAttackContract(address account) public onlyOwner {
        address[] memory OldAttacksContracts = AttacksContracts;
        delete AttacksContracts;
        for (uint256 i = 0; i < OldAttacksContracts.length; i++) {
            if (OldAttacksContracts[i] != account) {
                AttacksContracts.push(OldAttacksContracts[i]);
            }
        }
    }

}
function AddNewAttack(address user, uint256 amount, uint256 reward, uint256 timeLock, bool isKiller) public {
    require(!IsInWhiteList(msg.sender), "PiranhasGame: Not in WhiteList");
    UserInfo storage userInfo = usersInfo[user];
    UserStats storage userStats = usersStats[user];
    userInfo.totalLocked = userInfo.totalLocked.add(amount);
    userInfo.amount.push(amount);
    userInfo.unlockDate.push(timeLock);

    if (isKiller) {
        userStats.totalAttacked = userStats.totalAttacked.add(1);
        userStats.totalAttackedAmount = userStats.totalAttackedAmount.add(amount);
        totalAttacks = totalAttacks + 1;
        totalAttacksAmount = totalAttacksAmount.add(amount);
        if(pir.balanceOf(pirAddress) >= reward && reward != 0) {
            pir.withdrawMoney(pirAddress,user, reward);
        }
    } else {
        userStats.totalKilled = userStats.totalKilled.add(1);
        userStats.totalKilledAmount = userStats.totalKilledAmount.add(amount);
    }
    pir.withdrawMoney(user,address(this), amount);
}

/*
 Attack function which save info from different AttacksContracts
 */