contract CardMintingFacilitator {
    CardConfig schema = CardConfig(0x08584271df3d0249c2c06ac1bc1237a1dd30cb9a); 
    EtherGenCore storageContract = EtherGenCore(0x677aa1dc08b9429c595efd4425b2d218cc22fd6e);
    address public owner = 0x08F4aE96b647B30177cc15B21195960625BA4163;
    
    function generateRandomCard(uint32 randomSeed) internal constant returns (uint8[14]) {
        uint8[14] memory cardDetails;
       
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        cardDetails[0] = schema.getType(randomSeed);

        if (cardDetails[0] == uint8(CardConfig.Type.Monster)) {
            generateRandomMonster(cardDetails, randomSeed);
        } else {
            generateRandomSpell(cardDetails, randomSeed);
        }
        
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        if (randomSeed % 200 == 13) { // Lucky number 13
            cardDetails[12] = 1; // Secret golden attribute
        }
        
        return cardDetails;
    }
    
    function generateRandomMonster(uint8[14] cardDetails, uint32 randomSeed) internal constant {
        uint24 totalCost;
        
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        cardDetails[1] = schema.getRace(randomSeed);
        totalCost += schema.getCostForRace(cardDetails[1]);

        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        cardDetails[2] = schema.getTrait(randomSeed);
        totalCost += schema.getCostForTrait(cardDetails[2]);

        uint8 newMutation;
        uint24 newMutationCost;
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        
        uint8 numMutations = uint8(randomSeed % 12); // 0 = 0 mutations, 1 = 1 mutation, 2-5 = 2 mutations, 6-11 = 3 mutations 
        if (numMutations > 5) {
            numMutations = 3;
        } else if (numMutations > 2) {
            numMutations = 2;
        }
        
        for (uint8 i = 0; i < numMutations; i++) {
            randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
            if (bool(randomSeed % 3 == 0)) { // 0: Race; 1-2: Neutral
                randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));

                // Horribly add new mutations (rather than looping for a fresh one) this is cheaper
                (newMutationCost, newMutation) = schema.getMutationForRace(CardConfig.Race(cardDetails[1]), randomSeed);
                if (totalCost + newMutationCost < 290000) {
                    if (cardDetails[6] == 0) {
                        cardDetails[6] = newMutation;
                        totalCost += newMutationCost;
                    } else if (cardDetails[6] > 0 && cardDetails[7] == 0 && cardDetails[6] != newMutation) {
                        cardDetails[7] = newMutation;
                        totalCost += newMutationCost;
                    } else if  (cardDetails[6] > 0 && cardDetails[7] > 0 && cardDetails[8] == 0 && cardDetails[6] != newMutation && cardDetails[7] != newMutation) {
                        cardDetails[8] = newMutation;
                        totalCost += newMutationCost;
                    }
                }
            } else {
                randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));

                // Horribly add new mutations (rather than looping for a fresh one) this is cheaper
                (newMutationCost, newMutation) = schema.getNeutralMutation(randomSeed);
                if (totalCost + newMutationCost < 290000) {
                    if (cardDetails[9] == 0) {
                        cardDetails[9] = newMutation;
                        totalCost += newMutationCost;
                    } else if (cardDetails[9] > 0 && cardDetails[10] == 0 && cardDetails[9] != newMutation) {
                        cardDetails[10] = newMutation;
                        totalCost += newMutationCost;
                    } else if (cardDetails[9] > 0 && cardDetails[10] > 0 && cardDetails[11] == 0 && cardDetails[9] != newMutation && cardDetails[10] != newMutation) {
                        cardDetails[11] = newMutation;
                        totalCost += newMutationCost;
                    }
                }
            }
        }

        // For attack & health distribution
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        uint24 powerCost = schema.getCostForHealth(1) + uint24(randomSeed % (301000 - (totalCost + schema.getCostForHealth(1)))); // % upto 300999 will allow 30 cost cards

        if (totalCost + powerCost < 100000) { // Cards should cost at least 10 crystals (10*10000 exponant)
            powerCost = 100000 - totalCost;
        }
        
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        cardDetails[5] = 1 + uint8(schema.getHealthForCost(randomSeed % powerCost)); // should be (1 + powerCost - schema.getCostForHealth(1))
        totalCost += schema.getCostForHealth(cardDetails[5]);
        
        powerCost = powerCost - schema.getCostForHealth(cardDetails[5]); // Power left for attack
        cardDetails[4] = uint8(schema.getAttackForCost(powerCost));
        totalCost += schema.getCostForAttack(cardDetails[4]);
       
        // Remove exponent to get total card cost [10-30]
        cardDetails[3] = uint8(totalCost / 10000);
    }
    
    
    function generateRandomSpell(uint8[14] cardDetails, uint32 randomSeed) internal constant {
        uint24 totalCost;
        
        uint8 newAbility;
        uint24 newAbilityCost;
        randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));
        
        uint8 numAbilities = uint8(randomSeed % 16); // 0 = 1 ability, 1-8 = 2 abilities, 9-15 = 3 abilities 
        if (numAbilities > 8) {
            numAbilities = 3;
        } else if (numAbilities > 0) {
            numAbilities = 2;
        } else {
            numAbilities = 1;
        }
        
        for (uint8 i = 0; i < numAbilities; i++) {
            randomSeed = uint32(sha3(block.blockhash(block.number), randomSeed));

            // Horribly add new spell abilities (rather than looping for a fresh one) this is cheaper
            (newAbilityCost, newAbility) = schema.getSpellAbility(randomSeed);
            if (totalCost + newAbilityCost <= 300000) {
                if (cardDetails[9] == 0) {
                    cardDetails[9] = newAbility;
                    totalCost += newAbilityCost;
                } else if (cardDetails[9] > 0 && cardDetails[10] == 0 && cardDetails[9] != newAbility) {
                    cardDetails[10] = newAbility;
                    totalCost += newAbilityCost;
                } else if (cardDetails[9] > 0 && cardDetails[10] > 0 && cardDetails[11] == 0 && cardDetails[9] != newAbility && cardDetails[10] != newAbility) {
                    cardDetails[11] = newAbility;
                    totalCost += newAbilityCost;
                }
            }
        }
        
        // Remove exponent to get total card cost [10-30]
        cardDetails[3] = uint8(totalCost / 10000);
    }
    
    
    function generateCostFromAttributes(uint8[14] cardDetails) internal constant returns (uint8 cost) {
        uint24 exponentCost = 0;
        if (cardDetails[0] == 1) { // Spell
            exponentCost += schema.getSpellAbilityCost(cardDetails[9]);
            exponentCost += schema.getSpellAbilityCost(cardDetails[10]);
            exponentCost += schema.getSpellAbilityCost(cardDetails[11]);
        } else {
            exponentCost += schema.getCostForRace(cardDetails[1]);
            exponentCost += schema.getCostForTrait(cardDetails[2]);
            exponentCost += schema.getCostForAttack(cardDetails[4]);
            exponentCost += schema.getCostForHealth(cardDetails[5]);
            exponentCost += schema.getRaceMutationCost(CardConfig.Race(cardDetails[1]), cardDetails[6]);
            exponentCost += schema.getRaceMutationCost(CardConfig.Race(cardDetails[1]), cardDetails[7]);
            exponentCost += schema.getRaceMutationCost(CardConfig.Race(cardDetails[1]), cardDetails[8]);
            exponentCost += schema.getNeutralMutationCost(cardDetails[9]);
            exponentCost += schema.getNeutralMutationCost(cardDetails[10]);
            exponentCost += schema.getNeutralMutationCost(cardDetails[11]);
        }
        return uint8(exponentCost / 10000); // Everything is factor 10000 for ease of autonomous Workshop cost-tuning
    }
    
    // Allows future extensibility (New card mutations + Workshop updates)
    function upgradeCardConfig(address newCardConfig) external {
        require(msg.sender == owner);
        schema = CardConfig(newCardConfig);
    }
    
    function updateStorageContract(address newStorage) external {
        require(msg.sender == owner);
        storageContract = EtherGenCore(newStorage);
    }
    
    function updateOwner(address newOwner) external {
        require(msg.sender == owner);
        owner = newOwner;
    }
}
// EtherGen Contract: 
// Base Minting Contract
function upgradeCardConfig(address newCardConfig) external {
    require(msg.sender == owner);
    schema = CardConfig(newCardConfig);
}

// Allows future extensibility (New card mutations + Workshop updates)