contract BytetherOV is usingOraclize, BasicUtility, BasicAccessControl, BytetherOVI{
    enum ResultCode { 
        SUCCESS,
        ERROR_EXIST,
        ERROR_NOT_EXIST,
        ERROR_PARAM,
        ERROR_NOT_ENOUGH_BALANCE,
        ERROR_INVALID_QUERY,
        ERROR_INVALID_SIGNATURE
    }

    struct Ownership {
        string btcAddress;
        address myEther;
        bytes32 referCode;
    }
    
    uint public total = 0;
    bool public maintaining = false;
    string public verifyUrl = "";
    
    // verify mapping
    mapping(bytes32=>bytes32) verifiedQueries; // btcAddressHash -> query_id
    mapping(bytes32=>Ownership) queries; // query_id -> ownership info
    
    modifier isActive {
        require(maintaining != true || msg.sender == owner);
        _;
    }

    function BytetherOV(string _verifyUrl) public {
        verifyUrl = _verifyUrl;
        owner = msg.sender;
    }

    function () payable public {}

    // event
    event LogCreateTrigger(bytes32 indexed btcAddress, bytes32 indexed queryId, ResultCode result);
    event LogCreate(bytes32 indexed queryId, uint code, ResultCode result);
    
    // moderators function
    function ToggleMaintenance() onlyModerators public {
        maintaining = !maintaining;
    }
    
    function setVerifyUrl(string _verifyUrl) onlyModerators public {
        verifyUrl = _verifyUrl;
    }
    
    function UnclockVerification(string _btcAddress) onlyModerators public {
        bytes32 btcAddressHash = keccak256(_btcAddress);
        if (verifiedQueries[btcAddressHash] != 0x0) {
            verifiedQueries[btcAddressHash] = 0x0;
            total -= 1;
        }
    }
    
    // public function
    function GetOwnership(string _btcAddress) constant public returns(address, bytes32) {
        bytes32 btcAddressHash = keccak256(_btcAddress);
        bytes32 queryId = verifiedQueries[btcAddressHash];
        var info = queries[queryId];
        return (info.myEther, info.referCode);
    }
    
    function GetQueryInfo(bytes32 queryId) constant public returns(string, address, bytes32) {
        var info = queries[queryId];
        return (info.btcAddress, info.myEther, info.referCode);
    }

    function __callback(bytes32 _queryId, string _result) public {
        if (msg.sender != oraclize_cbAddress()) {
            LogCreate(_queryId, 0, ResultCode.ERROR_INVALID_QUERY);
            return;
        }
        
        var info = queries[_queryId];
        if (info.myEther == 0) {
            LogCreate(_queryId, 0, ResultCode.ERROR_INVALID_QUERY);
            return;
        }
        
        uint code = stringToUint(_result);
        if (code != 1) {
            LogCreate(_queryId, code, ResultCode.ERROR_INVALID_SIGNATURE);
            return;
        }
        
         // check whether this address has been verified
        bytes32 btcAddressHash = keccak256(info.btcAddress);
        if (verifiedQueries[btcAddressHash] != 0) {
            LogCreate(_queryId, code, ResultCode.ERROR_EXIST);
            return;
        }
        
        total += 1;
        verifiedQueries[btcAddressHash] = _queryId;
        LogCreate(_queryId, code, ResultCode.SUCCESS);
        return; 
    }
    
    function AddOwnership(string _btcAddress, string _signature, string _referCode) isActive public returns(ResultCode) {
        if (!checkValidBitcoinAddress(_btcAddress)) {
            LogCreateTrigger(0, 0, ResultCode.ERROR_PARAM);
            return ResultCode.ERROR_PARAM;            
        }
        
        if (!checkValidBase64(_signature)) {
            LogCreateTrigger(0, 0, ResultCode.ERROR_PARAM);
            return ResultCode.ERROR_PARAM;
        }
        
        bytes32 btcAddressHash = keccak256(_btcAddress);
        // check whether this address has been verified
        if (verifiedQueries[btcAddressHash] != 0) {
            LogCreateTrigger(btcAddressHash, 0, ResultCode.ERROR_EXIST);
            return ResultCode.ERROR_EXIST; 
        }

        if (oraclize_getPrice("URL") > this.balance) {
            LogCreateTrigger(btcAddressHash, 0, ResultCode.ERROR_NOT_ENOUGH_BALANCE);
            return ResultCode.ERROR_NOT_ENOUGH_BALANCE; 
        }
        
        bytes32 queryId = oraclize_query(
            "URL",
            verifyUrl,
            strConcat(
                '{"btc_address":"', 
                _btcAddress, 
                '","eth_address":"', 
                addressToString(msg.sender), 
                '","signature":"', 
                _signature, 
                '"}')
        );
        
        var info = queries[queryId];
        info.btcAddress = _btcAddress;
        info.myEther = msg.sender;
        info.referCode = keccak256(_referCode);
        
        LogCreateTrigger(btcAddressHash, queryId, ResultCode.SUCCESS);
        return ResultCode.SUCCESS;
    }
}
function ToggleMaintenance() onlyModerators public {
    maintaining = !maintaining;
}

// moderators function