contract BillsOfExchange is BurnableToken {

    /* ---- Bill of Exchange requisites: */

    /**
    * Number of this contract in the ledger maintained by factory contract
    */
    uint256 public billsOfExchangeContractNumber;

    /**
    * Legal name of a person who issues the bill (drawer)
    * This can be a name of a company/organization or of a physical person
    */
    string public drawerName;

    /**
    * Ethereum address of the signer
    * His/her identity has to be verified via Cryptonomica.net smart contract
    */
    address public drawerRepresentedBy;

    /**
    * Link to information about signer's authority to represent the drawer
    * This should be a proof that signer can represent drawer
    * It can be link to public register like Companies House in U.K., or other proof.
    * Whosoever puts his signature on a bill of exchange as representing a person for whom he had no power to act is
    * bound himself as a party to the bill. The same rule applies to a representative who has exceeded his powers.
    */
    string public linkToSignersAuthorityToRepresentTheDrawer;

    /**
    * The name of the person who is to pay (drawee)
    */
    string public drawee;

    /**
    * Ethereum address of a person who can represent the drawee
    * This address should be verified via Cryptonomica.net smart contract
    */
    address public draweeSignerAddress;

    /**
    * This should be a proof that signer can represent drawee.
    */
    string  public linkToSignersAuthorityToRepresentTheDrawee;

    /*
    * Legal conditions to be included
    */
    string public description;
    string public order;
    string public disputeResolutionAgreement;
    CryptonomicaVerification public cryptonomicaVerification;

    /*
    *  a statement of the time of payment
    *  we use string to make possible variants like: '01 Jan 2021', 'at sight', 'at sight but not before 2019-12-31'
    *  '10 days after sight' etc.,
    * see https://www.jus.uio.no/lm/bills.of.exchange.and.promissory.notes.convention.1930/doc.html#109
    */
    string public timeOfPayment;

    // A statement of the date and of the place where the bill is issued
    uint256 public issuedOnUnixTime;
    string public placeWhereTheBillIsIssued; //  i.e. "London, U.K.";

    // a statement of the place where payment is to be made;
    // usually it is an address of the payer
    string public placeWherePaymentIsToBeMade;

    // https://en.wikipedia.org/wiki/ISO_4217
    // or crypto currency
    string public currency; // for example: "EUR", "USD"

    uint256 public sumToBePaidForEveryToken; //

    /*
    * number of signatures under disputeResolution agreement
    */
    uint256 public disputeResolutionAgreementSignaturesCounter;

    /*
    * @param signatoryAddress Ethereum address of the person, that signed the agreement
    * @param signatoryName Legal name of the person that signed agreement. This can be a name of a legal or physical
    * person
    */
    struct Signature {
        address signatoryAddress;
        string signatoryName;
    }

    mapping(uint256 => Signature) public disputeResolutionAgreementSignatures;

    /*
    * Event to be emitted when disputeResolution agreement was signed by new person
    * @param signatureNumber Number of the signature (see 'disputeResolutionAgreementSignaturesCounter')
    * @param singedBy Name of the person who signed disputeResolution agreement
    * @param representedBy Ethereum address of the person who signed disputeResolution agreement
    * @param signedOn Timestamp (Unix time)
    */
    event disputeResolutionAgreementSigned(
        uint256 indexed signatureNumber,
        string signedBy,
        address indexed representedBy,
        uint256 signedOn
    );

    /*
       * @param _signatoryAddress Ethereum address of the person who signs agreement
       * @param _signatoryName Name of the person that signs dispute resolution agreement
       */
    function signDisputeResolutionAgreementFor(
        address _signatoryAddress,
        string memory _signatoryName
    ) public returns (bool success){

        require(
            msg.sender == _signatoryAddress ||
            msg.sender == creator,
            "Not authorized to sign dispute resolution agreement"
        );

        // ! signer should have valid identity verification in cryptonomica.net smart contract:

        require(
            cryptonomicaVerification.keyCertificateValidUntil(_signatoryAddress) > now,
            "Signer has to be verified on Cryptonomica.net"
        );

        // revokedOn returns uint256 (unix time), it's 0 if verification is not revoked
        require(
            cryptonomicaVerification.revokedOn(_signatoryAddress) == 0,
            "Verification for this address was revoked, can not sign"
        );

        disputeResolutionAgreementSignaturesCounter++;

        disputeResolutionAgreementSignatures[disputeResolutionAgreementSignaturesCounter].signatoryAddress = _signatoryAddress;
        disputeResolutionAgreementSignatures[disputeResolutionAgreementSignaturesCounter].signatoryName = _signatoryName;

        emit disputeResolutionAgreementSigned(disputeResolutionAgreementSignaturesCounter, _signatoryName, msg.sender, now);

        return true;
    }

    function signDisputeResolutionAgreement(string calldata _signatoryName) external returns (bool success){
        return signDisputeResolutionAgreementFor(msg.sender, _signatoryName);
    }

    /**
    * set up new bunch of bills of exchange and sign dispute resolution agreement
    *
    * @param _billsOfExchangeContractNumber A number of this contract in the ledger ('billsOfExchangeContractsCounter' from BillsOfExchangeFactory)
    * @param _currency Currency of the payment, for example: "EUR", "USD"
    * @param _sumToBePaidForEveryToken The amount in the above currency, that have to be paid for every token (bill of exchange)
    * @param _drawerName The person who issues the bill (drawer)
    * @param _drawerRepresentedBy The Ethereum address of the signer
    * @param _linkToSignersAuthorityToRepresentTheDrawer Link to information about signers authority to represent the drawer
    * @param  _drawee The name of the person who is to pay (can be the same as drawer)
    */
    function initBillsOfExchange(
        uint256 _billsOfExchangeContractNumber,
        string calldata _currency,
        uint256 _sumToBePaidForEveryToken,
        string calldata _drawerName,
        address _drawerRepresentedBy,
        string calldata _linkToSignersAuthorityToRepresentTheDrawer,
        string calldata _drawee,
        address _draweeSignerAddress
    ) external {

        require(msg.sender == creator, "Only contract creator can call 'initBillsOfExchange' function");

        billsOfExchangeContractNumber = _billsOfExchangeContractNumber;

        // https://en.wikipedia.org/wiki/ISO_4217
        // or crypto currency
        currency = _currency;

        sumToBePaidForEveryToken = _sumToBePaidForEveryToken;

        // person who issues the bill (drawer)
        drawerName = _drawerName;
        drawerRepresentedBy = _drawerRepresentedBy;
        linkToSignersAuthorityToRepresentTheDrawer = _linkToSignersAuthorityToRepresentTheDrawer;

        // order to
        // (the name of the person who is to pay)
        drawee = _drawee;
        draweeSignerAddress = _draweeSignerAddress;

    }

    /**
    * Set places and time
    * not included in 'init' because of exception: 'Stack too deep, try using fewer variables.'
    * @param _timeOfPayment The time when payment has to be made
    * @param  _placeWhereTheBillIsIssued Place where the bills were issued. Usually it's the address of the drawer.
    * @param _placeWherePaymentIsToBeMade Place where the payment has to be made. Usually it's the address of the drawee.
    */
    function setPlacesAndTime(
        string calldata _timeOfPayment,
        string calldata _placeWhereTheBillIsIssued,
        string calldata _placeWherePaymentIsToBeMade
    ) external {

        require(msg.sender == creator, "Only contract creator can call 'setPlacesAndTime' function");

        // require(issuedOnUnixTime == 0, "setPlacesAndTime can be called one time only");
        // (this can be ensured in factory contract)

        issuedOnUnixTime = now;
        timeOfPayment = _timeOfPayment;

        placeWhereTheBillIsIssued = _placeWhereTheBillIsIssued;
        placeWherePaymentIsToBeMade = _placeWherePaymentIsToBeMade;

    }

    /*
    * @param _description Legal description of bills of exchange created.
    * @param _order Order to pay (text or the order)
    * @param _disputeResolutionAgreement Agreement about dispute resolution (text)
    * this function should be called only once - when initializing smart contract
    */
    function setLegal(
        string calldata _description,
        string calldata _order,
        string calldata _disputeResolutionAgreement,
        address _cryptonomicaVerificationAddress

    ) external {

        require(msg.sender == creator, "Only contract creator can call 'setLegal' function");

        // require(address(cryptonomicaVerification) == address(0), "setLegal can be called one time only");
        // (this can be ensured in factory contract)

        description = _description;
        order = _order;
        disputeResolutionAgreement = _disputeResolutionAgreement;
        cryptonomicaVerification = CryptonomicaVerification(_cryptonomicaVerificationAddress);

    }

    uint256 public acceptedOnUnixTime;

    /**
    * Drawee can accept only all bills in the smart contract, or not accept at all
    * @param acceptedOnUnixTime Time when drawee accepted bills
    * @param drawee The name of the drawee
    * @param draweeRepresentedBy The Ethereum address of the drawee's representative
    * (or drawee himself if he is a physical person)
    */
    event Acceptance(
        uint256 acceptedOnUnixTime,
        string drawee,
        address draweeRepresentedBy
    );

    /**
    * function for drawee to accept bill of exchange
    * see:
    * http://www.legislation.gov.uk/ukpga/Vict/45-46/61/section/17
    * https://www.jus.uio.no/lm/bills.of.exchange.and.promissory.notes.convention.1930/doc.html#69
    *
    * @param _linkToSignersAuthorityToRepresentTheDrawee Link to information about signer's authority to represent the drawee
    */
    function accept(string calldata _linkToSignersAuthorityToRepresentTheDrawee) external returns (bool success) {

        /*
        * this should be called only by address, previously indicated as drawee's address by the drawer
        * or by BillsOfExchangeFactory address via 'createAndAcceptBillsOfExchange' function
        */
        require(
            msg.sender == draweeSignerAddress ||
            msg.sender == creator,
            "Not authorized to accept"
        );

        signDisputeResolutionAgreementFor(draweeSignerAddress, drawee);

        linkToSignersAuthorityToRepresentTheDrawee = _linkToSignersAuthorityToRepresentTheDrawee;

        acceptedOnUnixTime = now;

        emit Acceptance(acceptedOnUnixTime, drawee, msg.sender);

        return true;
    }

}
/**
 * This contract represents a bunch of bills of exchange issued by one person at the same time and on the same conditions
 */
function signDisputeResolutionAgreementFor(
    address _signatoryAddress,
    string memory _signatoryName
) public returns (bool success){

    require(
        msg.sender == _signatoryAddress ||
        msg.sender == creator,
        "Not authorized to sign dispute resolution agreement"
    );

    // ! signer should have valid identity verification in cryptonomica.net smart contract:

    require(
        cryptonomicaVerification.keyCertificateValidUntil(_signatoryAddress) > now,
        "Signer has to be verified on Cryptonomica.net"
    );

    // revokedOn returns uint256 (unix time), it's 0 if verification is not revoked
    require(
        cryptonomicaVerification.revokedOn(_signatoryAddress) == 0,
        "Verification for this address was revoked, can not sign"
    );

    disputeResolutionAgreementSignaturesCounter++;

    disputeResolutionAgreementSignatures[disputeResolutionAgreementSignaturesCounter].signatoryAddress = _signatoryAddress;
    disputeResolutionAgreementSignatures[disputeResolutionAgreementSignaturesCounter].signatoryName = _signatoryName;

    emit disputeResolutionAgreementSigned(disputeResolutionAgreementSignaturesCounter, _signatoryName, msg.sender, now);

    return true;
}

/*
 * @param _signatoryAddress Ethereum address of the person who signs agreement
 * @param _signatoryName Name of the person that signs dispute resolution agreement
 */