contract Validation is Priced, Storage {
    uint8   private gidMax     = 5;
    uint16  private zMin       = 100;
    uint16  private zMax       = 1600;
    uint8   private lName      = 25;
    uint8   private lMessage   = 140;
    uint8   private maxCT      = 255; // Max color, types
    uint256 private nonce      = 1;
    uint8   private maxIRandom = 4;
    uint16  private boxSize    = 20;  // Universe box size
    uint8   private inboxXY    = 100;

    // Available box count in each z index (zIndex => count)
    mapping(uint8 => uint16) private boxes;


    /**
     * Set validation data
     */
    function setValidationData(uint16 _zMin, uint16 _zMax, uint8 _lName, uint8 _lMessage, uint8 _maxCT, uint8 _maxIR, uint16 _boxSize) external onlyOwner {
        zMin       = _zMin;
        zMax       = _zMax;
        lName      = _lName;
        lMessage   = _lMessage;
        maxCT      = _maxCT;
        maxIRandom = _maxIR;
        boxSize    = _boxSize;
        inboxXY    = uint8((boxSize * boxSize) / 4);
    }

    function setGidMax(uint8 _gidMax) external onlyOwner {
        gidMax = _gidMax;
    }


    /**
     * Get set boxes
     */
    function setBoxCount(uint16 z, uint16 count) external onlyOwner {
        require(isValidZ(z));
        boxes[getZIndex(z)] = count;
    }

    function getBoxCount(uint16 z) external view returns(uint16 count) {
        require(isValidZ(z));
        return boxes[getZIndex(z)];
    }

    function getBoxCountZIndex(uint8 zIndex) private view returns(uint16 count) {
        return boxes[zIndex];
    }


    /**
     * Get z index and z count
     */
    function getZIndex(uint16 z) internal view returns(uint8 zIndex) {
        return uint8(z / boxSize);
    }

    function getZCount(uint8 gid, uint8 zIndex) public view returns(uint16 count) {
        return zCount[gid][zIndex];
    }

    
    /**
     * Validate star parameters
     */
    function isValidGid(uint8 gid) internal view returns(bool) {
        return gid > 0 && gid <= gidMax;
    }

    function isValidZ(uint16 z) internal view returns(bool) {
        return z >= zMin && z <= zMax;
    }

    function isValidBox(uint8 gid, uint8 zIndex, uint16 box) internal view returns(bool) {
        return getStarIdAtPosition(gid, zIndex, box) == 0;
    }


    /**
     * Check name and message length
     */
    function isValidNameLength(string name) internal view returns(bool) {
        return UTF8.getStringLength(name) <= lName;
    }

    function isValidMessageLength(string message) internal view returns(bool) {
        return UTF8.getStringLength(message) <= lMessage;
    }


    /**
     * Check is valid msg value
     */
    function isValidMsgValue(uint256 price) internal returns(bool) {
        if (msg.value < price) return false;
        if (msg.value > price)
            msg.sender.transfer(msg.value - price);
        return true;
    }


    /**
     * Get random number
     */
    function getRandom16(uint16 min, uint16 max) private returns(uint16) {
        nonce++;
        return Math.rand16(nonce, min, max);
    }

    function getRandom8(uint8 min, uint8 max) private returns(uint8) {
        nonce++;
        return Math.rand8(nonce, min, max);
    }

    function getRandomColorType() internal returns(uint8) {
        return getRandom8(0, maxCT);
    }


    /**
     * Get random star position
     */
    function getRandomPosition(uint8 gid, uint8 zIndex) internal returns(uint16 box, uint8 inbox) {
        uint16 boxCount = getBoxCountZIndex(zIndex);
        uint16 randBox  = 0;
        if (boxCount == 0) revert();

        uint8 ii   = maxIRandom;
        bool valid = false;
        while (!valid && ii > 0) {
            randBox = getRandom16(0, boxCount);
            valid   = isValidBox(gid, zIndex, randBox);
            ii--;
        }

        if (!valid) revert();
        return(randBox, getRandom8(0, inboxXY));
    }
}
/***********************************************
 ***************
 * Validation contract
 ***************
 **********************************************/
function setValidationData(uint16 _zMin, uint16 _zMax, uint8 _lName, uint8 _lMessage, uint8 _maxCT, uint8 _maxIR, uint16 _boxSize) external onlyOwner {
    zMin       = _zMin;
    zMax       = _zMax;
    lName      = _lName;
    lMessage   = _lMessage;
    maxCT      = _maxCT;
    maxIRandom = _maxIR;
    boxSize    = _boxSize;
    inboxXY    = uint8((boxSize * boxSize) / 4);
}

/**
 * Set validation data
 */