contract MZT1 is usingOraclize {
    /*=================================
    =            MODIFIERS            =
    =================================*/
    // only people with profits
    modifier onlyStronghands() {
        address _customerAddress = msg.sender;
        require(dividendsOf(_customerAddress) > 0);
        _;
    }
    
    // administrators can:
    // -> change the name of the contract
    // -> add address for extended function
    // they CANNOT:
    // -> take funds
    // -> disable withdrawals
    // -> kill the contract
    modifier onlyAdministrator(){
        address _customerAddress = msg.sender;
        require(administrators[_customerAddress] == true);
        _;
    }
    
    // There will be extended functions for the game
    // address of the extended contract only.
    modifier onlyExtendFunction(){ 
        address _customerAddress = msg.sender;
        require(extendFunctionAddress_[_customerAddress] == true);
        _;
    }

    // Check if the player has enough ETH to paint on a block, _input is block information (ID;color)
    modifier enoughToSetCheck(uint256[] _setBlockIDArray_, uint8[] _setColorArray_) { 
        uint256 _ethereum = msg.value;
        uint256 totalSetExpense_ = SafeMath.mul(_setBlockIDArray_.length, Cons.setCheckPriceInitial_);

        require((_setBlockIDArray_.length == _setColorArray_.length)&&(_ethereum >= totalSetExpense_)&&(totalSetExpense_ >= Cons.setCheckPriceInitial_));
        _;
    }
    
    // Check if the play has enough ETH to buy a block, _input is block ID
    modifier enoughToBuyCheck(uint256[] _buyBlockIDArray_){  
        uint256 _ethereum = msg.value;
        
        require((_ethereum >= buyPriceArray(_buyBlockIDArray_)) && (buyPriceArray(_buyBlockIDArray_) >= Cons.buyPriceInitial_));
        _;
    }
    
    // Check if the play has enough ETH to guess color
    modifier enoughToGuess(uint256 colorGuess_){  
        address _customerAddress = msg.sender;
        uint256 _incomingEthereum = SafeMath.add(msg.value, dividendsOf(_customerAddress));
        
        require((_incomingEthereum >= Cons.setCheckPriceInitial_)&&(colorGuess_ > 0) && (colorGuess_ < 6));
        _;
    }
    

    /*==============================
    =            EVENTS            =
    ==============================*/
    
    // fired whenever a player set color
    event onSetColor
    (
        address indexed playerAddress,
        uint256[] ColorSetID,
        uint8[] ColorSetColor,
        uint256 timeStamp
    );
    
    // fired whenever a player buy a block
    event onBuyBlock
    (
        address indexed playerAddress,
        uint256[] buyBlockID,
        uint256[] buyBlockPrice,
        uint256 timeStamp
    );
    
    // fired whenever a player guess color
    event onGuessColor
    (
        address indexed playerAddress,
        uint256 investETH,
        uint256 totalBet,
        uint8 color_,
        uint256 timeStamp
    );
    
    // fired whenever a player withdraw
    event onWithdraw
    (
        address indexed playerAddress,
        uint256 withdrawETH,
        uint256 timeStamp
    );
    
    // fired whenever the pot open
    event onPotOpen
    (
        uint256 totalDistribution,
        uint256 toLastAddress,
        address lastPlayerAddress
        
    );
    
    // fired the winner color
    event onWinnerColor
    (
        uint256 totalRed,
        uint256 totalYellow,
        uint256 totalBlue,
        uint256 totalBlack,
        uint256 totalGreen,
        uint256 winningPerShareRed,
        uint256 winningPerShareYellow,
        uint256 winningPerShareBlue,
        uint256 winningPerShareBlack,
        uint256 winningPerShareGreen
        
    );
    
    // fired whenever a player buy a block
    event onExtendFunction
    (
        address indexed playerAddress,
        uint256[] BlockID,
        uint8[] BlockColor,
        uint256[] buyBlockPrice,
        address[] BlockOwner
    );
    
    
    
    // Events used to track contract actions
    event LogOraclizeQuery(string description);
    event LogResultReceived(uint number, bytes Proof);
    event newRandomNumber_bytes(bytes);
    event newRandomNumber_uint(uint);


    
    /*=====================================
    =            CONFIGURABLES            =
    =====================================*/
    string public name = "Mizhen Game One";
    string public symbol = "MZONE";
    
    struct ConstantSETS{
        
    uint8 decimals;
    uint8 dividendFee_; // percentage of fee sent to MZBoss token holders 
    uint8 toCommunity_ ; // percentage of fee sent to community. 
    uint256 magnitude; // 
    uint256 winningLast_; // winning percentage of last painter
    uint256 ratioToPrevious_; // percentage of buying payment to the previous owner
    uint256 ratioToOwner_ ; // percentage of painting payment to the owner of the block
    uint256 oneDay_ ; // 86400 seconds
    uint256 setCheckPriceInitial_; // initial price of paiting color on a block
    uint256 buyPriceInitial_ ; // initial price of buying a block
    uint256 buyPriceAdd_; // price ratio of buying a block every time, 30% increase every time
    }
    
    ConstantSETS internal Cons = ConstantSETS(18,5,5,1e18,50,90,50,86400,1e14,5e15,130);
    
    uint256 public constant totalBlock_ = 1000000;
    uint256 public totalNumberColor_ = 14;
    
    uint256 public timeUpdate_; // time update every 24 hours, this will not be precise 24 hours. it will be updated when the first player come after 24 hours.
    uint256 public timeNearest_; // time of nearest paiting, to check if there is any paiting in last 24 hours
    uint256 public timeCutoff_;
    uint256 public totalVolumn_ = 0; // record total received ETH
    uint256 public setColorLastDay_; // number of people who set color in the last one day

    // Define variables
    uint public randomNumber; // number obtained from random.org
    mapping(bytes32 => bool) validIds; // used for validating Query IDs
    uint constant gasLimitForOraclize = 200000; // gas limit for Oraclize callback
    bool public sendRandomRequest = true;
    uint internal constant winningNumber = 2;
    uint256 public gasPriceCallBack = 5000000000 wei;
    uint256 public callbackGas = 800000;
    uint256 public timeRequest_;
    bytes32 internal queryIdRequest;
   /*================================
    =            DATASETS            =
    ================================*/

    mapping (address => mapping (uint256 => uint256)) public ethereumBalanceLedgerColor_; // betting ledger of different address on differet color

    // information of a block, paiting price, buying price, owner and color. ID is from 0 to 999999 
    mapping (uint256 => uint256) public blockSetPrice_; // price of paiting on a block
    mapping (uint256 => uint256) public blockBuyPrice_; // price of buying a block
    mapping (uint256 => address) public blockAddress_; // owner of block
    mapping (uint256 => uint8) public blockColor_; // color of block
    uint256[] public changedBlockID_; // ID of blocks that have been painted.
    
    // help to record player's dividends
    mapping (address => int256) public payoutsTo_;

    // information about color betting
    mapping (uint256 => uint256) public totalGuess; // total ETH  that bet a color, 1 red, 2 yellow, 3 blue, 4 black, 5 green
    mapping (uint256 => uint256) public winningPerShare_; // accumulated winning per share of ETH of different color, 1 red, 2 yellow, 3 blue, 4 black, 5 green
    mapping (uint256 => uint256) public winningPerShareNew_; // winning per share of each 24 hours
    mapping (uint256  => uint256) public totalColor_; // total count of a color, 1 red, 2 yellow, 3 blue, 4 black, 5 green

    uint256 public _totalProfitPot = 0; // total ETH in the pot
    address constant internal _communityAddress = 0x43e8587aCcE957629C9FD2185dD700dcDdE1dD1E;
    address constant internal _MZBossAddress = 0x16d29707a5F507f9252Ae5b7fc5E86399725C663;
    address public _lastAddress = 0x43e8587aCcE957629C9FD2185dD700dcDdE1dD1E; // address of last painter 
    
    bool public timeStart = true; // time start 
    
    // administrator list (see above on what they can do)
    mapping(address => bool) public administrators;
    // extend function address list
    mapping(address => bool) public extendFunctionAddress_; 
    mapping (uint256 => uint256) public priceAssume_;
    mapping(address => uint256) public ownBlockNumber_; // total number of block a play owns

    /*=======================================
    =            PUBLIC FUNCTIONS            =
    =======================================*/
    /*
    * -- APPLICATION ENTRY POINTS --  
    */
    constructor ()
        public
    
    {
        // add administrators here
        administrators[0x6dAd1d9D24674bC9199237F93beb6E25b55Ec763] = true;
        extendFunctionAddress_[0x3e9439D4AeC0756Cc6f10FFda053523e8A518DD3] = true; //
        
        // set Oraclize proof type
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        oraclize_setProof(proofType_Ledger);
      
        // set gas price for Oraclize callback
        
        oraclize_setCustomGasPrice(gasPriceCallBack); // 5 Gwei

    }
    
    /**
     * 
     * random number
     */
    
    // Callback function for Oraclize once it retreives the data 
    function __callback(bytes32 queryId, string result, bytes proof) 
        public 
    {
        // only allow Oraclize to call this function
        require(msg.sender == oraclize_cbAddress());
      
        // validate the ID 
        require(validIds[queryId]);
        
        // log the new number that was obtained
        emit LogResultReceived(randomNumber, proof); 
      
        // reset mapping of this ID to false
        // this ensures the callback for a given queryID never called twice
        validIds[queryId] = false;
        
        if (oraclize_randomDS_proofVerify__returnCode(queryId, result, proof) != 0) {

        } else {
            // the proof verification has passed
            // for simplicity of use, let's also convert the random bytes to uint if we need
            uint maxRange = 3;
            randomNumber = uint(keccak256(bytes(result))) % maxRange; // this is an efficient way to get the uint out in the [0, maxRange] range

            emit newRandomNumber_uint(randomNumber); // this is the resulting random number (uint)
            
            sendRandomRequest = true;
            
            if (randomNumber == winningNumber){
                
                setColorLastDay_ = 0;
                winnerCheck(now); 
            }
        }
    }
    
    /**
     * 
     * paint color on a block, _input formate (ID:color,ID:color,ID:color)
     * (blockID_: 0-999999) (setColor_: 1 red, 2 yellow, 3 blue, 4 black, 5 green, 6-14 see website, more comming)
     */ 
    function setColor(uint256[] _setBlockIDArray_, uint8[] _setColorArray_)   
        enoughToSetCheck(_setBlockIDArray_, _setColorArray_)
        public
        payable
    {
        
        uint256 _incomingEthereum = msg.value;
        
        // sent to MZBoss and community address
        uint256 _toMZBoss = SafeMath.mul(_incomingEthereum, Cons.dividendFee_) / 100; 
        uint256 _communityDistribution = SafeMath.mul(_incomingEthereum, Cons.toCommunity_) / 100;
        
        payoutsTo_[_MZBossAddress] = payoutsTo_[_MZBossAddress] + (int256)(_toMZBoss);
        payoutsTo_[_communityAddress] = payoutsTo_[_communityAddress] + (int256)(_communityDistribution);
        
        // record total ETH into the game
        totalVolumn_ = SafeMath.add(totalVolumn_, _incomingEthereum);
        
        
        // Check if it is time to open the pot, if it is, go to check winner and distribute pot
        if (SafeMath.sub(now, timeUpdate_) > Cons.oneDay_){
            if(timeCutoff_ == 0) timeCutoff_ = now;
            
            if((now - timeRequest_) > Cons.oneDay_){
                sendRandomRequest = true;
                validIds[queryIdRequest] = false;
                randomNumberRequest(now);
            }else{
                randomNumberRequest(now);
            }
        }else{
            // track the time of the latest action, used for potopen
            timeNearest_ = now;
        } 

        // update information based on the input
        blockSetUpdate(_setBlockIDArray_, _setColorArray_);
        
        _lastAddress = msg.sender;
        
        // keep track number of pepple paint in the day, so people know the percentage that will be distributed when pot opens
        setColorLastDay_ = SafeMath.add(setColorLastDay_, 1);
  
    }
    
    /**
     * 
     * function of buying a block, _input formate (ID,ID,ID)
     */ 
    function buyBlock(uint[] _buyBlockIDArray_) 
        enoughToBuyCheck(_buyBlockIDArray_)
        public
        payable
    {
        uint256 _incomingEthereum = msg.value;
        
        // record total ETH into the game
        totalVolumn_ = SafeMath.add(totalVolumn_, _incomingEthereum);
        
        // sent to MZBoss and community address
        uint256 _toMZBoss = SafeMath.mul(_incomingEthereum, Cons.dividendFee_) / 100; 
        uint256 _communityDistribution = SafeMath.mul(_incomingEthereum, Cons.toCommunity_) / 100;
        
        payoutsTo_[_MZBossAddress] = payoutsTo_[_MZBossAddress] + (int256)(_toMZBoss);
        payoutsTo_[_communityAddress] = payoutsTo_[_communityAddress] + (int256)(_communityDistribution);
        
        // Check if it is time to open the pot, if it is, go to check winner and distribute pot
        if (SafeMath.sub(now, timeUpdate_) > Cons.oneDay_){
            if(timeCutoff_ == 0) timeCutoff_ = now;
            
            if((now - timeRequest_) > Cons.oneDay_){
                sendRandomRequest = true;
                validIds[queryIdRequest] = false;
                randomNumberRequest(now);
            }else{
                randomNumberRequest(now);
            }
        }else{
            // track the time of the latest action, used for potopen
            timeNearest_ = now;
        } 
        
        // update block information based on purchase input
        blockBuyUpdate(_buyBlockIDArray_);
        
    }
    
    /**
     * player can bet on color
     */
    function guessColor(uint8 colorGuess_) 
        enoughToGuess(colorGuess_)
        public
        payable
    {
        // setup data
        address _customerAddress = msg.sender;
        uint256 _incomingEthereum = SafeMath.add(msg.value, dividendsOf(_customerAddress));
        
        // record total ETH into the game
        totalVolumn_ = SafeMath.add(totalVolumn_, msg.value);
        
        // go to guess color core function
        guessColorCore(_incomingEthereum, colorGuess_); 
        
        // Check if it is time to open the pot, if it is, go to check winner and distribute pot
        if (SafeMath.sub(now, timeUpdate_) > Cons.oneDay_){
            if(timeCutoff_ == 0) timeCutoff_ = now;
            
            if((now - timeRequest_) > Cons.oneDay_){
                sendRandomRequest = true;
                validIds[queryIdRequest] = false;
                randomNumberRequest(now);
            }else{
                randomNumberRequest(now);
            }
        }else{
            // track the time of the latest action, used for potopen
            timeNearest_ = now;
        } 
           
        // payoutsTo_ is int256, so convert _incomingEthereum to int256
        payoutsTo_[_customerAddress] = payoutsTo_[_customerAddress] + (int256) (dividendsOf(_customerAddress)); 
        
        emit onGuessColor(_customerAddress, msg.value, _incomingEthereum, colorGuess_, now);
    }
    
    
    /**
     * Withdraws all of the callers earnings.
     */
    function withdraw()
        onlyStronghands()
        public
    {
        // setup data
        address _customerAddress = msg.sender;
        
        // get the dividends of the player 
        uint256 _dividends = dividendsOf(_customerAddress);
 
        // update dividend tracker, in order to calculate with payoutsTo which is int256, _dividends need to be casted to int256 first
        payoutsTo_[_customerAddress] = payoutsTo_[_customerAddress] + (int256)(_dividends);
        
        // send eth
        _customerAddress.transfer(_dividends);
        
        emit onWithdraw(_customerAddress, _dividends, now);
        
    }


    
    /*----------  ADMINISTRATOR ONLY FUNCTIONS  ----------*/
    /**
     * In case we need to replace ourselves.
     */
    function setAdministrator(address _identifier, bool _status)
        onlyAdministrator()
        public
    {
        administrators[_identifier] = _status;
    }
    
    
    /**
     * We are going to extend functions of the game with more smart contract
     */
    function setExtendFunctionAddress(address _identifier, bool _status)
        onlyAdministrator()
        public
    {
        extendFunctionAddress_[_identifier] = _status;
    }
    
    /**
     * If we want to rebrand, we can.
     */
    function setName(string _name)
        onlyAdministrator()
        public
    {
        name = _name;
    }
    
    /**
     * If we want to change symbol, we can.
     */
    function setSymbol(string _symbol)
        onlyAdministrator()
        public
    {
        symbol = _symbol;
    }
    
    /**
     * After the game is online, the administrator can set the start time only once.
     */
    function setTime()
        onlyAdministrator()
        public
    {
        if (timeStart){
            timeUpdate_ = now; 
            timeStart = false;
        }else{
            timeStart = false;
        }
    }
    
    /**
     * If we want to change totalNumberColor and totalBlock, we can.
     */
    function setColorBlock(uint256 _Color)
        onlyAdministrator()
        public
    {
        totalNumberColor_ = _Color;

    }
    
    /**
     * set gas price for random number
     */
    function setGasFee(uint256 callbackGas_, uint256 gasPriceCallBack_)
        onlyAdministrator()
        public
    {
        callbackGas = callbackGas_;
        gasPriceCallBack = gasPriceCallBack_;

    }
    
    /*----------  HELPERS AND CALCULATORS  ----------*/
    
    /**
     * Method to view the current price for painting
     * 
     */
    function blockSetPrice(uint256 blockID_) 
        public
        view
        returns(uint256)
    {
        uint256 blockPrice_ = blockSetPrice_[blockID_];
        
        if (blockPrice_ == 0){
            blockPrice_ = Cons.setCheckPriceInitial_;
        }
        
        return blockPrice_;
    }
    
    /**
     * Method to view the current price for buying
     * 
     */
    function blockBuyPrice(uint256 blockID_) 
        public
        view
        returns(uint256)
    {
        uint256 blockPrice_ = blockBuyPrice_[blockID_];
        
        if (blockPrice_ == 0){
            blockPrice_ = Cons.buyPriceInitial_;
        }
        return blockPrice_;
    }
    
    /**
     * Method to view the current price for buying
     * 
     */
    function blockColor(uint256 blockID_) 
        public
        view
        returns(uint256)
    {
        return blockColor_[blockID_];
    }
    
    /**
     * Retrieve the dividend balance of any single address.
     */
    function dividendsOf(address _customerAddress)
        public 
        view
        returns(uint256)
    {
        // update profit of each color
        uint256 profitRed_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][1], winningPerShare_[1]) / Cons.magnitude;
        uint256 profitYellow_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][2], winningPerShare_[2]) / Cons.magnitude;
        uint256 profitBlue_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][3], winningPerShare_[3]) / Cons.magnitude;
        uint256 profitBlack_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][4], winningPerShare_[4]) / Cons.magnitude;
        uint256 profitGreen_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][5], winningPerShare_[5]) / Cons.magnitude;
        uint256 totalProfit_ = SafeMath.add(SafeMath.add(SafeMath.add(SafeMath.add(profitRed_, profitYellow_), profitBlue_), profitBlack_), profitGreen_);

        if ((int256(totalProfit_) - payoutsTo_[_customerAddress]) > 0 )
        
           return uint256(int256(totalProfit_) - payoutsTo_[_customerAddress]);     
        else 
        
           return 0;
    }
    
    /**
     * Retrieve the payoutsTo_ of any single address.
     */
    function payoutsTo(address _customerAddress)
        public
        view
        returns(int256)
    {
        return payoutsTo_[_customerAddress];
    }
    
    /**
     * total amount of ETH on different color bet
     */
    function colorTotalGuess(uint256 colorGuess_)
        public
        view
        returns(uint256)
    {
        return totalGuess[colorGuess_];
    }
    
    /**
     * player's amount of ETH on different color bet
     */
    function playerColorGuess(address _customerAddress, uint256 colorGuess_)
        public
        view
        returns(uint256)
    {
        return ethereumBalanceLedgerColor_[_customerAddress][colorGuess_];
    }
    
    /**
     * total number of blocks for each color
     */
    function totalColorNumber(uint256 colorID_)
        public
        view
        returns(uint256)
    {
        return totalColor_[colorID_];
    }
    
    /**
     * total blocks owned by a player
     */
    function ownBlockNumber(address _customerAddress)
        public
        view
        returns(uint256)
    {
        return ownBlockNumber_[_customerAddress];
    }
    
    /**
     * display winningPerShareNew_
     */
    function winningPerShareNew()
        public
        view
        returns(uint256)
    {
        uint256 value_ = 0;
        for (uint256 i = 1; i < 6; i++) {
            if(winningPerShareNew_[i] > 0)
            value_ = winningPerShareNew_[i];
            
        }
            
        return value_;
    }
    
    /**
     * painted block
     */
        
    function setColorUpdate(uint256 loop_)
        public
        view
        returns(uint256[], uint8[])
    {
            uint256 n =  (changedBlockID_.length)/100000;
            uint256 j = loop_ - 1;
        
            uint256 start_ = j * 100000;
            uint256 k = start_ + 100000;
            
            uint256 length_ = changedBlockID_.length - (n * 100000);
            if ((n > 0)&&(j < n)){
                length_ = 100000;
            }
            uint8[] memory blockColorArray_ = new uint8[](length_);
            uint256[] memory changedBlockIDArray_ = new uint256[](length_);
            for(uint256 i = start_; (i < changedBlockID_.length) && (i < k); i++) { 
                
                changedBlockIDArray_[i-start_] = changedBlockID_[i];
                blockColorArray_[i-start_] = blockColor_[changedBlockID_[i]];
            }
            return (changedBlockIDArray_, blockColorArray_);
        
    }
    /**
     * number of painted blocks 
     */
    function paintedBlockNumber()
        public
        view
        returns(uint256)
    {
        return changedBlockID_.length;
    }
    
    
    /*==========================================
    =            INTERNAL FUNCTIONS            =
    ==========================================*/

    /**
     * Core function of bet on a color
     */
     function guessColorCore(uint256 _incomingEthereum, uint256 colorGuess_)
        private
    {

        address _customerAddress = msg.sender;
        
        // sent to MZBoss and community address
        uint256 _toMZBoss = SafeMath.mul(_incomingEthereum, Cons.dividendFee_) / 100; 
        uint256 _communityDistribution = SafeMath.mul(_incomingEthereum, Cons.toCommunity_) / 100;
        
        payoutsTo_[_MZBossAddress] = payoutsTo_[_MZBossAddress] + (int256)(_toMZBoss);
        payoutsTo_[_communityAddress] = payoutsTo_[_communityAddress] + (int256)(_communityDistribution);
 
        // after tax
        uint256 _taxedEthereum = SafeMath.sub(_incomingEthereum, SafeMath.add(_communityDistribution, _toMZBoss));

        // add the payment to the total ETH that bet on the color
        totalGuess[colorGuess_] = SafeMath.add(totalGuess[colorGuess_], _taxedEthereum);
        
        // update the player's ledger
        ethereumBalanceLedgerColor_[_customerAddress][colorGuess_] = SafeMath.add(ethereumBalanceLedgerColor_[_customerAddress][colorGuess_], _taxedEthereum);
 
        // update total pot
        _totalProfitPot = SafeMath.add(_totalProfitPot, _taxedEthereum);
        
        // calculate the current total winningPerShare_ of the color and pre deducte them from player's account
        uint256 profitExtra_ = SafeMath.mul(ethereumBalanceLedgerColor_[_customerAddress][colorGuess_], winningPerShare_[colorGuess_]) / Cons.magnitude;
        
        // update player's payoutsTo_
        payoutsTo_[_customerAddress] = payoutsTo_[_customerAddress] + (int256)(profitExtra_);
    }
    
    /**
     * random number check
     */
    function randomNumberRequest(uint256 timeNew_)
        internal
    {
        
        // send query // require ETH to cover callback gas costs
        uint N = 1; // number of random bytes we want the datasource to return
        uint delay = 0; // number of seconds to wait before the execution takes place

        if((sendRandomRequest == true) && (_totalProfitPot > SafeMath.mul(callbackGas, gasPriceCallBack))){ 

            queryIdRequest = oraclize_newRandomDSQuery(delay, N, callbackGas); // this function internally generates the correct oraclize_query and returns its queryId
        
            // log that query was sent
            emit LogOraclizeQuery("Oraclize query was sent, standing by for the answer..");
          
            // add query ID to mapping
            validIds[queryIdRequest] = true;
            sendRandomRequest = false;
            timeRequest_ = timeNew_;
            _totalProfitPot = SafeMath.sub(_totalProfitPot, SafeMath.mul(callbackGas, gasPriceCallBack));
        }
           
    }
    
    /**
     * check the winner color and distribute pot
     */
    function winnerCheck(uint256 timeNew_)
        private
    {

        timeUpdate_ = timeNew_;
        
        // check there is any action in last 24 hours, which will activate the distribution of 75% of the pot
        if (SafeMath.sub(timeCutoff_, timeNearest_) > Cons.oneDay_){
            
            uint256 _profitToDistributeTotal = SafeMath.mul(_totalProfitPot, 75)/100;

        }else{
            // otherwise just distribution 10%
            _profitToDistributeTotal = SafeMath.mul(_totalProfitPot, 10)/100;
        }
        
        // udpate pot
        _totalProfitPot = SafeMath.sub(_totalProfitPot, _profitToDistributeTotal);
        timeCutoff_ = 0;
        timeNearest_ = timeNew_; 
        // open the pot
        potOpen(_profitToDistributeTotal);
    }
    
    
    /**
     * open pot and check color every 24 hours, distribute profit
     */
    function potOpen(uint256 _profitToDistribute) 
        private
    {
   
        // sent to MZBoss and community address
        uint256 _toMZBoss = SafeMath.mul(_profitToDistribute, Cons.dividendFee_) / 100; 
        uint256 _communityDistribution = SafeMath.mul(_profitToDistribute, Cons.toCommunity_) / 100;
        
        payoutsTo_[_MZBossAddress] = payoutsTo_[_MZBossAddress] + (int256)(_toMZBoss);
        payoutsTo_[_communityAddress] = payoutsTo_[_communityAddress] + (int256)(_communityDistribution);
        
        sendPotProfit((uint256)(payoutsTo_[_MZBossAddress]));
        _communityAddress.transfer((uint256)(payoutsTo_[_communityAddress]));
        
        payoutsTo_[_MZBossAddress] = 0;
        payoutsTo_[_communityAddress] = 0;
        
        // after tax
        uint256 _taxedEthereum = SafeMath.sub(_profitToDistribute, SafeMath.add(_communityDistribution, _toMZBoss));
        
        // profit go to the last painter    
        uint256 _distributionToLast = SafeMath.mul(_taxedEthereum, Cons.winningLast_)/100;
        
        // update the payoutsTo_ of the last painter    
        payoutsTo_[_lastAddress] = payoutsTo_[_lastAddress] - (int256)(_distributionToLast);
        
        // the rest goes to the color winner    
        uint256 _profitToColorBet = SafeMath.sub(_taxedEthereum, _distributionToLast);

        // activate result and distribution of color betting
        winnerColor(_profitToColorBet);
        
        emit onPotOpen(_profitToDistribute, _distributionToLast, _lastAddress);
    }
    
    /**
     * update block painting information
     */
    function blockSetUpdate(uint256[] _blockIDArray_, uint8[] _setColorArray_)  
        private
    {
        
        address _customerAddress = msg.sender;
        uint256 timeNew_ = now;
        
        for (uint i = 0; i < _blockIDArray_.length; i++) { 
            
            uint256 blockID_ = _blockIDArray_[i];
            uint8 setColor_ = _setColorArray_[i]; 
            
            if ((blockID_ >= 0) && (blockID_ < totalBlock_)&&(setColor_ > 0) && (setColor_ < totalNumberColor_+1)){
                
            // update block price
            if (blockSetPrice_[blockID_] == 0){
    
                blockSetPrice_[blockID_] = Cons.setCheckPriceInitial_;
                changedBlockID_.push(blockID_);
                
            }else{
                uint8 _originalColor = blockColor_[blockID_];
                // update color count of the replaced color
                totalColor_[_originalColor] = totalColor_[_originalColor] - 1;
            }
        
            // update color count of the new color
            totalColor_[setColor_] = totalColor_[setColor_] + 1;
        
            // calculate incomming after tax
            uint256 blockExpense = SafeMath.mul(blockSetPrice_[blockID_], (100 - Cons.dividendFee_ - Cons.toCommunity_))/100; 
        
            // update block color and price
            blockColor_[blockID_] = setColor_;

            // get the owner address of the block
            address owner_ = blockAddress_[blockID_];

            // if there is no owner, the money goes to the pot
            if (owner_ == 0x0) {
            
                _totalProfitPot = SafeMath.add(_totalProfitPot, blockExpense);
            
            }else{

                // otherwise 50% goes to the owner
                uint256 toOwner_ = SafeMath.mul(blockExpense, Cons.ratioToOwner_)/100;
           
                // update owner's payoutsTo_ 
                payoutsTo_[owner_] = payoutsTo_[owner_] - (int256)(toOwner_); 
           
                // half of the rest goes to the pot (25%)
                uint256 _toPot = SafeMath.sub(blockExpense, toOwner_)/2;
                _totalProfitPot = SafeMath.add(_totalProfitPot, _toPot);
                
                // the other half of the rest sent to MZBoss and community address
                uint256 _toMZBoss = SafeMath.mul(_toPot, 13) / 25; 
                uint256 _communityDistribution = SafeMath.mul(_toPot, 12) / 25;
                
                payoutsTo_[_communityAddress] = payoutsTo_[_communityAddress] + (int256)(_communityDistribution);
                payoutsTo_[_MZBossAddress] = payoutsTo_[_MZBossAddress] + (int256)(_toMZBoss);
            }  
            
            }

        }   

        emit onSetColor(_customerAddress, _blockIDArray_, _setColorArray_, timeNew_);
    }
    
    /**
     * update block purchase information
     */
    function blockBuyUpdate(uint256[] _blockIDArray_)  
        private
    {
        address _customerAddress = msg.sender;
        uint256[] memory buyBlockPrice_ = new uint256[](_blockIDArray_.length);
        
        for (uint i = 0; i < _blockIDArray_.length; i++) { 
            
            uint256 blockID_ = _blockIDArray_[i]; 
        
            if ((blockID_ >= 0) && (blockID_ < totalBlock_)){
                
                uint256 priceNow_ = blockBuyPrice_[blockID_];
            
                // update block price
                if (blockAddress_[blockID_] == 0x0){
                
                    priceNow_ = Cons.buyPriceInitial_;
                    
                    uint256 afterTax_ = SafeMath.mul(priceNow_, (100 - Cons.dividendFee_ - Cons.toCommunity_))/100;
                
                    _totalProfitPot = SafeMath.add(_totalProfitPot, afterTax_);
                
                }else{
            
                    // get address of previous owner
                    address previous_ = blockAddress_[blockID_];
                    afterTax_ = SafeMath.mul(priceNow_, (100 - Cons.dividendFee_ - Cons.toCommunity_))/100;
            
                    // 90% to previous owner
                    uint256 toPrevious_ = SafeMath.mul(afterTax_, Cons.ratioToPrevious_)/100;
                    
                    // update payoutsTo_of previous owner
                    payoutsTo_[previous_] = payoutsTo_[previous_] - (int256)(toPrevious_); 
                    
                    // the rest goes to pot
                    _totalProfitPot = SafeMath.add(_totalProfitPot, SafeMath.sub(afterTax_, toPrevious_));
                    
                    // update number of blocks owned by previous owner
                    ownBlockNumber_[previous_] = SafeMath.sub(ownBlockNumber_[previous_], 1);
                }
                
                // update block purchase price
                blockBuyPrice_[blockID_] = SafeMath.mul(priceNow_, Cons.buyPriceAdd_)/100;
                
                buyBlockPrice_[i] = blockBuyPrice_[blockID_];
                
                //owner of the block
                blockAddress_[blockID_] = _customerAddress;
                
                // number of blocks owned by the new owner
                ownBlockNumber_[_customerAddress] = SafeMath.add(ownBlockNumber_[_customerAddress], 1);
            
            }
        
        } 

        // track the time 
        uint256 timeNew_ = now;
        
        emit onBuyBlock(_customerAddress, _blockIDArray_, buyBlockPrice_, timeNew_);
 
    }
    
    /** send eth to MZBoss
	 *
	 */
	function sendPotProfit(uint256 valueToSend)
	    private
	{
		
		MZBoss m = MZBoss(_MZBossAddress);
		m.potDistribution.value(valueToSend)();
	}
	
    /**
     * convert buy color input array and check purchase expense is enough or not
     */
    
    function buyPriceArray(uint256[] _buyBlockIDArray_) 
        private
        returns (uint256)
    {
        
        uint256 totalBuyExpense_ = 0;
        
        for (uint i = 0; i < _buyBlockIDArray_.length; i++) {

            uint256 ID_ = _buyBlockIDArray_[i];
            
            if ((ID_ >= 0) && (ID_ < totalBlock_)){
                
                priceAssume_[ID_] = blockBuyPrice(ID_);
            
            }
        }
        for (i = 0; i < _buyBlockIDArray_.length; i++) {

            ID_ = _buyBlockIDArray_[i];
            
            if ((ID_ >= 0) && (ID_ < totalBlock_)){
                
               totalBuyExpense_ = SafeMath.add(totalBuyExpense_, priceAssume_[ID_]);

               priceAssume_[ID_] = SafeMath.mul(priceAssume_[ID_], Cons.buyPriceAdd_)/100;

            }
        }
        return totalBuyExpense_;
        
    }
    
    
    /**
     * reserve extended function 
     */ 
    function extendFunctionUpdate(uint256[] _blockIDArray_, address[] _blockAddressArray_, uint256[] _blockBuyPriceArray_, uint8[] _blockColorArray_) 
        onlyExtendFunction()
        public
    {
        address _customerAddress = msg.sender;
        
        // update information with incomming array
        for (uint i = 0; i < _blockIDArray_.length; i++) {
            uint256 blockIDUpdate_ = _blockIDArray_[i];
            uint8 blockColorUpdate_ = _blockColorArray_[i];
            
            if ((blockIDUpdate_ >= 0) && (blockIDUpdate_ < totalBlock_) && (blockColorUpdate_ > 0) && (blockColorUpdate_ < totalNumberColor_+1)) {
                
                if (blockSetPrice_[blockIDUpdate_] == 0){
                    
                    changedBlockID_.push(blockIDUpdate_);
                    blockSetPrice_[blockIDUpdate_] = Cons.setCheckPriceInitial_;
                
                }
                
                blockBuyPrice_[blockIDUpdate_] = _blockBuyPriceArray_[i];
                
                // update color count of the replaced color
                if(blockColor_[blockIDUpdate_] > 0){
                           
                    totalColor_[blockColor_[blockIDUpdate_]] = totalColor_[blockColor_[blockIDUpdate_]] - 1;
                }      
                
                    blockColor_[blockIDUpdate_] = _blockColorArray_[i];
                
                    // update color count of the new color
                    totalColor_[_blockColorArray_[i]] = totalColor_[_blockColorArray_[i]] + 1;
                
                if(blockAddress_[blockIDUpdate_] != 0x0){
                    
                    ownBlockNumber_[blockAddress_[blockIDUpdate_]] = ownBlockNumber_[blockAddress_[blockIDUpdate_]] - 1;
                }
                
                blockAddress_[blockIDUpdate_] = _blockAddressArray_[i];
                
                if(blockAddress_[blockIDUpdate_] != 0x0){
                    
                    ownBlockNumber_[blockAddress_[blockIDUpdate_]] = ownBlockNumber_[blockAddress_[blockIDUpdate_]] + 1;
                }
                

            }
            
        }

        emit onExtendFunction(
            _customerAddress,
            _blockIDArray_,
            _blockColorArray_,
            _blockBuyPriceArray_,
            _blockAddressArray_
        );
    
    }
    

    
    /**
     * check maximum color and calculate winning profit 
     */
    function winnerColor(uint256 _distributionAmount) // 1 red, 2 yellow, 3 blue, 4 black, 5 green
        private

    {
        uint256 Maximum = totalColor_[1];
        
        // find the number of blocks of the maximum color
        for (uint i = 2; i < 6; i++) {
            
            if (Maximum < totalColor_[i]) {
                
                Maximum = totalColor_[i];
                
            }
            
        }
        
        if (Maximum != 0){

            uint256 totalMaxColor_ = 0;
            uint256[6] memory MaximumSign;
            // find how many colors are the winner
            for ( i = 1; i < 6; i++) {
              
                if (Maximum == totalColor_[i]) {
                    
                    MaximumSign[i] = 1;
                }else{
                  
                    MaximumSign[i] = 0;  
                }
                // calculate total color winners
                totalMaxColor_ += MaximumSign[i];
                
            } 
        
            
            // evenly distribute the profit for color winner to each winning color        
            if (totalMaxColor_ > 0){
                uint256 _distributionAmountEach = _distributionAmount/totalMaxColor_;
            }
        
            // calculate winning ETH per share of players in each color        
            for (i = 1; i < 6; i++) {
              
                if (totalGuess[i] > 0){
                
                    uint256 winningProfitPerShare_ = SafeMath.mul(_distributionAmountEach, Cons.magnitude) / totalGuess[i];
                    winningPerShareNew_[i] = SafeMath.mul(winningProfitPerShare_, MaximumSign[i]);
                    winningPerShare_[i] = SafeMath.add(winningPerShare_[i], winningPerShareNew_[i]);
              
                }
            }
        }
        
        emit onWinnerColor(
            
            totalColor_[1],
            totalColor_[2],
            totalColor_[3],
            totalColor_[4],
            totalColor_[5],
            winningPerShareNew_[1],
            winningPerShareNew_[2],
            winningPerShareNew_[3],
            winningPerShareNew_[4],
            winningPerShareNew_[5]
            
            );
        
    }   
    
}
function guessColor(uint8 colorGuess_) 
    enoughToGuess(colorGuess_)
    public
    payable
{
    // setup data
    address _customerAddress = msg.sender;
    uint256 _incomingEthereum = SafeMath.add(msg.value, dividendsOf(_customerAddress));
        
    // record total ETH into the game
    totalVolumn_ = SafeMath.add(totalVolumn_, msg.value);
        
    // go to guess color core function
    guessColorCore(_incomingEthereum, colorGuess_); 
        
    // Check if it is time to open the pot, if it is, go to check winner and distribute pot
    if (SafeMath.sub(now, timeUpdate_) > Cons.oneDay_){
        if(timeCutoff_ == 0) timeCutoff_ = now;
            
        if((now - timeRequest_) > Cons.oneDay_){
            sendRandomRequest = true;
            validIds[queryIdRequest] = false;
            randomNumberRequest(now);
        }else{
            randomNumberRequest(now);
        }
    }else{
        // track the time of the latest action, used for potopen
        timeNearest_ = now;
    } 
           
    // payoutsTo_ is int256, so convert _incomingEthereum to int256
    payoutsTo_[_customerAddress] = payoutsTo_[_customerAddress] + (int256) (dividendsOf(_customerAddress)); 
        
    emit onGuessColor(_customerAddress, msg.value, _incomingEthereum, colorGuess_, now);
}

/**
 * player can bet on color
 */