contract CryptoSharesFactory is ManagedContractWithPaidService {

    /**
    * Arbitration clause (dispute resolution agreement) text
    * see: https://en.wikipedia.org/wiki/Arbitration_clause
    * https://en.wikipedia.org/wiki/Arbitration#Arbitration_agreement
    */
    string public disputeResolutionAgreement =
    "Any dispute, controversy or claim arising out of or relating to this smart contract, including transfer of shares/tokens managed by this smart contract or ownership of the shares/tokens, or any voting managed by this smart contract shall be settled by arbitration in accordance with the Cryptonomica Arbitration Rules (https://github.com/Cryptonomica/arbitration-rules) in the version in effect at the time of the filing of the claim. In the case of the Ethereum blockchain fork, the blockchain that has the highest hashrate is considered valid, and all the others are not considered a valid registry, in case of dispute, dispute should be resolved by arbitration court. All Ethereum test networks are not valid registries.";

    event DisputeResolutionAgreementTextChanged(
        string newText,
        address indexed changedBy
    );

    /*
    * @param _newText New text for arbitration agreement. This will be used for future shares contracts only and
    * will not change arbitration agreement text in already deployed shares contracts.
    */
    function changeDisputeResolutionAgreement(string calldata _newText) external onlyAdmin returns (bool success){

        disputeResolutionAgreement = _newText;

        emit DisputeResolutionAgreementTextChanged(_newText, msg.sender);

        return true;
    }

    /* Address of the smart contract with xEUR tokens (see: https://xeuro.online) */
    address public xEurContractAddress;

    /**
    * @param from Old address
    * @param to New address
    * @param by Who made a change
    */
    event XEuroContractAddressChanged(
        address indexed from,
        address indexed to,
        address indexed by
    );

    /**
    * @param _newAddress Address of new contract for xEUR to be used (for the case that this address changes)
    * This function makes change only for future shares contracts, and does not change shares contract already deployed.
    */
    function changeXEuroContractAddress(address _newAddress) public onlyAdmin returns (bool success) {

        emit XEuroContractAddressChanged(xEurContractAddress, _newAddress, msg.sender);

        xEurContractAddress = _newAddress;

        return true;
    }

    /* ---- Constructor ---- */

    constructor() public {

        isAdmin[msg.sender] = true;

        changePrice(0.2 ether);

        setWithdrawalAddress(msg.sender);

        // Ropsten: > mock up contract, verification always valid for any address
        changeCryptonomicaVerificationContractAddress(0xE48BC3dB5b512d4A3e3Cd388bE541Be7202285B5);
        // TODO: change in production to https://etherscan.io/address/0x846942953c3b2A898F10DF1e32763A823bf6b27f <<<<<<<
        // changeCryptonomicaVerificationContractAddress(0x846942953c3b2A898F10DF1e32763A823bf6b27f);

        // Ropsten: > mock up contract, allows to create tokens for every address
        changeXEuroContractAddress(0x9a2A6C32352d85c9fcC5ff0f91fCB9CE42c15030);
        // TODO: change in production to https://etherscan.io/address/0xe577e0B200d00eBdecbFc1cd3F7E8E04C70476BE <<<<<<<
        // changeXEuroContractAddress(0xe577e0B200d00eBdecbFc1cd3F7E8E04C70476BE)

    } // end of constructor()

    /* ---- Creating and managing CryptoShares smart contracts ---- */

    // counts deployed contracts
    uint public cryptoSharesContractsCounter;

    /*
    * This struct contains information about deployed contract;
    */
    struct CryptoSharesContract {
        uint contractId;
        address contractAddress;
        uint deployedOnUnixTime;
        string name; // the same as token name
        string symbol; // the same as token symbol
        uint totalSupply; // the same as token totalSupply
        uint dividendsPeriod; // period between dividends payout rounds, in seconds
    }

    event NewCryptoSharesContractCreated(
        uint indexed contractId,
        address indexed contractAddress,
        string name, // the same as token name
        string symbol, // the same as token symbol
        uint totalSupply, // the same as token totalSupply
        uint dividendsPeriod // period between dividends payout rounds, in seconds
    );

    mapping(uint => CryptoSharesContract) public cryptoSharesContractsLedger;

    /*
    * @dev This function creates new shares contracts.
    * @param _description Description of the project or organization (can be short text or link to web page)
    * @param _name Name of the token, as required by ERC20.
    * @param _symbol Symbol for the token, as required by ERC20.
    * @param _totalSupply Total supply, as required by ERC20.
    * @param _dividendsPeriodInSeconds Period between dividends payout rounds, in seconds.
    */
    function createCryptoSharesContract(
        string calldata _description,
        string calldata _name,
        string calldata _symbol,
        uint _totalSupply,
        uint _dividendsPeriodInSeconds
    ) external payable returns (bool success){

        require(
            msg.value >= price,
            "msg.value is less than price"
        );

        CryptoShares cryptoSharesContract = new CryptoShares();

        cryptoSharesContractsCounter++;

        cryptoSharesContract.initToken(
            cryptoSharesContractsCounter,
            _description,
            _name,
            _symbol,
            _dividendsPeriodInSeconds,
            xEurContractAddress,
            address(cryptonomicaVerification),
            disputeResolutionAgreement
        );

        cryptoSharesContract.issueTokens(
            _totalSupply,
            msg.sender
        );

        cryptoSharesContractsCounter;
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractId = cryptoSharesContractsCounter;
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractAddress = address(cryptoSharesContract);
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].deployedOnUnixTime = now;
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].name = cryptoSharesContract.name();
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].symbol = cryptoSharesContract.symbol();
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].totalSupply = cryptoSharesContract.totalSupply();
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].dividendsPeriod = cryptoSharesContract.dividendsPeriod();

        emit NewCryptoSharesContractCreated(
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractId,
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractAddress,
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].name,
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].symbol,
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].totalSupply,
            cryptoSharesContractsLedger[cryptoSharesContractsCounter].dividendsPeriod
        );

        return true;

    } // end of function createCryptoSharesContract

}
/*
 * dev: Smart contract to deploy shares smart contracts and maintain a ledger of deployed contracts.
 */
function createCryptoSharesContract(
    string calldata _description,
    string calldata _name,
    string calldata _symbol,
    uint _totalSupply,
    uint _dividendsPeriodInSeconds
) external payable returns (bool success){

    require(
        msg.value >= price,
        "msg.value is less than price"
    );

    CryptoShares cryptoSharesContract = new CryptoShares();

    cryptoSharesContractsCounter++;

    cryptoSharesContract.initToken(
        cryptoSharesContractsCounter,
        _description,
        _name,
        _symbol,
        _dividendsPeriodInSeconds,
        xEurContractAddress,
        address(cryptonomicaVerification),
        disputeResolutionAgreement
    );

    cryptoSharesContract.issueTokens(
        _totalSupply,
        msg.sender
    );

    cryptoSharesContractsCounter;
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractId = cryptoSharesContractsCounter;
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractAddress = address(cryptoSharesContract);
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].deployedOnUnixTime = now;
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].name = cryptoSharesContract.name();
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].symbol = cryptoSharesContract.symbol();
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].totalSupply = cryptoSharesContract.totalSupply();
    cryptoSharesContractsLedger[cryptoSharesContractsCounter].dividendsPeriod = cryptoSharesContract.dividendsPeriod();

    emit NewCryptoSharesContractCreated(
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractId,
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].contractAddress,
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].name,
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].symbol,
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].totalSupply,
        cryptoSharesContractsLedger[cryptoSharesContractsCounter].dividendsPeriod
    );

    return true;

} // end of function createCryptoSharesContract

/*
 * @dev This function creates new shares contracts.
 * @param _description Description of the project or organization (can be short text or link to web page)
 * @param _name Name of the token, as required by ERC20.
 * @param _symbol Symbol for the token, as required by ERC20.
 * @param _totalSupply Total supply, as required by ERC20.
 * @param _dividendsPeriodInSeconds Period between dividends payout rounds, in seconds.
 */