// Contract: CryptoSharesFactory
// Target function: function approve(address _spender, uint _value) public returns (bool success)
 public shareholderID;

    /*
    * @param shareholderID The same as in shareholderID mapping
    * @param shareholderEthereumAddress Ethereum address of the shareholder
    * @param shareholderName Legal name of the shareholder, it can be name of the legal person, or fist and last name
    * for the physical person
    * @param shareholderRegistrationNumber Registration number of the legal person or personal ID of the physical person
    * @param shareholderAddress Shareholder's legal address (house number, street, city, zip, country)
    * @param  shareholderIsLegalPerson True if shareholder is a legal person, false if shareholder is a physical person
    * @param linkToSignersAuthorityToRepresentTheShareholder Link to ledger/register record or to document that
    * contains information about person's that manages ETH address  authority to represent the shareholder. If shareholder
    * is a physical person that represents himself/herself can contain "no representation" string
    * @param balanceOf This is the same as balanceOf(shareholderEthereumAddress), stored here for convenience
    * (to get all shareholder's data in one request)
    */
    struct Shareholder {
        uint shareholderID;                                     // 1
        address payable shareholderEthereumAddress;             // 2
        string shareholderName;                                 // 3
        string shareholderRegistrationNumber;                   // 4
        string shareholderAddress;                              // 5
        bool shareholderIsLegalPerson;                          // 6
        string linkToSignersAuthorityToRepresentTheShareholder; // 7
        uint balanceOf;                                         // 8
    }

    mapping(uint => Shareholder) public shareholdersLedgerByIdNumber;

    mapping(address => Shareholder) public shareholdersLedgerByEthAddress;

    event shareholderAddedOrUpdated(
        uint indexed shareholderID,
        address shareholderEthereumAddress,
        bool indexed isLegalPerson,
        string shareholderName,
        string shareholderRegistrationNumber,
        string shareholderAddress,
        uint shares,
        bool indexed newRegistration
    );

    /*
    * @notice Using this function a token holder can register himself as shareholder.
    * Any share (token) not owned of registered shareholder is considered 'in transfer'.
    * Shares 'in transfer' can not vote and does not receive dividends.
    * In the ledger (variables: 'shareholderID', 'shareholderEthereumAddress' and so on ) we store data of all
    * historical shareholders,
    * and they persist even if a shareholder transferred all his shares and his 'balanceOf' is zero.
    * @param _isLegalPerson This indicates if new shareholder is a legal person or physical person.
    * @param _shareholderName Legal name of the shareholder. If this is different from name registered in Cryptonomica
    * verification smart contract, we consider a person registered in Cryptonomica smart contract representative of the
    * shareholder.
    * @param _shareholderRegistrationNumber Registration number of a legal person or personal ID number for physical person.
    * @param  _shareholderAddress Shareholder's legal address (country/state, street, building/house number, apartment/office number)
    *
    * we allow allow change/update shareholder data (if entered incorrectly or some data changed) by calling
    * this function again by existing shareholder
    */
    function registerAsShareholderAndSignArbitrationAgreement(
        bool _isLegalPerson,
        string calldata _shareholderName,
        string calldata _shareholderRegistrationNumber,
        string calldata _shareholderAddress,
        string calldata _linkToSignersAuthorityToRepresentTheShareholder
    ) external returns (bool success){

        require(
            balanceOf[msg.sender] > 0,
            "To be registered address has to hold at least one token/share"
        );

        require(
            addressIsVerifiedByCryptonomica(msg.sender),
            "Shareholder address has to be verified on Cryptonomica"
        );

        bool newShareholder;
        uint id;

        if (shareholderID[msg.sender] == 0) {
            shareholdersCounter++;
            id = shareholdersCounter;
            shareholderID[msg.sender] = id;
            newShareholder = true;
            /* add these shares to shares of registered shareholders (i.e. not "in transfer"*/
            registeredShares = registeredShares.add(balanceOf[msg.sender]);
        } else {
            id = shareholderID[msg.sender];
            newShareholder = false;
        }

        // 1
        shareholdersLedgerByIdNumber[id].shareholderID = id;
        // 2
        shareholdersLedgerByIdNumber[id].shareholderEthereumAddress = msg.sender;
        // 3
        shareholdersLedgerByIdNumber[id].shareholderName = _shareholderName;
        // 4
        shareholdersLedgerByIdNumber[id].shareholderRegistrationNumber = _shareholderRegistrationNumber;
        // 5
        shareholdersLedgerByIdNumber[id].shareholderAddress = _shareholderAddress;
        // 6
        shareholdersLedgerByIdNumber[id].shareholderIsLegalPerson = _isLegalPerson;
        // 7
        shareholdersLedgerByIdNumber[id].linkToSignersAuthorityToRepresentTheShareholder = _linkToSignersAuthorityToRepresentTheShareholder;
        // 8
        shareholdersLedgerByIdNumber[id].balanceOf = balanceOf[msg.sender];

        /* copy struct  */
        shareholdersLedgerByEthAddress[msg.sender] = shareholdersLedgerByIdNumber[id];

        emit shareholderAddedOrUpdated(
            shareholdersLedgerByIdNumber[id].shareholderID,
            shareholdersLedgerByIdNumber[id].shareholderEthereumAddress,
            shareholdersLedgerByIdNumber[id].shareholderIsLegalPerson,
            shareholdersLedgerByIdNumber[id].shareholderName,
            shareholdersLedgerByIdNumber[id].shareholderRegistrationNumber,
            shareholdersLedgerByIdNumber[id].shareholderAddress,
            shareholdersLedgerByIdNumber[id].balanceOf,
            newShareholder
        );

        /*
        * even if shareholder updates data he makes new signature under dispute resolution agreement
        */
        signDisputeResolutionAgreement(
            id,
            shareholdersLedgerByIdNumber[id].shareholderName,
            shareholdersLedgerByIdNumber[id].shareholderRegistrationNumber,
            shareholdersLedgerByIdNumber[id].shareholderAddress
        );

        return true;
    }

    /* ---------------- Dividends --------------- */

    /**
    * Time in seconds between dividends distribution rounds.
    * Next round can be started only if the specified number of seconds has elapsed since the end of the previous round.
    * See: https://en.wikipedia.org/wiki/Dividend#Dividend_frequency
    */
    uint public dividendsPeriod;

    /*
    * @param roundIsRunning Shows if this dividends payouts round is running.
    * @param sumWeiToPayForOneToken Sum in wei to pay for one token in this round.
    * @param sumXEurToPayForOneToken Sum in xEUR to pay for one token in this round.
    * @param allRegisteredShareholders Number of all shareholder registered in whole smart contract.
    * history, at the moment this round was started.
    * @shareholdersCounter Number (id) of shareholder to whom last payment was made.
    * On the start it will be 0, and on the end of the round shareholdersCounter == allRegisteredShareholders.
    * @registeredShares Number of shares that will receive dividends in this round, i.e. number of shares,
    * hold by registered shareholder (that's all shares minus shares 'in transfer')
    * @param roundStartedOnUnixTime Timestamp.
    * @param roundFinishedOnUnixTime Timestamp.
    * @param weiForTxFees Amount in wei deposited for this round to reward those who make transactions to distribute
    * dividends.
    */
    struct DividendsRound {
        bool roundIsRunning; //............0
        uint sumWeiToPayForOneToken; //....1
        uint sumXEurToPayForOneToken; //...2
        uint allRegisteredShareholders; //.3
        uint shareholdersCounter; //.......4
        uint registeredShares; //..........5
        uint roundStartedOnUnixTime; //....6
        uint roundFinishedOnUnixTime; //...7
        uint weiForTxFees; //..............8
    }

    /**
    * 'dividendsRoundsCounter' holds the sequence number of the last (or current) round of dividends payout
    * We record all historical dividends payouts data
    */
    uint public dividendsRoundsCounter;
    mapping(uint => DividendsRound) public dividendsRound;

    /*
    * @param dividendsRound Number of dividends distribution round.
    * @param startedBy ETH address that started round (if time to pay dividends, can be started by any ETH address)
    * @param totalWei Sum in wei that has to be distributed in this round.
    * @param totalXEur Sum in xEUR that has to be distributed in this round.
    * @param sharesToPayDividendsTo The same as 'registeredShares' in struct DividendsRound.
    * @param sumWeiToPayForOneShare Sum in wei to pay for one token in this round.
    * @param sumXEurToPayForOneShare Sum in xEUR to pay for one token in this round.
    */
    event DividendsPaymentsStarted(
        uint indexed dividendsRound, //..0
        address indexed startedBy, //...1
        uint totalWei, //................2
        uint totalXEur, //...............3
        uint sharesToPayDividendsTo, //..4
        uint sumWeiToPayForOneShare, //..5
        uint sumXEurToPayForOneShare //..6
    );

    /**
    * @param dividendsRound Number of dividends distribution round
    */
    event DividendsPaymentsFinished(
        uint indexed dividendsRound
    );

    /**
    * dev: Info about the payment in ETH made to next shareholder
    */
    event DividendsPaymentEther (
        bool indexed success,
        address indexed to,
        uint shareholderID,
        uint shares,
        uint sumWei,
        uint indexed dividendsRound
    );

    /**
    * dev: Info about the payment in xEUR made to next shareholder
    */
    event DividendsPaymentXEuro (
        bool indexed success,
        address indexed to,
        uint shareholderID,
        uint shares,
        uint sumXEuro,
        uint indexed dividendsRound
    );

    /*
    * @notice This function starts dividend payout round, and can be started from ANY address if the time has come.
    */
    function startDividendsPayments() public returns (bool success) {

        require(
            dividendsRound[dividendsRoundsCounter].roundIsRunning == false,
            "Already running"
        );

        // dividendsRound[dividendsRoundsCounter].roundFinishedOnUnixTime is zero for first round
        // so it can be started right after contract deploy
        require(now.sub(dividendsRound[dividendsRoundsCounter].roundFinishedOnUnixTime) > dividendsPeriod,
            "To early to start"
        );

        require(registeredShares > 0,
            "No registered shares to distribute dividends to"
        );

        uint sumWeiToPayForOneToken = address(this).balance / registeredShares;
        uint sumXEurToPayForOneToken = xEuro.balanceOf(address(this)) / registeredShares;

        require(
            sumWeiToPayForOneToken > 0 || sumXEurToPayForOneToken > 0,
            "Nothing to pay"
        );

        // here we start the next dividends payout round:
        dividendsRoundsCounter++;

        dividendsRound[dividendsRoundsCounter].roundIsRunning = true;
        dividendsRound[dividendsRoundsCounter].roundStartedOnUnixTime = now;
        dividendsRound[dividendsRoundsCounter].registeredShares = registeredShares;
        dividendsRound[dividendsRoundsCounter].allRegisteredShareholders = shareholdersCounter;

        dividendsRound[dividendsRoundsCounter].sumWeiToPayForOneToken = sumWeiToPayForOneToken;
        dividendsRound[dividendsRoundsCounter].sumXEurToPayForOneToken = sumXEurToPayForOneToken;

        emit DividendsPaymentsStarted(
            dividendsRoundsCounter,
            msg.sender,
            address(this).balance,
            xEuro.balanceOf(address(this)),
            registeredShares,
            sumWeiToPayForOneToken,
            sumXEurToPayForOneToken
        );

        return true;
    }

    /*
    * @dev Reward for tx distributing dividends was paid
    * @dividendsRoundNumber Number (Id) of dividends round.
    * @dividendsToShareholderNumber Shareholder ID, to whom payment was made by the transaction
    * @dividendsToShareholderAddress Shareholder ETH address, to whom payment was made by the transaction
    * @feePaidTo Address (ETH), who received the reward (this is the address who send tx to pay dividends to above
    * stated shareholder
    * @feeInWei Amount of the reward paid.
    * @paymentSuccesful Shows if fee payment was successful (msg.sender.send == true)
    */
    event FeeForDividendsDistributionTxPaid(
        uint indexed dividendsRoundNumber,
        uint dividendsToShareholderNumber,
        address dividendsToShareholderAddress,
        address indexed feePaidTo,
        uint feeInWei,
        bool feePaymentSuccesful
    );

    /*
    * @notice This function pays dividends due to the next shareholder.
    * dev: This functions is intended to be called by external script (bot), but can be also called manually.
    * External script can be run by any person interested in distributing dividends.
    * Script code is open source and published on smart contract's web site and/or on Github.
    * Technically this functions can be run also manually (acceptable option for small number of shareholders)
    */
    function payDividendsToNext() external returns (bool success) {

        require(
            dividendsRound[dividendsRoundsCounter].roundIsRunning,
            "Dividends payments round is not open"
        );

        dividendsRound[dividendsRoundsCounter].shareholdersCounter = dividendsRound[dividendsRoundsCounter].shareholdersCounter + 1;

        uint nextShareholderToPayDividends = dividendsRound[dividendsRoundsCounter].shareholdersCounter;

        uint sumWeiToPayForOneToken = dividendsRound[dividendsRoundsCounter].sumWeiToPayForOneToken;
        uint sumXEurToPayForOneToken = dividendsRound[dividendsRoundsCounter].sumXEurToPayForOneToken;

        address payable to = shareholdersLedgerByIdNumber[nextShareholderToPayDividends].shareholderEthereumAddress;

        if (balanceOf[to] > 0) {

            if (sumWeiToPayForOneToken > 0) {

                uint sumWeiToPay = sumWeiToPayForOneToken * balanceOf[to];

                // 'send' is the low-level counterpart of 'transfer'.
                // If the execution fails, the current contract will not stop with an exception, but 'send' will return false.
                // https://solidity.readthedocs.io/en/v0.5.10/types.html?highlight=send#members-of-addresses
                // So we use 'send' and not 'transfer' to ensure that execution continues even if sending ether fails.
                bool result = to.send(sumWeiToPay);
                emit DividendsPaymentEther(
                    result,
                    to,
                    nextShareholderToPayDividends,
                    balanceOf[to],
                    sumWeiToPay,
                    dividendsRoundsCounter
                );
            }

            if (sumXEurToPayForOneToken > 0) {
                uint sumXEuroToPay = sumXEurToPayForOneToken * balanceOf[to];
                //  if (sumXEuroToPay <= xEuro.balanceOf(address(this))) {
                bool result = xEuro.transfer(to, sumXEuroToPay);
                emit DividendsPaymentXEuro(
                    result,
                    to,
                    nextShareholderToPayDividends,
                    sumXEuroToPay,
                    nextShareholderToPayDividends,
                    dividendsRoundsCounter
                );
                //  }
            }

        }

        // if the round started shareholdersCounter can not be zero
        // because to start the round we need at least one registered share and thus at least one registered shareholder

        uint feeForTxCaller = dividendsRound[dividendsRoundsCounter].weiForTxFees / shareholdersCounter;

        if (
            feeForTxCaller > 0
            && msg.sender == tx.origin // < msg.sender is not a contract (to prevent reentrancy)
        ) {

            // we use send not transfer (returns false, not fail)
            bool feePaymentSuccessful = msg.sender.send(feeForTxCaller);
            emit FeeForDividendsDistributionTxPaid(
                dividendsRoundsCounter,
                nextShareholderToPayDividends,
                to,
                msg.sender,
                feeForTxCaller,
                feePaymentSuccessful
            );
        }

        // if this is the last registered shareholder for this round
        // then FINISH the round:
        if (nextShareholderToPayDividends == shareholdersCounter) {

            dividendsRound[dividendsRoundsCounter].roundIsRunning = false;
            dividendsRound[dividendsRoundsCounter].roundFinishedOnUnixTime = now;

            emit DividendsPaymentsFinished(
                dividendsRoundsCounter
            );
        }

        return true;
    }

    /*
    * Interested party can provide funds to pay for dividends distribution.
    * @param forDividendsRound Number (Id) of dividends payout round.
    * @param sumInWei Sum in wei received.
    * @param from Address from which sum was received.
    * @param currentSum Current sum of wei to reward accounts sending dividends distributing transactions.
    */
    event FundsToPayForDividendsDistributionReceived(
        uint indexed forDividendsRound,
        uint sumInWei,
        address indexed from,
        uint currentSum
    );

    /*
    * Function to add funds to reward transactions distributing dividends in this round/
    */
    function fundDividendsPayout() public payable returns (bool success){

        /* We allow this only for running round */
        require(
            dividendsRound[dividendsRoundsCounter].roundIsRunning,
            "Dividends payout is not running"
        );

        dividendsRound[dividendsRoundsCounter].weiForTxFees = dividendsRound[dividendsRoundsCounter].weiForTxFees + msg.value;

        emit FundsToPayForDividendsDistributionReceived(
            dividendsRoundsCounter,
            msg.value,
            msg.sender,
            dividendsRound[dividendsRoundsCounter].weiForTxFees // totalSum
        );

        return true;
    }

    /*
    * @dev startDividendsPayments() and fundDividendsPayout() combined for convenience
    */
    function startDividendsPaymentsAndFundDividendsPayout() external payable returns (bool success) {
        startDividendsPayments();
        return fundDividendsPayout();
    }

    /* ============= ERC20 functions ============ */