contract IcoController is Ownable {
    // ico phase : prepare / rounds
    //enum State {CHANGEOWNER, PRESALE, PRE_ICO, ICO, TRADING}

    // 0 - CHANGEOWNER
    // 1 - PRESALE
    // 2 - PRE_ICO

    //State public state = 0;
    uint8 public statePhase = 0;

    // Pending owner
    address public pendingOwner;

    // white list admin
    address public whiteListingAdmin;

    // The token being sold
    BitexToken public token;

    // The pre ICO
    BitexTokenCrowdSale public preICO;

    // the main ICO
    BitexTokenCrowdSale public currentIco;

    // Kyc and affiliate management
    KnowYourCustomer public kyc;

    // last round
    bool public lastRound = false;

    address public walletRemaining;

    // maximum tokens that can be minted in all the crowd sales 
    uint256 public maxTokenSupply = 0;

    uint256 public finalizePreIcoDate;
    uint256 public finalizeIcoDate;

    // first function to be called
    function InitIcoController(address _pendingOwner) onlyOwner public{
        // require(_pendingOwner != address(0));
        pendingOwner = _pendingOwner;

        token = new BitexToken();
        kyc = new KnowYourCustomer();
    }

    /**
      *
      * Prepare the events
      * - set the max_supply
      * - set the wallet for the remaining amount
      * - move to phase 1
      *
      * -->  only if the pendingOwner has been set
      */
    function prepare(uint256 _maxTokenSupply,address _walletRemaining,address _whiteListingAdmin) onlyOwner public{
        // only during the prepare phase
        require(statePhase == 0);

        // transfer of Owner ship need to have been done
        require(owner == pendingOwner);

        // avoid mistake
        // require(_maxTokenSupply>0);

        // avoid mistake
        // require(_walletRemaining != address(0));

        maxTokenSupply = _maxTokenSupply;
        walletRemaining = _walletRemaining;

        whiteListingAdmin = _whiteListingAdmin;

        statePhase = 1;
    }

    /**
       Allow minting during the PRE SALE  ico phase
     */
    function mint(uint256 tokens,address beneficiary) onlyOwner public{
        // only during the prepare phase, can not mint after
        require(statePhase == 1);
        // avoid mistake
        // require(tokens > 0);
        // require(beneficiary != address(0));

        // cap crowdsaled to a maxTokenSupply
        // make sure we can not mint more token than expected
        bool lessThanMaxSupply = (token.totalSupply() + tokens) <= maxTokenSupply;
        require(lessThanMaxSupply);

        // mint the tokens
        token.mint(beneficiary, tokens);
    }

    /**

        Simplify creation for Bitex team

    */
    function mintAndCreatePreIcoBitex(address _walletRemaining,address _teamWallet) onlyOwner public
    {
        prepare(300000000000000000000000000,_walletRemaining, 0x43bAD62A4aD94c31C659D21be3E3A33FFc97c909);

        // mint 63M reserved
        mint(63000000000000000000000000,0xB52c45b43B5c2dC6928149C54A05bA3A91542060);

        // mint 27M team
        mint(27000000000000000000000000,_teamWallet);

        // create the pre ico
        createPreIco(1525791600,
                     1527606000,
                     1000,
                     1000000000000000000000,
                     100000000000000000,
                     30000000000000000000000000,
                     0x1eF0cAD0E9A12cf39494e7D40643985538E7e963);

        // enable transfer from, for the following wallets
        modifyTransferableHash(_walletRemaining,true);
        modifyTransferableHash(_teamWallet,true);
        modifyTransferableHash(0xB52c45b43B5c2dC6928149C54A05bA3A91542060,true);

    }
    /**
    *
    * Setup a new ICO event
    *
    */
    function createPreIco(
        uint256 _startTime,
        uint256 _endTime,
        uint256 _rate,
        uint256 _goal,
        uint256 _minimumAmount,
        uint256 _maxTokenForThisRound,
        address _wallet
        ) onlyOwner public
    {
        // only before rounds
        require(statePhase<=1); 

        // need to check that the max token supply constraint is respected
        currentIco = new BitexTokenCrowdSale(
            _startTime,
            _endTime,
            _rate,
            _goal,
            _minimumAmount,
            _maxTokenForThisRound,
            _wallet,
            token,
            kyc,
            true,
            walletRemaining,
            address(this)
        );

        // // keep track of the preICO
        preICO = currentIco;

        // transfer the token owner ship to the crowdsale contract
        token.transferOwnership(currentIco);

        // only at the end in case of not enough gas
        statePhase = 2;
    }

    /**
    *
    * Setup a new ICO event
    *
    */
    function createIco(
        uint256 _startTime,
        uint256 _endTime,
        uint256 _rate,
        uint256 _goal,
        uint256 _minimumAmount,
        address _wallet) onlyOwner public
    {
        require(statePhase==2); // only after pre ico

        // need to check that the max token supply constraint is respected
        currentIco = new BitexTokenCrowdSale(
            _startTime,
            _endTime,
            _rate,
            _goal,
            _minimumAmount,
            maxTokenSupply,
            _wallet,
            token,
            kyc,
            false,
            walletRemaining,
            pendingOwner // transfer the ownership to the actual pending owner
        );

        // transfer the token owner ship to the crowdsale contract
        token.transferOwnership(currentIco);

        // only if successful until that point
        statePhase = 3;
    }


    function finalizeIco() onlyOwner public
    {
        if (statePhase==2)
        {
            finalizePreIcoDate = now;
        }else{
            finalizeIcoDate = now;
        }
        currentIco.finalize();
    }
     /**
      *
      * Token functions
      * can be called only during the prepare phase, because the owner change after this to be own by the crowdsale,
      * so nobody can do anything on the token at all from the controller perpective once rounds are started
      */
    function modifyTransferableHash(address _spender, bool value) onlyOwner public
    {
        // require(statePhase <=1 ); // only before rounds  - not for bitex
        // owner is the icoController

        // during the presale the token is own by 'this' so we can call it directly
        if (statePhase<=1)
        {
            token.modifyTransferableHash(_spender,value);
       }else{
           // during the crowdsale, it shall not be allowed 
           // but if it is needed by the project owner it can by calling the crowdsale object
           // own by 'this'
           currentIco.modifyTransferableHash(_spender, value);
        }

    }

    /**
      *
      * Admin functions only executed by owner:
      * Can change minimum amount
      *
      */
    function changeMinimumAmount(uint256 _minimumAmount) onlyOwner public {
        // avoid mistake
        // require(statePhase >=2 );
        // require(_minimumAmount > 0);

        currentIco.changeMinimumAmount(_minimumAmount);
    }

     /**
      *
      * Admin functions only executed by owner:
      * Can change rate
      *
      * We do not use an oracle here as oracle need to be paid each time, and if the oracle is not responding
      * or hacked the rate could be detrimentally modified from an contributor perspective.
      *
      */
    function changeRate(uint256 _rate) onlyOwner public {
        // avoid mistake
        // require(statePhase >=2 );
        currentIco.changeRate(_rate);
    }

    /**
      *
      * Admin functions only called by owner:
      * Can change events dates
      *
      */
    function changeDates(uint256 _startTime, uint256 _endTime) onlyOwner public {
        // avoid mistake
        // require(statePhase >=2 );
        currentIco.changeDates(_startTime, _endTime);
    }

    /**
      *
      * Admin functions only executed by pendingOwner
      * Change the owner of the crowdsale
      * --> thiscall shall be possible 15 days after the end of each of the event, and each of them would need to be finalized
      *     that way contributor have the time to get their refunds if needed.
      */
    function transferCrowdSale(bool preIco) onlyOwner public {
        if (preIco)
        {
            require(finalizePreIcoDate!=0);
            require(now>=(finalizePreIcoDate+30 days));
            preICO.transferOwnership(owner);
            kyc.transferOwnership(owner);
        }else{
            require(finalizeIcoDate!=0);
            require(now>=finalizeIcoDate+30 days);
            currentIco.transferOwnership(owner);
        }
    }

    /**
    *
    * Only by the whitelist admin
    *
    */
    function setContributor(address _address, bool cleared, uint16 contributor_get, uint16 affiliate_get, address ref) public{
        require(msg.sender == whiteListingAdmin);
        // this call is done under the icoController address the real owner of the kyc. It ensure that only the icoController can call the kyc smart contract
        // until the end of the event, where the ownership is transfered back to the pendingOwner
        kyc.setContributor(_address, cleared, contributor_get, affiliate_get, ref);
    }

   /**
      *
      * Admin functions only executed by pendingOwner
      * Change the owner
      *
      */
    function transferOwnerShipToPendingOwner() public {

        // only the pending owner can change the ownership
        require(msg.sender == pendingOwner);

        // // can only be changed one time - no impact removed
        // require(owner != pendingOwner);

        // raise the event
        emit OwnershipTransferred(owner, pendingOwner);

        // change the ownership
        owner = pendingOwner;

    }


}
function transferOwnerShipToPendingOwner() public {

        // only the pending owner can change the ownership
        require(msg.sender == pendingOwner);

        // // can only be changed one time - no impact removed
        // require(owner != pendingOwner);

        // raise the event
        emit OwnershipTransferred(owner, pendingOwner);

        // change the ownership
        owner = pendingOwner;

    }
/**
 *
 * Admin functions only executed by pendingOwner
 * Change the owner
 *
 */