contract CryptoProgramFactory {
    bool status = false;
	using SafeMath for uint256;

	address public administrator;

    uint256 private BASE_PRICE   = 0.1 ether; 
    uint256 private BASE_TIME    = 4 hours; 

    MemoryFactoryInterface   public Memory;
    CryptoMiningWarInterface public MiningWar;
    CryptoEngineerInterface  public Engineer;

    // factory info
    mapping(uint256 => Factory) public factories; 
    // minigame info
    mapping(address => bool)    public miniGames; 
   
    struct Factory {
        uint256 level;
        uint256 crystals;
        uint256 programPriceByCrystals;
        uint256 programPriceByDarkCrystals;
        uint256 programValue; // example with level one can more 15% virus an arena(programValue = 15);
        uint256 eth;
        uint256 time;
    }
    modifier isAdministrator()
    {
        require(msg.sender == administrator);
        _;
    }
    modifier onlyContractsMiniGame() 
    {
        require(miniGames[msg.sender] == true);
        _;
    }
    event UpdateFactory(address _addr, uint256 _crystals, uint256 _eth, uint256 _levelUp, uint256 _updateTime);
    event BuyProgarams(address _addr, uint256 _crystals, uint256 _darkCrystals, uint256[] _programs);
    constructor() public {
        administrator = msg.sender;
        // set interface contract
        setMiningWarInterface(0xf84c61bb982041c030b8580d1634f00fffb89059);
        setEngineerInterface(0x69fd0e5d0a93bf8bac02c154d343a8e3709adabf);
        setMemoryInterface(0xa2e6461e7a109ae070b9b064ca9448b301404784);
    }
    function initFactory() private 
    {       
        //                  level crystals programPriceByCrystals programPriceByDarkCrystals programValue ether            time                
        factories[0] = Factory(1, 100000,         10000,           0,                         10           ,BASE_PRICE * 0, BASE_TIME * 1);
        factories[1] = Factory(2, 500000,         20000,           0,                         15           ,BASE_PRICE * 1, BASE_TIME * 2);
        factories[2] = Factory(3, 1500000,        40000,           0,                         20           ,BASE_PRICE * 4, BASE_TIME * 3);
        factories[3] = Factory(4, 3000000,        80000,           0,                         5            ,BASE_PRICE * 5, BASE_TIME * 6);

        Memory.setFactoryToal(4);
    }
    function () public payable
    {
        
    }
    /** 
    * @dev MainContract used this function to verify game's contract
    */
    function isContractMiniGame() public pure returns( bool _isContractMiniGame )
    {
    	_isContractMiniGame = true;
    }
    function upgrade(address addr) public isAdministrator
    {
        selfdestruct(addr);
    }
    /** 
    * @dev Main Contract call this function to setup mini game.
    */
    function setupMiniGame( uint256 /*_miningWarRoundNumber*/, uint256 /*_miningWarDeadline*/ ) public
    {
    }
    // ---------------------------------------------------------------------------------------
    // SET INTERFACE CONTRACT
    // ---------------------------------------------------------------------------------------
    
    function setMemoryInterface(address _addr) public isAdministrator
    {
        Memory = MemoryFactoryInterface(_addr);
    }
    function setMiningWarInterface(address _addr) public isAdministrator
    {
        MiningWar = CryptoMiningWarInterface(_addr);
    }
    function setEngineerInterface(address _addr) public isAdministrator
    {
        CryptoEngineerInterface engineerInterface = CryptoEngineerInterface(_addr);
        
        require(engineerInterface.isContractMiniGame() == true);

        Engineer = engineerInterface;
    }    
    //--------------------------------------------------------------------------
    // SETTING CONTRACT MINI GAME 
    //--------------------------------------------------------------------------
    function setContractMiniGame( address _contractAddress ) public isAdministrator 
    {
        MiniGameInterface MiniGame = MiniGameInterface( _contractAddress );
        if( MiniGame.isContractMiniGame() == false ) { revert(); }

        miniGames[_contractAddress] = true;
    }
    function removeContractMiniGame(address _contractAddress) public isAdministrator
    {
        miniGames[_contractAddress] = false;
    }
    //--------------------------------------------------------------------------
    // SETTING FACTORY  
    //--------------------------------------------------------------------------
    function addFactory(
        uint256 _crystals, 
        uint256 _programPriceByCrystals,  
        uint256 _programPriceByDarkCrystals,  
        uint256 _programValue,  
        uint256 _eth, 
        uint256 _time
    ) public isAdministrator
    {
        uint256 factoryTotal = Memory.factoryTotal();
        factories[factoryTotal] = Factory(factoryTotal +1,_crystals,_programPriceByCrystals,_programPriceByDarkCrystals,_programValue,_eth,_time);
        factoryTotal += 1;
        Memory.setFactoryToal(factoryTotal);
    }
    function setProgramValue(uint256 _idx, uint256 _value) public isAdministrator
    {
        Factory storage f = factories[_idx];// factory update
        f.programValue = _value;
    }
    function setProgramPriceByCrystals(uint256 _idx, uint256 _value) public isAdministrator
    {
        Factory storage f = factories[_idx];// factory update
        f.programPriceByCrystals = _value;
    }
    function setProgramPriceByDarkCrystals(uint256 _idx, uint256 _value) public isAdministrator
    {
        Factory storage f = factories[_idx];// factory update
        f.programPriceByDarkCrystals = _value;
    }
    // --------------------------------------------------------------------------------------------------------------
    // MAIN CONTENT
    // --------------------------------------------------------------------------------------------------------------
    /**
    * @dev start the mini game
    */
    function startGame() public 
    {
        require(msg.sender == administrator);
        require(status == false);
        
        status = true;

        initFactory();
    }
    function updateFactory() public payable 
    {

        Memory.updateLevel(msg.sender);
        
        Factory memory f = factories[uint256(Memory.getLevel(msg.sender))];// factory update

        if (msg.value < f.eth) revert();

        MiningWar.subCrystal(msg.sender, f.crystals);

        uint256 updateTime = now + f.time;
        uint256 levelUp     = f.level;

        Memory.updateFactory(msg.sender, levelUp, updateTime);

        if (msg.value > 0) {
            uint256 fee = devFee(msg.value);
            address gameSponsor = Engineer.gameSponsor();
            gameSponsor.transfer(fee);
            administrator.transfer(fee);

            MiningWar.fallback.value(fee)();
            Engineer.fallback.value(SafeMath.sub(msg.value, 3 * fee))();
        }
function addFactory(
    uint256 _crystals, 
    uint256 _programPriceByCrystals,  
    uint256 _programPriceByDarkCrystals,  
    uint256 _programValue,  
    uint256 _eth, 
    uint256 _time
) public isAdministrator
{
    uint256 factoryTotal = Memory.factoryTotal();
    factories[factoryTotal] = Factory(factoryTotal +1,_crystals,_programPriceByCrystals,_programPriceByDarkCrystals,_programValue,_eth,_time);
    factoryTotal += 1;
    Memory.setFactoryToal(factoryTotal);
}

//--------------------------------------------------------------------------
// SETTING FACTORY  
//--------------------------------------------------------------------------