// SPDX-License-Identifier: MIT

pragma solidity >=0.4.16 <0.9.0;

contract EthereumContract {
    enum Status {
        Blank,
        Initiated,
        Locked, // ETH was locked by buyer
        Set,    //intitiator address set by buyer
        Refunded, // ETH was refunded to buyer
        Claimed // ETH was sucessfully claimed by order initiator
    }

    struct respondOrder {
        address initiatorEthAddress;
        address buyerOntAddress;
        address buyerEthAddress;
        bytes32 hashlock;
        uint mxbid;
        uint refundTimelock;
        uint bidTimelock;
        string secret;
        Status status;
    }

    mapping (bytes32 => respondOrder) public orderList; // hashlock => order data
    uint timelockDuration = 600; // set to 600 seconds for testing purposes
    uint bidtime = 300; //set to 300 seconds for testing

    //Can we do it without this function
    function initiateBidding(bytes32 hashlock) public {
        if((orderList[hashlock].bidTimelock!=0) && (block.timestamp>orderList[hashlock].bidTimelock) && (block.timestamp-orderList[hashlock].bidTimelock>900)){
            clear(hashlock);
        }
        require(orderList[hashlock].status == Status.Blank, "Still in progress");
        orderList[hashlock].bidTimelock = block.timestamp + bidtime;
        orderList[hashlock].status = Status.Initiated;
        orderList[hashlock].refundTimelock = block.timestamp + timelockDuration;
    }

    function bid(bytes32 hashlock, address buyerOntAddress) public payable {
        require(orderList[hashlock].status == Status.Initiated || orderList[hashlock].status==Status.Locked, "Transaction in Invalid State");
        require(block.timestamp<=orderList[hashlock].bidTimelock, "Bidding Period Ended");
        require(msg.value>orderList[hashlock].mxbid,"Bidding price should be greater than the highest bid");
        // If no one bid for the exchange control won't come here
        if(orderList[hashlock].status == Status.Locked){
            payable(orderList[hashlock].buyerEthAddress).transfer(orderList[hashlock].mxbid);
        }
        orderList[hashlock].mxbid = msg.value;
        orderList[hashlock].buyerEthAddress = msg.sender;   
        orderList[hashlock].buyerOntAddress = buyerOntAddress;     
        orderList[hashlock].status = Status.Locked;
    }

    
    //setting initiator address
    function setInitiatorAddr(bytes32 hashlock, address initiatorEthAddress) public{
        require(orderList[hashlock].buyerEthAddress==msg.sender, "Access Denied"); //Only max bidder can enter
        require(orderList[hashlock].refundTimelock>block.timestamp, "Time Ended");
        require(orderList[hashlock].status==Status.Locked, "Wrong state");
        orderList[hashlock].initiatorEthAddress = initiatorEthAddress;
        orderList[hashlock].status = Status.Set;
    }

    function claimEth (bytes32 hashlock, string memory secret) public {
        require(orderList[hashlock].initiatorEthAddress == msg.sender, "Can only be perfomed by initiator");
        require(orderList[hashlock].status == Status.Set, "Order status should be Set");
        require(orderList[hashlock].refundTimelock>block.timestamp, "Claim Time Finished");
        require(sha256(abi.encodePacked(secret)) == hashlock, "Secret does not match the hashlock");
        
        orderList[hashlock].secret = secret;
        orderList[hashlock].status = Status.Claimed;
        payable(msg.sender).transfer(orderList[hashlock].mxbid);

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

    function refundEth (bytes32 hashlock) public {
        require(orderList[hashlock].buyerEthAddress == msg.sender, "Can only be perfomed by order initiator");
        require(block.timestamp >= orderList[hashlock].refundTimelock, "Timelock is not over");
        require(orderList[hashlock].status == Status.Set || orderList[hashlock].status==Status.Locked, "Order should be in Set state for refund");

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

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

    function clear(bytes32 hashlock) private {
        orderList[hashlock] = respondOrder({
            buyerOntAddress: address(0),
            buyerEthAddress: address(0),
            initiatorEthAddress: address(0),
            hashlock: bytes32(0),
            mxbid: 0,
            refundTimelock: 0,
            bidTimelock: 0,
            secret: "",
            status: Status.Blank
        });
    }
}
