contract CompanyMain is CompanyBase, ApprovalContract, TradingVolume {
    uint public withdrawableBalance;
    
    // The cut contract takes from the share sales of an approved company.
    // price is in wei
    function _computeSalesCut(uint _price) 
    pure
    internal 
    returns (uint) {
        return (_price * SALES_CUT)/100;
    }
    
    // Whenever there is transfer of tokens from _from to _to, CEO of company might get changed!
    function _updateCEOIfRequired(Company storage _company, uint _companyId, address _to) 
    internal {
        uint sharesRequiredToBecomeCEO = (_company.sharesInCirculation/2 ) + 1;
        address currentCEO = _company.ceoOfCompany;
        
        if (_company.shareHolders.ownerAddressToShares[currentCEO] >= sharesRequiredToBecomeCEO) {
            return;
        } 
        
        if (_to != address(this) && _company.shareHolders.ownerAddressToShares[_to] >= sharesRequiredToBecomeCEO) {
            _company.ceoOfCompany = _to;
            emit CEOChanged(_companyId, currentCEO, _to);
            return;
        }
        
        if (currentCEO == 0x0) {
            return;
        }
        _company.ceoOfCompany = 0x0;
        emit CEOChanged(_companyId, currentCEO, 0x0);
    }
    

    /// Transfer tokens from _from to _to and verify if CEO of company has changed!
    // _from should have enough tokens before calling this functions!
    // _numberOfTokens should be greator than 0
    function _transfer(uint _companyId, address _from, address _to, uint _numberOfTokens) 
    internal {
        Company storage company = companies[_companyId];
        
        _sharesSold(company.shareHolders, _from, _numberOfTokens);
        _sharesBought(company.shareHolders, _to, _numberOfTokens);

        _updateCEOIfRequired(company, _companyId, _to);
        
        emit Transfer(_companyId, _from, _to, _numberOfTokens);
    }
    
    function transferPromotionalShares(uint _companyId, address _to, uint _amount)
    onlyCLevel
    whenNotPaused
    external
    {
        Company storage company = companies[_companyId];
        // implies a promotional company
        require(company.pricePerShare == 0);
        require(companies[_companyId].shareHolders.ownerAddressToShares[msg.sender] >= _amount);
        _transfer(_companyId, msg.sender, _to, _amount);
    }
    
    function addPromotionalCompany(string _companyName, uint _precentageSharesToRelease, uint _coolDownTime, uint _sharesInCirculation)
    onlyCLevel
    whenNotPaused 
    external
    {
        bytes32 companyNameHash = keccak256(_companyName);
        
        // There shouldn't be a company that is already registered with same name!
        require(registredCompanyNames[companyNameHash] == false);
        
        // Max 10% shares can be released in one release cycle, to control liquidation
        // and uncontrolled issuing of new tokens. Furthermore the max shares that can
        // be released in one cycle can only be upto 500.
        require(_precentageSharesToRelease <= MAX_PERCENTAGE_SHARE_RELEASE);
        
        // The min release cycle should be at least 10 days
        require(_coolDownTime >= MIN_COOLDOWN_TIME && _coolDownTime <= MAX_COOLDOWN_TIME);

        uint _companyId = companies.length;
        uint _nextSharesReleaseTime = now + _coolDownTime * 1 days;
        
        Company memory company = Company({
            companyNameHash: companyNameHash,
            
            percentageSharesToRelease : uint32(_precentageSharesToRelease),
            coolDownTime : uint32(_coolDownTime),
            
            sharesInCirculation : uint32(_sharesInCirculation),
            nextSharesReleaseTime : _nextSharesReleaseTime,
            unclaimedShares : 0,
            
            pricePerShare : 0,
            
            ceoOfCompany : 0x0,
            ownedBy : msg.sender,
            shareHolders : ShareHolders({numberOfShareHolders : 0})
            });

        companies.push(company);
        _addNewCompanyTraders(_companyId);
        // Register company name
        registredCompanyNames[companyNameHash] = true;
        _sharesBought(companies[_companyId].shareHolders, msg.sender, _sharesInCirculation);
        emit Listed(_companyId, _companyName, _sharesInCirculation, 0, _precentageSharesToRelease, _nextSharesReleaseTime, msg.sender);
    }

    // Add a new company with the given name  
    function addNewCompany(string _companyName, uint _precentageSharesToRelease, uint _coolDownTime, uint _sharesInCirculation, uint _pricePerShare) 
    external 
    whenNotPaused 
    {
        bytes32 companyNameHash = keccak256(_companyName);
        
        // There shouldn't be a company that is already registered with same name!
        require(registredCompanyNames[companyNameHash] == false);
        
        // Owner have the permissions to launch the company
        require(approvedToLaunch[companyNameHash] == msg.sender);
        
        // Max 10% shares can be released in one release cycle, to control liquidation
        // and uncontrolled issuing of new tokens. Furthermore the max shares that can
        // be released in one cycle can only be upto 500.
        require(_precentageSharesToRelease <= MAX_PERCENTAGE_SHARE_RELEASE);
        
        // The min release cycle should be at least 10 days
        require(_coolDownTime >= MIN_COOLDOWN_TIME && _coolDownTime <= MAX_COOLDOWN_TIME);
        
        require(_sharesInCirculation >= INIT_MIN_SHARES_IN_CIRCULATION &&
        _sharesInCirculation <= INIT_MAX_SHARES_IN_CIRCULATION);

        uint _companyId = companies.length;
        uint _nextSharesReleaseTime = now + _coolDownTime * 1 days;

        Company memory company = Company({
            companyNameHash: companyNameHash,
            
            percentageSharesToRelease : uint32(_precentageSharesToRelease),
            nextSharesReleaseTime : _nextSharesReleaseTime,
            coolDownTime : uint32(_coolDownTime),
            
            sharesInCirculation : uint32(_sharesInCirculation),
            unclaimedShares : uint32(_sharesInCirculation),
            
            pricePerShare : _pricePerShare,
            
            ceoOfCompany : 0x0,
            ownedBy : msg.sender,
            shareHolders : ShareHolders({numberOfShareHolders : 0})
            });

        companies.push(company);
        _addNewCompanyTraders(_companyId);
        // Register company name
        registredCompanyNames[companyNameHash] = true;
        emit Listed(_companyId, _companyName, _sharesInCirculation, _pricePerShare, _precentageSharesToRelease, _nextSharesReleaseTime, msg.sender);
    }
    
    // People can claim shares from the company! 
    // The share price is fixed. However, once bought the users can place buy/sell
    // orders of any amount!
    function claimShares(uint _companyId, uint _numberOfShares) 
    whenNotPaused
    external 
    payable {
        Company storage company = companies[_companyId];
        
        require (_numberOfShares > 0 &&
            _numberOfShares <= (company.sharesInCirculation * MAX_CLAIM_SHARES_PERCENTAGE)/100);

        require(company.unclaimedShares >= _numberOfShares);
        
        uint totalPrice = company.pricePerShare * _numberOfShares;
        require(msg.value >= totalPrice);

        company.unclaimedShares -= uint32(_numberOfShares);

        _sharesBought(company.shareHolders, msg.sender, _numberOfShares);
        _updateCEOIfRequired(company, _companyId, msg.sender);

        if (totalPrice > 0) {
            uint salesCut = _computeSalesCut(totalPrice);
            withdrawableBalance += salesCut;
            uint sellerProceeds = totalPrice - salesCut;

            company.ownedBy.transfer(sellerProceeds);
        } 

        emit Claimed(_companyId, _numberOfShares, msg.sender);
    }
    
    // Company's next shares can be released only by the CEO of the company! 
    // So there should exist a CEO first
    function releaseNextShares(uint _companyId) 
    external 
    whenNotPaused {

        Company storage company = companies[_companyId];
        
        require(company.ceoOfCompany == msg.sender);
        
        // If there are unclaimedShares with the company, then new shares can't be relased!
        require(company.unclaimedShares == 0 );
        
        require(now >= company.nextSharesReleaseTime);

        company.nextSharesReleaseTime = now + company.coolDownTime * 1 days;
        
        // In worst case, we will be relasing max 500 tokens every 10 days! 
        // If we will start with max(10K) tokens, then on average we will be adding
        // 18000 tokens every year! In 100 years, it will be 1.8 millions. Multiplying it
        // by 10 makes it 18 millions. There is no way we can overflow the multiplication here!
        uint sharesToRelease = (company.sharesInCirculation * company.percentageSharesToRelease)/100;
        
        // Max 500 tokens can be relased
        if (sharesToRelease > MAX_SHARES_RELEASE_IN_ONE_CYCLE) {
            sharesToRelease = MAX_SHARES_RELEASE_IN_ONE_CYCLE;
        }
        
        if (sharesToRelease > 0) {
            company.sharesInCirculation += uint32(sharesToRelease);
            _sharesBought(company.shareHolders, company.ceoOfCompany, sharesToRelease);
            emit SharesReleased(_companyId, company.ceoOfCompany, sharesToRelease, company.nextSharesReleaseTime);
        }
    }
    
    function _updateTradingVolume(uint _companyId, address _from, address _to, uint _amount) 
    internal {
        Traders storage traders = companyIdToTraders[_companyId];
        _updateTradingVolume(traders, _from, _to, _amount);
        
        if (now < traders.relaseTime) {
            return;
        }
        
        Company storage company = companies[_companyId];
        uint _newShares = company.sharesInCirculation/100;
        if (_newShares > MAX_SHARES_RELEASE_IN_ONE_CYCLE) {
            _newShares = 100;
        }
        company.sharesInCirculation += uint32(_newShares);
         _sharesBought(company.shareHolders, traders.winningTrader, _newShares);
        _updateCEOIfRequired(company, _companyId, traders.winningTrader);
        emit TradingWinnerAnnounced(_companyId, traders.winningTrader, _newShares);
        _clearWinner(traders);
    }
}
function _updateCEOIfRequired(Company storage _company, uint _companyId, address _to) 
internal {
    uint sharesRequiredToBecomeCEO = (_company.sharesInCirculation/2 ) + 1;
    address currentCEO = _company.ceoOfCompany;
        
    if (_company.shareHolders.ownerAddressToShares[currentCEO] >= sharesRequiredToBecomeCEO) {
        return;
    } 
        
    if (_to != address(this) && _company.shareHolders.ownerAddressToShares[_to] >= sharesRequiredToBecomeCEO) {
        _company.ceoOfCompany = _to;
        emit CEOChanged(_companyId, currentCEO, _to);
        return;
    }
        
    if (currentCEO == 0x0) {
        return;
    }
    _company.ceoOfCompany = 0x0;
    emit CEOChanged(_companyId, currentCEO, 0x0);
}

// Whenever there is transfer of tokens from _from to _to, CEO of company might get changed!