// File: lib/solmate/src/auth/Owned.sol



// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

// File: lib/telepathy-contracts/src/amb/interfaces/ITelepathy.sol



pragma solidity ^0.8.0;

enum MessageStatus {
    NOT_EXECUTED,
    EXECUTION_FAILED,
    EXECUTION_SUCCEEDED
}

struct Message {
    uint8 version;
    uint64 nonce;
    uint32 sourceChainId;
    address sourceAddress;
    uint32 destinationChainId;
    bytes32 destinationAddress;
    bytes data;
}

interface ITelepathyRouter {
    event SentMessage(uint64 indexed nonce, bytes32 indexed msgHash, bytes message);

    function send(uint32 destinationChainId, bytes32 destinationAddress, bytes calldata data)
        external
        returns (bytes32);

    function send(uint32 destinationChainId, address destinationAddress, bytes calldata data)
        external
        returns (bytes32);

    function sendViaStorage(
        uint32 destinationChainId,
        bytes32 destinationAddress,
        bytes calldata data
    ) external returns (bytes32);

    function sendViaStorage(
        uint32 destinationChainId,
        address destinationAddress,
        bytes calldata data
    ) external returns (bytes32);
}

interface ITelepathyReceiver {
    event ExecutedMessage(
        uint32 indexed sourceChainId,
        uint64 indexed nonce,
        bytes32 indexed msgHash,
        bytes message,
        bool status
    );

    function executeMessage(
        uint64 slot,
        bytes calldata message,
        bytes[] calldata accountProof,
        bytes[] calldata storageProof
    ) external;

    function executeMessageFromLog(
        bytes calldata srcSlotTxSlotPack,
        bytes calldata messageBytes,
        bytes32[] calldata receiptsRootProof,
        bytes32 receiptsRoot,
        bytes[] calldata receiptProof, // receipt proof against receipt root
        bytes memory txIndexRLPEncoded,
        uint256 logIndex
    ) external;
}

interface ITelepathyHandler {
    function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data)
        external
        returns (bytes4);
}

// File: src/PingPong.sol



// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {ITelepathyRouter, Message, ITelepathyHandler} from "telepathy-contracts/src/amb/interfaces/ITelepathy.sol";
import {Owned} from "solmate/auth/Owned.sol";

contract PingPong is ITelepathyHandler, Owned {
    bool public running;

    ITelepathyRouter public immutable telepathyRouter;

    uint256 public pingCounter;

    event PingSent(uint32 destinationChainId, address destinationAddress, uint256 pingCounter);
    event PingReceived(uint32 sourceChainId, address sourceAddress, uint256 pingCounter);

    constructor(address _telepathyRouter) Owned(msg.sender) {
        telepathyRouter = ITelepathyRouter(_telepathyRouter);
    }

    function run(bool shouldRun) public onlyOwner {
        running = shouldRun;
    }

    function ping(uint32 destinationChainId, address destinationAddress) public {
        if (!running) {
            return;
        }

        pingCounter++;
        bytes memory _data = abi.encode(pingCounter);
        telepathyRouter.send(destinationChainId, destinationAddress, _data);
        emit PingSent(destinationChainId, destinationAddress, pingCounter);
    }

    function handleTelepathy(uint32 _sourceChainId, address _sourceAddress, bytes memory _data)
        public
        returns (bytes4)
    {
        (uint256 count) = abi.decode(_data, (uint256));
        emit PingReceived(_sourceChainId, _sourceAddress, count);
        this.ping(_sourceChainId, _sourceAddress);
        return ITelepathyHandler.handleTelepathy.selector;
    }
}