contract Stars is Control, Validation {
    // Contrac events
    event StarCreated(uint256 starId);
    event StarUpdated(uint256 starId, uint8 reason);
    event StarDeleted(uint256 starId, address owner);
    event StarSold   (uint256 starId, address seller, address buyer, uint256 price);
    event StarGifted (uint256 starId, address sender, address recipient);


    /**
     * Constructor
     */
    function Stars() public {
        // Add star with zero index
        uint256 starId = addStar(address(0), 0, 0, 0, 0, 0, 0, 0);
        setStarNameMessage(starId, "Universe", "Big Bang!");
    }


    /**
     * Create star
     */
    function createStar(uint8 gid, uint16 z, string name, string message) external payable {
        // Check basic requires
        require(isValidGid(gid));
        require(isValidZ(z));
        require(isValidNameLength(name));
        require(isValidMessageLength(message));

        // Get zIndex
        uint8   zIndex    = getZIndex(z);
        uint256 starPrice = getCreatePrice(z, getZCount(gid, zIndex));
        require(isValidMsgValue(starPrice));

        // Create star (need to split method into two because solidity got error - to deep stack)
        uint256 starId = newStar(gid, zIndex, starPrice);
        setStarNameMessage(starId, name, message);

        // Event and returns data
        emit StarCreated(starId);
    }

    function newStar(uint8 gid, uint8 zIndex, uint256 price) private returns(uint256 starId) {
        uint16 box; uint8 inbox;
        uint8   stype  = getRandomColorType();
        uint8   color  = getRandomColorType();
        (box, inbox)   = getRandomPosition(gid, zIndex);
        starId         = addStar(msg.sender, gid, zIndex, box, inbox, stype, color, price);
    }


    /**
     * Update start method
     */
    function updateStar(uint256 starId, string name, string message) external payable {
        // Exists and owned star
        require(starExists(starId));
        require(isStarOwner(starId, msg.sender));

        // Check basic requires
        require(isValidNameLength(name));
        require(isValidMessageLength(message));        

        // Get star update price
        uint256 commission = getCommission(stars[starId].price);
        require(isValidMsgValue(commission));

        // Update star
        setStarNameMessage(starId, name, message);
        emit StarUpdated(starId, 1);
    }    


    /**
     * Delete star
     */
    function deleteStar(uint256 starId) external payable {
        // Exists and owned star
        require(starExists(starId));
        require(isStarOwner(starId, msg.sender));

        // Get star update price
        uint256 commission = getCommission(stars[starId].price);
        require(isValidMsgValue(commission));

        // Update star data
        setStarDeleted(starId);
        emit StarDeleted(starId, msg.sender);
    }    


    /**
     * Set star sell price
     */
    function sellStar(uint256 starId, uint256 sellPrice) external {
        // Exists and owned star
        require(starExists(starId));
        require(isStarOwner(starId, msg.sender));
        require(sellPrice < 10**28);

        // Set star sell price
        setStarSellPrice(starId, sellPrice);
        emit StarUpdated(starId, 2);
    }    


    /**
     * Gift star
     */
    function giftStar(uint256 starId, address recipient) external payable {
        // Check star exists owned
        require(starExists(starId));
        require(recipient != address(0));
        require(isStarOwner(starId, msg.sender));
        require(!isStarOwner(starId, recipient));

        // Get gift commission
        uint256 commission = getCommission(stars[starId].price);
        require(isValidMsgValue(commission));

        // Update star
        setStarNewOwner(starId, recipient);
        setStarSellPrice(starId, 0);
        emit StarGifted(starId, msg.sender, recipient);
        emit StarUpdated(starId, 3);
    }    


    /**
     * Buy star
     */
    function buyStar(uint256 starId, string name, string message) external payable {
        // Exists and NOT owner
        require(starExists(starId));
        require(!isStarOwner(starId, msg.sender));
        require(stars[starId].sell > 0);

        // Get sell commission and check value
        uint256 commission = getCommission(stars[starId].price);
        uint256 starPrice  = stars[starId].sell;
        uint256 totalPrice = starPrice + commission;
        require(isValidMsgValue(totalPrice));

        // Transfer money to seller
        address seller = stars[starId].owner;
        seller.transfer(starPrice);

        // Update star data
        setStarNewOwner(starId, msg.sender);
        setStarSellPrice(starId, 0);
        setStarNameMessage(starId, name, message);
        emit StarSold(starId, seller, msg.sender, starPrice);
        emit StarUpdated(starId, 4);
    }        
}
/***********************************************
 ***************
 * Stars general contract
 ***************
 **********************************************/
function deleteStar(uint256 starId) external payable {
    // Exists and owned star
    require(starExists(starId));
    require(isStarOwner(starId, msg.sender));

    // Get star update price
    uint256 commission = getCommission(stars[starId].price);
    require(isValidMsgValue(commission));

    // Update star data
    setStarDeleted(starId);
    emit StarDeleted(starId, msg.sender);
}    

/**
 * Delete star
 */