pragma solidity ^0.5.1;

import "./KMToken.sol";
import "./Owned.sol";
import "./BC.sol";
import "./TokenFactory.sol";
import "./Storage.sol";


contract KMP is Owned, Storage {

    // Events
    event KMPCompanyCreated(address company, string name, address owner);
    event KMPTokenCreated(address _company, address _token, string _name, string _symbol, uint256 _initialAmount);
    event KMMsgSender(address indexed msgSender);
    event KMUserTokenBalance(address indexed user, uint256 balance);
    event KMReturnedToken(KMToken returnToken);
    event KMReturnedBC(BC returnBC);
    event KMTokenAssigned(address _from, address _to, uint256 _amount);

    constructor() public {
        bcFactory = new BCFactory(msg.sender);
        tkFactory = new TokenFactory(msg.sender);
    }
   
    function factoryOwnerUtil() 
        public
        view
        returns (address)
    {
        return bcFactory.owner();   
    }    
    
    function testCreateBC() 
        public 
        returns (BC)
    { 
        address anAddress = 0xdA35deE8EDDeAA556e4c26268463e26FB91ff74f;
        return createBCCompany("Company Name", "123456789", "www.google.com", "did:eth:0x2f3fcf4c3", anAddress);
    }
   
    
    function testCreateCompanyAndToken() public {
        BC newCompany = testCreateBC();
        testCreateToken(address(newCompany));
    }
    
    function testCreateToken(address anAddress) public{
        createTokenForBCCompany(anAddress, "Tokenzito", "TKZT", 1000);
     }
    
    function testAssignTokenToUser(address company, address token, address user, uint256 amount) public {
        assignTokenToUser(company, token,  user, amount);
    }
    
    function assignTokenToUser(address _company, address _token, address _user, uint256 _amount)
        public
        returns (bool tokenTransfered)
    {
        require(msg.sender == findBCowner(_company), "Only company owner can transfer tokens.");
        require(EMPTY_ADDRESS != findBCToken(_company, _token)); 
        bytes memory payload = abi.encodeWithSignature("transfer(address,uint256)", _user, _amount);
        (bool tokenTransfered, bytes memory returnData) = _token.callNOFUNCIONA(payload);
        if (tokenTransfered){
            emit KMTokenAssigned(msg.sender, _user, _amount);
        }
        return tokenTransfered;
        
    }
     
     
    function getUserTokenBalance(address _company, address _token, address _user)
        public
        returns (uint256 aBalance)
    {
        require(msg.sender == findBCowner(_company), "Only company owner can query token balances.");
        require(EMPTY_ADDRESS != findBCToken(_company, _token)); 
        bytes memory payload = abi.encodeWithSignature("balanceOf(address)", _user);
        (bool result, bytes memory returnData) = _token.staticcall(payload);
        if (result) {
            emit KMMsgSender(msg.sender);
            aBalance = abi.decode(returnData, (uint256));
            return aBalance;
        }
         
    }
    
    
    
    function createBCCompany(string memory _companyName, string memory _phone, string memory _url, string memory _did, address _uPortAddress) 
    public 
   // ownerOnly(msg.sender) I want anybody to be able to create a new BC on my platform.
    returns(BC){
       (bool companyCreated, bytes memory returnData) = address(bcFactory).delegatecall(
            abi.encodeWithSignature("createBCCompany(string,string,string,string,address)",
            _companyName, _phone, _url, _did, _uPortAddress));
        if (companyCreated){
            (BC newCompany) = abi.decode(returnData, (BC));
            emit KMPCompanyCreated(address(newCompany), newCompany.name(), newCompany.owner());
            return newCompany;
        }
        revert("Unfortunately your company was not created correctly. Please contact KMP Support. Reverting state changes."); 
    } 
    
    
    
    function findBCownerUtil(address company) // Util methods are for development purposes only. 
        public
        view
        //ownerOnly(msg.sender)
        returns (address)
    {
        return findBCowner(company);
    }
    
    
    function findBCowner(address aCompany)
        internal
        view
        returns (address)
    {
        address[MAX_OWNER_COMPANIES] memory ownerCompanies = companies[msg.sender];
        for (uint8 i = 0; i < MAX_OWNER_COMPANIES; i++) {
            if (ownerCompanies[i] == aCompany){
                return BC(ownerCompanies[i]).owner();
            }
        }
        revert("Company address not found.");
    }
    
    function tokenInBC(address aCompany, address aToken)
        public 
        returns (bool)
    {
        require(msg.sender == findBCowner(aCompany), "Only company owner can search for tokens.");
        address[MAX_COMPANY_TOKENS] memory companyTokens = tokens[aCompany];
        for (uint8 i = 0; i < MAX_COMPANY_TOKENS; i++) {
            if (companyTokens[i] == aToken){
                return true; // Token found.
            }
        }
       return false; // Token not found. 
    }
    
    function findBCToken(address aCompany, address aToken)
        public 
        returns (address)
    {
        if (tokenInBC(aCompany, aToken)){
            return aToken;
        }
        return EMPTY_ADDRESS;
    }
    
   /* function companiesLimitReached()
        internal
        view
        returns (bool)
    {
        address anOwner = msg.sender; // Check if msg.sender gets here or if I need param.
        address[MAX_OWNER_COMPANIES] memory ownersCompanies = companies[anOwner];
        for (uint8 i = 0; i < MAX_OWNER_COMPANIES; i++) {
            if (ownersCompanies[i] == 0){
                return true; // There is an empty spot available.
            }
        }
       return false; // No empty spot available. 
    }*/
    
    function createTokenForBCCompany(address _bcCompany, string memory _name, string memory _symbol, uint256 _initialAmount) 
    public 
    //ownerOnly(companies[bcCompany].owner()) // Only Company owner can create tokens.
    returns(KMToken){
        
        require (msg.sender == findBCowner(_bcCompany), "Only company owner can create tokens.");
        
       (bool tokenCreated, bytes memory returnData) = address(tkFactory).delegatecall(
            abi.encodeWithSignature("createTokenForBCCompany(address,string,string,uint256)",
            _bcCompany, _name, _symbol, _initialAmount));
        if (tokenCreated){
            (KMToken newToken) = abi.decode(returnData, (KMToken));
            emit KMPTokenCreated(_bcCompany, address(newToken), _name, _symbol, _initialAmount);
            return newToken;
        }
        revert("Unfortunately your token was not created correctly. Please contact KMP Support. Reverting state changes."); 
        
    }
    
    function getTotalSupply(address token)
        public
        view
        returns (uint256)
    {
        return KMToken(token).totalSupply();
    }
    
    function getUserBalance(address token, address user)
        public
        
        returns (uint256)
    {
        return KMToken(token).balanceOf(user);
    }
    
    function kmpOwner()
        public
        view
        returns (address)
    {
        return owner;
    }
}