// SPDX-License-Identifier: MIT

pragma solidity >=0.4.16 <0.9.0;

contract OntologyContract {
    enum Status {
        Blank,
        Locked, // ONT tokens were locked by initiator
        Set,
        Refunded, // ONT was refunded to buyer
        Claimed // ONT was sucessfully claimed by order initiators
    }

    struct initiatedOrder {
        address initiatorOntAddress;
        address initiatorEthAddress;
        address buyerOntAddress;
        address buyerEthAddress;
        bytes32 hashlock;
        uint amountOnt;
        uint refundTimelock;
        uint initiatedTime;
        string secret;
        Status status;
    }

    mapping (bytes32 => initiatedOrder) public orderList; // hashlock => order data
    uint timelockDuration = 900; // set to 900 seconds for testing purposes
    
    function initiateOrder (bytes32 hashlock, address initiatorEthAddress) public payable {
        //For clearing an intermediate left txn
        if((orderList[hashlock].status!=Status.Blank) && ((block.timestamp-orderList[hashlock].initiatedTime)>1200)){
            clear(hashlock);
        }
        require(orderList[hashlock].status==Status.Blank, "Already in Use");
        orderList[hashlock].initiatorOntAddress = msg.sender;
        orderList[hashlock].initiatorEthAddress = initiatorEthAddress;
        orderList[hashlock].refundTimelock = block.timestamp + timelockDuration;
        orderList[hashlock].status = Status.Locked;
        orderList[hashlock].amountOnt = msg.value;
    }
    //ALICE HAS TO CHECK WHETHER BIDDING HAS FINISHED
    function setBuyerAddr(bytes32 hashlock, address buyerOntAddress) public {
        require(orderList[hashlock].initiatorOntAddress == msg.sender, "Only initiator can set buyer address");
        require(orderList[hashlock].status == Status.Locked, "Transaction in Invalid State");
        require(orderList[hashlock].refundTimelock>block.timestamp, "Time Ended");
        // Only initiator can reach here
        orderList[hashlock].buyerOntAddress = buyerOntAddress;
        orderList[hashlock].status = Status.Set;
    }

    function claimOnt (bytes32 hashlock, string memory secret) public {
        require(orderList[hashlock].buyerOntAddress == msg.sender, "Can only be perfomed by order buyer");
        require(orderList[hashlock].status == Status.Set, "Order status should be Locked");
        require(orderList[hashlock].refundTimelock>block.timestamp, "Claim time finished");
        require(sha256(abi.encodePacked(secret)) == hashlock, "Secret does not match the hashlock");
        
        orderList[hashlock].status = Status.Claimed;
        payable(msg.sender).transfer(orderList[hashlock].amountOnt);

        //Transaction Finished. Make the hash ready for probable other txn
        clear(hashlock);
    }

    function refundOnt (bytes32 hashlock) public {
        require(orderList[hashlock].initiatorOntAddress == msg.sender, "Can only be perfomed by order initiator");
        require(block.timestamp >= orderList[hashlock].refundTimelock, "Timelock is not over");

        orderList[hashlock].status = Status.Refunded;
        payable(msg.sender).transfer(orderList[hashlock].amountOnt);

        //Transaction Finished. Make the hash ready for probable other txn
        clear(hashlock);
    }
    
    function clear(bytes32 hashlock) private {
        orderList[hashlock] = initiatedOrder({
            buyerOntAddress: address(0),
            buyerEthAddress: address(0),
            initiatorOntAddress: address(0),
            initiatorEthAddress: address(0),
            hashlock: bytes32(0),
            amountOnt: 0,
            refundTimelock: 0,
            initiatedTime: 0,
            secret: "",
            status: Status.Blank
        });
    }
}
