contract MarketBase is CompanyMain {
    
    function MarketBase() public {
        ceoAddress = msg.sender;
        cfoAddress = msg.sender;
    }
    
    struct Order {
        // Owner who placed the order
        address owner;
                
        // Total number of tokens in order
        uint32 amount;
        
        // Amount of tokens that are already bought/sold by other people
        uint32 amountFilled;
        
        // Type of the order
        OrderType orderType;
        
        // Price of one share
        uint pricePerShare;
    }
    
    // A mapping of companyId to orders
    mapping (uint => Order[]) companyIdToOrders;
    
    // _amount > 0
    function _createOrder(uint _companyId, uint _amount, uint _pricePerShare, OrderType _orderType) 
    internal {
        Order memory order = Order({
            owner : msg.sender,
            pricePerShare : _pricePerShare,
            amount : uint32(_amount),
            amountFilled : 0,
            orderType : _orderType
        });
        
        uint index = companyIdToOrders[_companyId].push(order) - 1;
        emit OrderPlaced(_companyId, index, order.amount, order.pricePerShare, order.orderType, msg.sender);
    }
    
    // Place a sell request if seller have enough tokens!
    function placeSellRequest(uint _companyId, uint _amount, uint _pricePerShare) 
    whenNotPaused
    external {
        require (_amount > 0);
        require (_pricePerShare > 0);

        // Seller should have enough tokens to place a sell order!
        _verifyOwnershipOfTokens(_companyId, msg.sender, _amount);

        _transfer(_companyId, msg.sender, this, _amount);
        _createOrder(_companyId, _amount, _pricePerShare, OrderType.Sell);
    }
    
    // Place a request to buy shares of a particular company!
    function placeBuyRequest(uint _companyId, uint _amount, uint _pricePerShare) 
    external 
    payable 
    whenNotPaused {
        require(_amount > 0);
        require(_pricePerShare > 0);
        require(_amount == uint(uint32(_amount)));
        
        // Should have enough eth!
        require(msg.value >= _amount * _pricePerShare);

        _createOrder(_companyId, _amount, _pricePerShare, OrderType.Buy);
    }
    
    // Cancel a placed order!
    function cancelRequest(uint _companyId, uint _orderIndex) 
    external {        
        Order storage order = companyIdToOrders[_companyId][_orderIndex];
        
        require(order.owner == msg.sender);
        
        uint sharesRemaining = _getRemainingSharesInOrder(order);
        
        require(sharesRemaining > 0);

        order.amountFilled += uint32(sharesRemaining);
        
        if (order.orderType == OrderType.Buy) {

             // If its a buy order, transfer the ether back to owner;
            uint price = _getTotalPrice(order, sharesRemaining);
            
            // Sends money back to owner!
            msg.sender.transfer(price);
        } else {
            
            // Send the tokens back to the owner
            _transfer(_companyId, this, msg.sender, sharesRemaining);
        }

        emit OrderCancelled(_companyId, _orderIndex);
    }
    
    // Fill the sell order!
    function fillSellOrder(uint _companyId, uint _orderIndex, uint _amount) 
    whenNotPaused
    external 
    payable {
        require(_amount > 0);
        
        Order storage order = companyIdToOrders[_companyId][_orderIndex];
        require(order.orderType == OrderType.Sell);
        
        require(msg.sender != order.owner);
       
        _verifyRemainingSharesInOrder(order, _amount);

        uint price = _getTotalPrice(order, _amount);
        require(msg.value >= price);

        order.amountFilled += uint32(_amount);
        
        // transfer tokens to the buyer
        _transfer(_companyId, this, msg.sender, _amount);
        
        // send money to seller after taking a small share
        _transferOrderMoney(price, order.owner);  
        
        _updateTradingVolume(_companyId, msg.sender, order.owner, _amount);
        
        emit OrderFilled(_companyId, _orderIndex, _amount, msg.sender);
    }
    
    // Fill the sell order!
    function fillSellOrderPartially(uint _companyId, uint _orderIndex, uint _maxAmount) 
    whenNotPaused
    external 
    payable {
        require(_maxAmount > 0);
        
        Order storage order = companyIdToOrders[_companyId][_orderIndex];
        require(order.orderType == OrderType.Sell);
        
        require(msg.sender != order.owner);
       
        uint buyableShares = _getRemainingSharesInOrder(order);
        require(buyableShares > 0);
        
        if (buyableShares > _maxAmount) {
            buyableShares = _maxAmount;
        }

        uint price = _getTotalPrice(order, buyableShares);
        require(msg.value >= price);

        order.amountFilled += uint32(buyableShares);
        
        // transfer tokens to the buyer
        _transfer(_companyId, this, msg.sender, buyableShares);
        
        // send money to seller after taking a small share
        _transferOrderMoney(price, order.owner); 
        
        _updateTradingVolume(_companyId, msg.sender, order.owner, buyableShares);
        
        uint buyerProceeds = msg.value - price;
        msg.sender.transfer(buyerProceeds);
        
        emit OrderFilled(_companyId, _orderIndex, buyableShares, msg.sender);
    }

    // Fill the buy order!
    function fillBuyOrder(uint _companyId, uint _orderIndex, uint _amount) 
    whenNotPaused
    external {
        require(_amount > 0);
        
        Order storage order = companyIdToOrders[_companyId][_orderIndex];
        require(order.orderType == OrderType.Buy);
        
        require(msg.sender != order.owner);
        
        // There should exist enought shares to fulfill the request!
        _verifyRemainingSharesInOrder(order, _amount);
        
        // The seller have enought tokens to fulfill the request!
        _verifyOwnershipOfTokens(_companyId, msg.sender, _amount);
        
        order.amountFilled += uint32(_amount);
        
        // transfer the tokens from the seller to the buyer!
        _transfer(_companyId, msg.sender, order.owner, _amount);
        
        uint price = _getTotalPrice(order, _amount);
        
        // transfer the money from this contract to the seller
        _transferOrderMoney(price , msg.sender);
        
        _updateTradingVolume(_companyId, msg.sender, order.owner, _amount);

        emit OrderFilled(_companyId, _orderIndex, _amount, msg.sender);
    }
    
    // Fill buy order partially if possible!
    function fillBuyOrderPartially(uint _companyId, uint _orderIndex, uint _maxAmount) 
    whenNotPaused
    external {
        require(_maxAmount > 0);
        
        Order storage order = companyIdToOrders[_companyId][_orderIndex];
        require(order.orderType == OrderType.Buy);
        
        require(msg.sender != order.owner);
        
        // There should exist enought shares to fulfill the request!
        uint buyableShares = _getRemainingSharesInOrder(order);
        require(buyableShares > 0);
        
        if ( buyableShares > _maxAmount) {
            buyableShares = _maxAmount;
        }
        
        // The seller have enought tokens to fulfill the request!
        _verifyOwnershipOfTokens(_companyId, msg.sender, buyableShares);
        
        order.amountFilled += uint32(buyableShares);
        
        // transfer the tokens from the seller to the buyer!
        _transfer(_companyId, msg.sender, order.owner, buyableShares);
        
        uint price = _getTotalPrice(order, buyableShares);
        
        // transfer the money from this contract to the seller
        _transferOrderMoney(price , msg.sender);
        
        _updateTradingVolume(_companyId, msg.sender, order.owner, buyableShares);

        emit OrderFilled(_companyId, _orderIndex, buyableShares, msg.sender);
    }

    // transfer money to the owner!
    function _transferOrderMoney(uint _price, address _owner) 
    internal {
        uint priceCut = (_price * ORDER_CUT)/100;
        _owner.transfer(_price - priceCut);
        withdrawableBalance += priceCut;
    }

    // Returns the price for _amount tokens for the given order
    // _amount > 0
    // order should be verified
    function _getTotalPrice(Order storage _order, uint _amount) 
    view
    internal 
    returns (uint) {
        return _amount * _order.pricePerShare;
    }
    
    // Gets the number of remaining shares that can be bought or sold under this order
    function _getRemainingSharesInOrder(Order storage _order) 
    view
    internal 
    returns (uint) {
        return _order.amount - _order.amountFilled;
    }

    // Verifies if the order have _amount shares to buy/sell
    // _amount > 0
    function _verifyRemainingSharesInOrder(Order storage _order, uint _amount) 
    view
    internal {
        require(_getRemainingSharesInOrder(_order) >= _amount);
    }

    // Checks if the owner have at least '_amount' shares of the company
    // _amount > 0
    function _verifyOwnershipOfTokens(uint _companyId, address _owner, uint _amount) 
    view
    internal {
        require(companies[_companyId].shareHolders.ownerAddressToShares[_owner] >= _amount);
    }
    
    // Returns the length of array! All orders might not be active
    function getNumberOfOrders(uint _companyId) 
    view
    external 
    returns (uint numberOfOrders) {
        numberOfOrders = companyIdToOrders[_companyId].length;
    }

    function getOrderDetails(uint _comanyId, uint _orderIndex) 
    view
    external 
    returns (address _owner,
        uint _pricePerShare,
        uint _amount,
        uint _amountFilled,
        OrderType _orderType) {
            Order storage order =  companyIdToOrders[_comanyId][_orderIndex];
            
            _owner = order.owner;
            _pricePerShare = order.pricePerShare;
            _amount = order.amount;
            _amountFilled = order.amountFilled;
            _orderType = order.orderType;
    }
    
    function withdrawBalance(address _address) 
    onlyCLevel
    external {
        require(_address != 0x0);
        uint balance = withdrawableBalance;
        withdrawableBalance = 0;
        _address.transfer(balance);
    }
    
    // Only when the contract is paused and there is a subtle bug!
    function kill(address _address) 
    onlyCLevel
    whenPaused
    external {
        require(_address != 0x0);
        selfdestruct(_address);
    }
}
function _verifyOwnershipOfTokens(uint _companyId, address _owner, uint _amount) 
view
internal {
    require(companies[_companyId].shareHolders.ownerAddressToShares[_owner] >= _amount);
}

// Checks if the owner have at least '_amount' shares of the company
// _amount > 0