contract KWWBoatsVoting is IBoatsVoting, Ownable {
    address gameManager;

    int8 public voteDay1 = 2;
    int8 public voteDay2 = 3;
    uint8 public durationHours = 24;
    uint8 public daysBetweenVotes = 7;

    //tokenId => BoatData
    mapping(uint16 => BoatData) public boatsDetails;
    //boatState => State Vote data
    mapping(uint8 => TypeVote) public votes;

    /*
       EXECUTABLE FUNCTIONS
    */

    function setBoatDetails(uint16 idx, BoatData memory data)  public override onlyGameManager{
        boatsDetails[idx] = data;
    }

    function makeVote1(uint16 tokenId, uint256 vote, uint8 boatState) public override onlyGameManager{
        require(getCurrentOnlineVote() == 1, "Vote 1 is not online right now");
        require(getLastVoteTime(1) > getVoteTimeByDate(boatsDetails[tokenId].timestampVoted1 ,1), "Already Voted!");

        votes[boatState].priceSum += vote;
        votes[boatState].voters += 1;
        votes[boatState].avg = votes[boatState].priceSum / votes[boatState].voters;

        boatsDetails[tokenId].vote1 = vote;
        boatsDetails[tokenId].timestampVoted1 = uint48(block.timestamp);
    }

    function makeVote2(uint16 tokenId, bool vote, uint8 boatState) public onlyGameManager{
        require(getCurrentOnlineVote() == 2, "Vote 2 is not online right now");
        require(getLastVoteTime(2) > getVoteTimeByDate(boatsDetails[tokenId].timestampVoted2 ,2), "Already Voted!");

        votes[boatState].agreeToAvg += (vote ? int16(1) : int16(-1));
        
        boatsDetails[tokenId].vote2 = vote;
        boatsDetails[tokenId].timestampVoted2 = uint48(block.timestamp);
    }

    function updatePrice(uint8 boatState) public onlyGameManager {
        require(getCurrentOnlineVote() == 0, "Vote is online");
        require(votes[boatState].voters > 0, "No one voted this week");

        if(votes[boatState].agreeToAvg > 0){
            votes[boatState].finalPrice = votes[boatState].avg;
        }

        votes[boatState].voters = 0;
        votes[boatState].priceSum = 0;
        votes[boatState].avg = 0;
        votes[boatState].agreeToAvg = 0;
    }

    function setVoteDetails(uint8 boatState, TypeVote memory vote) public onlyGameManager {
        votes[boatState] = vote;
    }

    function setFinalPrice(uint8 boatState, uint256 finalPrice) public onlyGameManager {
        votes[boatState].finalPrice = finalPrice;
    }

    function setPriceSum(uint8 boatState, uint256 priceSum) public onlyGameManager {
        votes[boatState].priceSum = priceSum;
    }
    
    function setAvg(uint8 boatState, uint256 avg) public onlyGameManager {
        votes[boatState].avg = avg;
    }

    function setVoters(uint8 boatState, uint16 voters) public onlyGameManager {
        votes[boatState].voters = voters;
    }
    
    function setAgreeToAvg(uint8 boatState, int16 agreeToAvg) public onlyGameManager {
        votes[boatState].agreeToAvg = agreeToAvg;
    }

    function setVote1(uint16 tokenId, uint256 vote1) public onlyGameManager {
        boatsDetails[tokenId].vote1 = vote1;
    }

    function setVote2(uint16 tokenId, bool vote2) public onlyGameManager {
        boatsDetails[tokenId].vote2 = vote2;
    }
    
    function setTimestampVoted1(uint16 tokenId, uint48 timestampVoted1) public onlyGameManager {
        boatsDetails[tokenId].timestampVoted1 = timestampVoted1;
    }
    
    function setTimestampVoted2(uint16 tokenId, uint48 timestampVoted2) public onlyGameManager {
        boatsDetails[tokenId].timestampVoted2 = timestampVoted2;
    }
    
    /*
        GETTERS
    */

    function getBoatDetails(uint16 idx) public override view returns(BoatData memory){
        return boatsDetails[idx];
    }
    
    function getVoteDetails(uint8 boatState) public view returns(TypeVote memory){
        return votes[boatState];
    }

    function getAveragePrice(uint8 boatState) public view returns(uint256){
        return votes[boatState].avg;
    }

    function getPrice(uint8 boatState) public view returns(uint256){
        return votes[boatState].finalPrice;
    }

    function getLastVoteTime(uint8 voteType) public override view returns(int){
        return getVoteTimeByDate(block.timestamp, voteType);
    }

    function getVoteTimeByDate(uint256 timestamp, uint8 voteType) public override view returns (int){
        int8 voteDay = voteType == 1 ? voteDay1 : voteDay2;

        int8 delta = voteDay - int8(KWWUtils.getWeekday(timestamp));
        if(delta > 0) delta = delta - 7;
        int date = (int(timestamp) + delta * int64(KWWUtils.DAY_IN_SECONDS));
        return  date - date % int64(KWWUtils.DAY_IN_SECONDS);
    }

    function getCurrentVoteEndTime() public view returns(int256) {
        uint8 onlineVote = getCurrentOnlineVote();

        if(onlineVote == 1){
            return getLastVoteTime(1) + int64(durationHours * KWWUtils.HOUR_IN_SECONDS);
        }
        
        if(onlineVote == 2){
            return getLastVoteTime(2) + int64(durationHours * KWWUtils.HOUR_IN_SECONDS);
        }

        return 0;
    }

    function getCurrentOnlineVote() public override view returns (uint8) {
        int lastVote1 = getLastVoteTime(1);
        int lastVote2 = getLastVoteTime(2);

        //Vote1 Time
        if(lastVote1 > lastVote2 && lastVote1 + int64(durationHours * KWWUtils.HOUR_IN_SECONDS) > int(block.timestamp)){
            return 1;
        }

        if(lastVote2 + int64(durationHours * KWWUtils.HOUR_IN_SECONDS) > int(block.timestamp)){
            return 2;
        }

        return 0;
    }    

    /*
        MODIFIERS
    */

    modifier onlyGameManager() {
        require(gameManager != address(0), "Game manager not exists");
        require(msg.sender == owner() || msg.sender == gameManager, "caller is not the game manager");
        _;
    }

    /*
        ONLY OWNER
    */

    function setVoteDay1(uint8 newDay) public override onlyOwner {
      voteDay1 = int8(newDay);
    }

    function setVoteDay2(uint8 newDay) public override onlyOwner {
      voteDay2 = int8(newDay);
    }

    function setDurationHours(uint8 newHours) public override onlyOwner {
        require(newHours > 0, "Must be more than 0 and less than time between votes");
        durationHours = newHours;
    }

    function setGameManager(address _newAddress) public onlyOwner{
        gameManager = _newAddress;
    }
}
function setVoteDay1(uint8 newDay) public override onlyOwner {
  voteDay1 = int8(newDay);
}

/*
 ONLY OWNER
 */