contract SkinBase is Manager {

    struct Skin {
        uint128 appearance;
        uint64 cooldownEndTime;
        uint64 mixingWithId;
    }

    // All skins, mapping from skin id to skin apprance
    mapping (uint256 => Skin) skins;

    // Mapping from skin id to owner
    mapping (uint256 => address) public skinIdToOwner;

    // Whether a skin is on sale
    mapping (uint256 => bool) public isOnSale;

    // Number of all total valid skins
    // skinId 0 should not correspond to any skin, because skin.mixingWithId==0 indicates not mixing
    uint256 public nextSkinId = 1;  

    // Number of skins an account owns
    mapping (address => uint256) public numSkinOfAccounts;

    // // Give some skins to init account for unit tests
    // function SkinBase() public {
    //     address account0 = 0x627306090abaB3A6e1400e9345bC60c78a8BEf57;
    //     address account1 = 0xf17f52151EbEF6C7334FAD080c5704D77216b732;

    //     // Create simple skins
    //     Skin memory skin = Skin({appearance: 0, cooldownEndTime:0, mixingWithId: 0});
    //     for (uint256 i = 1; i <= 15; i++) {
    //         if (i < 10) {
    //             skin.appearance = uint128(i);
    //             if (i < 7) { 
    //                 skinIdToOwner[i] = account0;
    //                 numSkinOfAccounts[account0] += 1;
    //             } else {  
    //                 skinIdToOwner[i] = account1;
    //                 numSkinOfAccounts[account1] += 1;
    //             }
    //         } else {  
    //             skin.appearance = uint128(block.blockhash(block.number - i + 9));
    //             skinIdToOwner[i] = account1;
    //             numSkinOfAccounts[account1] += 1;
    //         }
    //         skins[i] = skin;
    //         isOnSale[i] = false;
    //         nextSkinId += 1;
    //     }
    // } 

    // Get the i-th skin an account owns, for off-chain usage only
    function skinOfAccountById(address account, uint256 id) external view returns (uint256) {
       uint256 count = 0;
       uint256 numSkinOfAccount = numSkinOfAccounts[account];
       require(numSkinOfAccount > 0);
       require(id < numSkinOfAccount);
       for (uint256 i = 1; i < nextSkinId; i++) {
           if (skinIdToOwner[i] == account) {
               // This skin belongs to current account
               if (count == id) {
                   // This is the id-th skin of current account, a.k.a, what we need
                    return i;
               } 
               count++;
           }
        }
        revert();
    }

    // Get skin by id
    function getSkin(uint256 id) public view returns (uint128, uint64, uint64) {
        require(id > 0);
        require(id < nextSkinId);
        Skin storage skin = skins[id];
        return (skin.appearance, skin.cooldownEndTime, skin.mixingWithId);
    }

    function withdrawETH() external onlyCAO {
        cfo.transfer(this.balance);
    }
}
function getSkin(uint256 id) public view returns (uint128, uint64, uint64) {
    require(id > 0);
    require(id < nextSkinId);
    Skin storage skin = skins[id];
    return (skin.appearance, skin.cooldownEndTime, skin.mixingWithId);
}

// Get skin by id