contract DividableAsset is AssetHashToken, ERC20Interface {
    using SafeMath for uint;

    ERC20Interface stableToken;

    string public name;
    string public symbol;
    uint8 public decimals;
    uint public _totalSupply;

    address operator;

    uint collectPrice;

    address[] internal allowners;
    mapping (address => uint) public indexOfowner;

    mapping (address => uint) public balances;
    mapping (address => mapping (address => uint)) public allowed;

    modifier onlySplitted {
        require(isSplitted == true, "Splitted status required");
        _;
    }

    modifier onlyOperator {
        require(operator == msg.sender, "Operation only permited by operator");
        _;
    }

    /**
     * The force collect event
     */
    event ForceCollectEvent (
        uint id,
        uint price,
        address operator
    );

    /**
     * The token split event
     */
    event TokenSplitEvent (
        uint id,
        uint supply,
        uint8 decim,
        uint price
    );

    /**
     * The merge event
     */
    event TokenMergeEvent (
        uint id,
        address owner
    );

    constructor(
        string _name,
        string _symbol,
        address _tokenAddress,
        uint _id,
        address _owner,
        uint _assetPrice,
        string _assetFileUrl,
        string _assetFileHashType,
        string _assetFileHashValue,
        string _legalFileUrl,
        string _legalFileHashType,
        string _legalFileHashValue
        ) public
        AssetHashToken(
            _id,
            _owner,
            _assetPrice,
            _assetFileUrl,
            _assetFileHashType,
            _assetFileHashValue,
            _legalFileUrl,
            _legalFileHashType,
            _legalFileHashValue
        )
    {
        name = _name;
        symbol = _symbol;
        operator = msg.sender; // TODO set to HashFuture owned address
        stableToken = ERC20Interface(_tokenAddress);
    }

    // ERC 20 Basic Functionality

    /**
     * Total supply
     */
    function totalSupply() public view returns (uint) {
        return _totalSupply.sub(balances[address(0)]);
    }

    /**
     * Get the token balance for account `tokenOwner`
     */
    function balanceOf(address tokenOwner) public view returns (uint balance) {
        return balances[tokenOwner];
    }

    /**
     * Returns the amount of tokens approved by the owner that can be
     * transferred to the spender's account
     */
    function allowance(address tokenOwner, address spender)
        public view
        returns (uint remaining)
    {
        return allowed[tokenOwner][spender];
    }

    /**
     * Transfer the balance from token owner's account to `to` account
     * - Owner's account must have sufficient balance to transfer
     * - 0 value transfers are allowed
     */
    function transfer(address to, uint tokens)
        public
        onlySplitted
        returns (bool success)
    {
        require(tokens > 0);
        balances[msg.sender] = balances[msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);


        // ensure that each address appears only once in allowners list
        // so that distribute divident or force collect only pays one time
        if (indexOfowner[to] == 0) {
            allowners.push(to);
            indexOfowner[to] = allowners.length;
        }
        // could be removed? no
        if (balances[msg.sender] == 0) {
            uint index = indexOfowner[msg.sender].sub(1);
            indexOfowner[msg.sender] = 0;

            if (index != allowners.length.sub(1)) {
                allowners[index] = allowners[allowners.length.sub(1)];
                indexOfowner[allowners[index]] = index.add(1);
            }

            //delete allowners[allowners.length.sub(1)];
            allowners.length = allowners.length.sub(1);
        }
        emit Transfer(msg.sender, to, tokens);
        return true;
    }

    /**
     * Token owner can approve for `spender` to transferFrom(...) `tokens`
     * from the token owner's account
     */
    function approve(address spender, uint tokens)
        public
        onlySplitted
        returns (bool success)
    {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }

    /**
     * Transfer `tokens` from the `from` account to the `to` account
     */
    function transferFrom(address from, address to, uint tokens)
        public
        onlySplitted
        returns (bool success)
    {
        require(tokens > 0);
        balances[from] = balances[from].sub(tokens);
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
        balances[to] = balances[to].add(tokens);

        // ensure that each address appears only once in allowners list
        // so that distribute divident or force collect only pays one time
        if (indexOfowner[to] == 0) {
            allowners.push(to);
            indexOfowner[to] = allowners.length;
        }

        // could be removed? no
        if (balances[from] == 0) {
            uint index = indexOfowner[from].sub(1);
            indexOfowner[from] = 0;

            if (index != allowners.length.sub(1)) {
                allowners[index] = allowners[allowners.length.sub(1)];
                indexOfowner[allowners[index]] = index.add(1);
            }
            //delete allowners[allowners.length.sub(1)];
            allowners.length = allowners.length.sub(1);
        }

        emit Transfer(from, to, tokens);
        return true;
    }

    /**
     * 
     * Warning: may fail when number of owners exceeds 100 due to gas limit of a block in Ethereum.
     */
    function distributeDivident(uint amount) public {
        // stableToken.approve(address(this), amount)
        // should be called by the caller to the token contract in previous
        uint value = 0;
        uint length = allowners.length;
        require(stableToken.balanceOf(msg.sender) >= amount, "Insufficient balance for sender");
        require(stableToken.allowance(msg.sender, address(this)) >= amount, "Insufficient allowance for contract");
        for (uint i = 0; i < length; i++) {
            //value = amount * balances[allowners[i]] / _totalSupply;
            value = amount.mul(balances[allowners[i]]);
            value = value.div(_totalSupply);

            // Always use a require when doing token transfer!
            // Do not think it works like the transfer method for ether,
            // which handles failure and will throw for you.
            require(stableToken.transferFrom(msg.sender, allowners[i], value));
        }
    }
    
    /**
     * Collect all small keys in batches.
     * Anyone can force collect all keys if he provides with sufficient stable tokens.
     * However, due to the gas limitation of Ethereum, he can not collect all keys
     * with only one call. Hence an agent that can be trusted is need.
     * The operator is such an agent who will first receive a request to collect all keys,
     * and then collect them with the stable tokens provided by the claimer.
     * @param _address each address in the array means a target address to be collected from.
     */
    function collectAllForce(address[] _address) public onlyOperator {
        // stableToken.approve(address(this), amount)
        // should be called by the caller to the token contract in previous
        uint value = 0;
        uint length = _address.length;

        uint total_amount = 0;

        for (uint j = 0; j < length; j++) {
            if (indexOfowner[_address[j]] == 0) {
                continue;
            }

            total_amount = total_amount.add(collectPrice.mul(balances[_address[j]]));
        }

        require(stableToken.balanceOf(msg.sender) >= total_amount, "Insufficient balance for sender");
        require(stableToken.allowance(msg.sender, address(this)) >= total_amount, "Insufficient allowance for contract");

        for (uint i = 0; i < length; i++) {
            // Always use a require when doing token transfer!
            // Do not think it works like the transfer method for ether,
            // which handles failure and will throw for you.
            if (indexOfowner[_address[i]] == 0) {
                continue;
            }

            value = collectPrice.mul(balances[_address[i]]);

            require(stableToken.transferFrom(msg.sender, _address[i], value));
            balances[msg.sender] = balances[msg.sender].add(balances[_address[i]]);
            emit Transfer(_address[i], msg.sender, balances[_address[i]]);

            balances[_address[i]] = 0;

            uint index = indexOfowner[_address[i]].sub(1);
            indexOfowner[_address[i]] = 0;

            if (index != allowners.length.sub(1)) {
                allowners[index] = allowners[allowners.length.sub(1)];
                indexOfowner[allowners[index]] = index.add(1);
            }
            allowners.length = allowners.length.sub(1);
        }

        emit ForceCollectEvent(id, collectPrice, operator);
    }
    
    /**
     * key inssurance. Split the whole token into small keys.
     * Only the owner can perform this when the token is still valid and unsplitted.
     * @param _supply Totol supply in ERC20 standard
     * @param _decim  Decimal parameter in ERC20 standard
     * @param _price The force acquisition price. If a claimer is willing to pay more than this value, he can
     * buy the keys forcibly. Notice: the claimer can only buy all keys at one time or buy nothing and the
     * buying process is delegated into a trusted agent. i.e. the operator.
     * @param _address The initial distribution plan for the keys. This parameter contains the addresses.
     * @param _amount  The amount corresponding to the initial distribution addresses.
     */
    function split(uint _supply, uint8 _decim, uint _price, address[] _address, uint[] _amount)
        public
        onlyOwner
        onlyValid
        onlyUnsplitted
    {
        require(_address.length == _amount.length);

        isSplitted = true;
        _totalSupply = _supply * 10 ** uint(_decim);
        decimals = _decim;
        collectPrice = _price;

        uint amount = 0;
        uint length = _address.length;

        balances[msg.sender] = _totalSupply;
        if (indexOfowner[msg.sender] == 0) {
            allowners.push(msg.sender);
            indexOfowner[msg.sender] = allowners.length;
        }
        emit Transfer(address(0), msg.sender, _totalSupply);

        for (uint i = 0; i < length; i++) {
            amount = _amount[i]; // * 10 ** uint(_decim);
            balances[_address[i]] = amount;
            balances[msg.sender] = balances[msg.sender].sub(amount);

            // ensure that each address appears only once in allowners list
            // so that distribute divident or force collect only pays one time
            if (indexOfowner[_address[i]] == 0) {
                allowners.push(_address[i]);
                indexOfowner[_address[i]] = allowners.length;
            }
            emit Transfer(msg.sender, _address[i], amount);
        }

        emit TokenSplitEvent(id, _supply, _decim, _price);
    }
    
    /**
     * Token conversion. Turn the keys to a whole token.
     * Only the sender with all keys in hand can perform this and he will be the new owner.
     */
    function merge() public onlyValid onlySplitted {
        require(balances[msg.sender] == _totalSupply);
        _totalSupply = 0;
        balances[msg.sender] = 0;
        owner = msg.sender;
        isTradable = false;
        isSplitted = false;
        emit Transfer(msg.sender, address(0), _totalSupply);
        emit TokenMergeEvent(id, msg.sender);
    }
}
function allowance(address tokenOwner, address spender)
    public view
    returns (uint remaining)
{
    return allowed[tokenOwner][spender];
}

/**
 * Returns the amount of tokens approved by the owner that can be
 * transferred to the spender's account
 */