pragma solidity ^0.4.0;
contract OraclizeI {
    address public cbAddress;
    function query(uint _timestamp, string _datasource, string _arg) payable returns (bytes32 _id);
    function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) payable returns (bytes32 _id);
    function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) payable returns (bytes32 _id);
    function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) payable returns (bytes32 _id);
    function getPrice(string _datasource) returns (uint _dsprice);
    function getPrice(string _datasource, uint gaslimit) returns (uint _dsprice);
    function useCoupon(string _coupon);
    function setProofType(byte _proofType);
    function setCustomGasPrice(uint _gasPrice);
}
contract OraclizeAddrResolverI {
    function getAddress() returns (address _addr);
}
contract usingOraclize {
    uint constant day = 60*60*24;
    uint constant week = 60*60*24*7;
    uint constant month = 60*60*24*30;
    byte constant proofType_NONE = 0x00;
    byte constant proofType_TLSNotary = 0x10;
    byte constant proofStorage_IPFS = 0x01;
    uint8 constant networkID_auto = 0;
    uint8 constant networkID_mainnet = 1;
    uint8 constant networkID_testnet = 2;
    uint8 constant networkID_morden = 2;
    uint8 constant networkID_consensys = 161;

    OraclizeAddrResolverI OAR;

    OraclizeI oraclize;
    modifier oraclizeAPI {
        if(address(OAR)==0) oraclize_setNetwork(networkID_auto);
        oraclize = OraclizeI(OAR.getAddress());
        _;
    }
    modifier coupon(string code){
        oraclize = OraclizeI(OAR.getAddress());
        oraclize.useCoupon(code);
        _;
    }

    function oraclize_setNetwork(uint8 networkID) internal returns(bool){
        if (getCodeSize(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed)>0){ //mainnet
            OAR = OraclizeAddrResolverI(0x1d3B2638a7cC9f2CB3D298A3DA7a90B67E5506ed);
            return true;
        }
        if (getCodeSize(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1)>0){ //ropsten testnet
            OAR = OraclizeAddrResolverI(0xc03A2615D5efaf5F49F60B7BB6583eaec212fdf1);
            return true;
        }
        if (getCodeSize(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e)>0){ //kovan testnet
            OAR = OraclizeAddrResolverI(0xB7A07BcF2Ba2f2703b24C0691b5278999C59AC7e);
            return true;
        }
        if (getCodeSize(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48)>0){ //rinkeby testnet
            OAR = OraclizeAddrResolverI(0x146500cfd35B22E4A392Fe0aDc06De1a1368Ed48);
            return true;
        }
        if (getCodeSize(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475)>0){ //ethereum-bridge
            OAR = OraclizeAddrResolverI(0x6f485C8BF6fc43eA212E93BBF8ce046C7f1cb475);
            return true;
        }
        if (getCodeSize(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA)>0){ //browser-solidity
            OAR = OraclizeAddrResolverI(0x51efaF4c8B3C9AfBD5aB9F4bbC82784Ab6ef8fAA);
            return true;
        }
        return false;
    }

    function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query.value(price)(0, datasource, arg);
    }
    function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query.value(price)(timestamp, datasource, arg);
    }
    function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query_withGasLimit.value(price)(timestamp, datasource, arg, gaslimit);
    }
    function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query_withGasLimit.value(price)(0, datasource, arg, gaslimit);
    }
    function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query2.value(price)(0, datasource, arg1, arg2);
    }
    function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource);
        if (price > 1 ether + tx.gasprice*200000) return 0; // unexpectedly high price
        return oraclize.query2.value(price)(timestamp, datasource, arg1, arg2);
    }
    function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query2_withGasLimit.value(price)(timestamp, datasource, arg1, arg2, gaslimit);
    }
    function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
        uint price = oraclize.getPrice(datasource, gaslimit);
        if (price > 1 ether + tx.gasprice*gaslimit) return 0; // unexpectedly high price
        return oraclize.query2_withGasLimit.value(price)(0, datasource, arg1, arg2, gaslimit);
    }
    function oraclize_cbAddress() oraclizeAPI internal returns (address){
        return oraclize.cbAddress();
    }
    function oraclize_setProof(byte proofP) oraclizeAPI internal {
        return oraclize.setProofType(proofP);
    }
    function oraclize_setCustomGasPrice(uint gasPrice) oraclizeAPI internal {
        return oraclize.setCustomGasPrice(gasPrice);
    }

    function getCodeSize(address _addr) constant internal returns(uint _size) {
        assembly {
            _size := extcodesize(_addr)
        }
    }


    function parseAddr(string _a) internal returns (address){
        bytes memory tmp = bytes(_a);
        uint160 iaddr = 0;
        uint160 b1;
        uint160 b2;
        for (uint i=2; i<2+2*20; i+=2){
            iaddr *= 256;
            b1 = uint160(tmp[i]);
            b2 = uint160(tmp[i+1]);
            if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87;
            else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48;
            if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87;
            else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48;
            iaddr += (b1*16+b2);
        }
        return address(iaddr);
    }


    function strCompare(string _a, string _b) internal returns (int) {
        bytes memory a = bytes(_a);
        bytes memory b = bytes(_b);
        uint minLength = a.length;
        if (b.length < minLength) minLength = b.length;
        for (uint i = 0; i < minLength; i ++)
            if (a[i] < b[i])
                return -1;
            else if (a[i] > b[i])
                return 1;
        if (a.length < b.length)
            return -1;
        else if (a.length > b.length)
            return 1;
        else
            return 0;
   }

    function indexOf(string _haystack, string _needle) internal returns (int)
    {
        bytes memory h = bytes(_haystack);
        bytes memory n = bytes(_needle);
        if(h.length < 1 || n.length < 1 || (n.length > h.length))
            return -1;
        else if(h.length > (2**128 -1))
            return -1;
        else
        {
            uint subindex = 0;
            for (uint i = 0; i < h.length; i ++)
            {
                if (h[i] == n[0])
                {
                    subindex = 1;
                    while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex])
                    {
                        subindex++;
                    }
                    if(subindex == n.length)
                        return int(i);
                }
            }
            return -1;
        }
    }

    function strConcat(string _a, string _b, string _c, string _d, string _e) internal returns (string){
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) babcde[k++] = _ba[i];
        for (i = 0; i < _bb.length; i++) babcde[k++] = _bb[i];
        for (i = 0; i < _bc.length; i++) babcde[k++] = _bc[i];
        for (i = 0; i < _bd.length; i++) babcde[k++] = _bd[i];
        for (i = 0; i < _be.length; i++) babcde[k++] = _be[i];
        return string(babcde);
    }

    function strConcat(string _a, string _b, string _c, string _d) internal returns (string) {
        return strConcat(_a, _b, _c, _d, "");
    }

    function strConcat(string _a, string _b, string _c) internal returns (string) {
        return strConcat(_a, _b, _c, "", "");
    }

    function strConcat(string _a, string _b) internal returns (string) {
        return strConcat(_a, _b, "", "", "");
    }

    // parseInt
    function parseInt(string _a) internal returns (uint) {
        return parseInt(_a, 0);
    }

    // parseInt(parseFloat*10^_b)
    function parseInt(string _a, uint _b) internal returns (uint) {
        bytes memory bresult = bytes(_a);
        uint mint = 0;
        bool decimals = false;
        for (uint i=0; i<bresult.length; i++){
            if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
                if (decimals){
                   if (_b == 0) break;
                    else _b--;
                }
                mint *= 10;
                mint += uint(bresult[i]) - 48;
            } else if (bresult[i] == 46) decimals = true;
        }
        if (_b > 0) mint *= 10**_b;
        return mint;
    }


}
// </ORACLIZE_API>

contract coinback is usingOraclize {

    struct betInfo{
        address srcAddress;
        uint betValue;
    }

    uint POOL_AWARD;                                          //奖池
    uint constant FREE_PERCENT = 1;                          //服务费用比例1%

    uint32 public oraclizeGas = 200000;
    uint32 constant MAX_RANDOM_NUM = 1000000;                 //最大随机数
    betInfo overBetPlayer;                                    //超过奖池的玩家
    uint32 betId;
    uint32 luckyIndex;
    uint32 public turnId;                                     //期数
    uint   public beginTime;                                  //当期开始时间
    uint   public totalAward;                                 //累计奖励

    bool public stopContract;                                 //停止合约
    bool public stopBet;                                      //停止投注
    bool public exitOverPlayer;
    address owner;
    mapping(uint32=>betInfo) betMap;

    event LOG_NewTurn(uint turnNo,uint time,uint totalnum);                                         //新一轮(期号,开始时间,奖池金额)
    event LOG_PlayerBet(address srcAddress,uint betNum,uint turnNo,uint totalnum,uint time);        //投注事件(地址，金额，期号，奖池金额，本期开始时间)
    event LOG_LuckyPLayer(address luckyAddress,uint luckyNum,uint turnNo);                         //中奖事件(中奖地址，奖池金额，期数)

    modifier onlyOwner {
        if (owner != msg.sender) throw;
        _;
    }

    modifier notStopContract {
        if (stopContract) throw;
        _;
    }

    modifier notStopBet {
        if (stopBet) throw;
        _;
    }

    function coinback(uint initPool){

        owner = msg.sender;
        POOL_AWARD = initPool;
        turnId = 0;
        stopContract = false;
        exitOverPlayer = false;
        betId = 0;
        startNewTurn();
    }

    function ()payable {
        bet();
    }

    function bet() payable
        notStopContract
        notStopBet{

        uint betValue = msg.value;
        totalAward = address(this).balance;
        if(totalAward > POOL_AWARD)
            totalAward = POOL_AWARD;

        if(address(this).balance >= POOL_AWARD)
        {
            uint overValue = address(this).balance - POOL_AWARD;
            if(overValue > 0)
            {
                betValue = betValue - overValue;
                overBetPlayer = betInfo({srcAddress:msg.sender,betValue:overValue});
            }
            stopBet = true;
        }
        betMap[betId] = betInfo({srcAddress:msg.sender,betValue:betValue});
        betId++;

        LOG_PlayerBet(msg.sender,msg.value,turnId,totalAward,beginTime);

        if(stopBet)
          closeThisTurn();
    }

    function __callback(bytes32 myid, string result) {
        if (msg.sender != oraclize_cbAddress()) throw;

        uint randomNum = parseInt(result);
        totalAward = address(this).balance;
        if(totalAward > POOL_AWARD)
            totalAward = POOL_AWARD;

        uint randomBalance = totalAward*randomNum/MAX_RANDOM_NUM;
        uint32 index = 0;

        index = getLunckyIndex(randomBalance);
        uint winCoin = totalAward*(100-FREE_PERCENT)/100;
        uint waiterfree = totalAward*FREE_PERCENT/100;

        LOG_LuckyPLayer(betMap[index].srcAddress,totalAward,turnId);

        if(!betMap[index].srcAddress.send(winCoin)) throw;
        if(!owner.send(waiterfree)) throw;

        startNewTurn();
    }

    function getLunckyIndex(uint randomBalance) private returns(uint32){

        uint range = 0;
        for(uint32 i =0; i< betId; i++)
        {
            range += betMap[i].betValue;
            if(range >= randomBalance)
            {
                luckyIndex = i;
                return i;
            }
        }
    }

    function startNewTurn() private{

        clearBetMap();
        betId = 0;
        if(exitOverPlayer)
        {
            betMap[betId] = overBetPlayer;
            betId++;
            exitOverPlayer = false;
        }
        turnId++;
        beginTime = now;
        totalAward = address(this).balance;
        stopBet = false;
        LOG_NewTurn(turnId,beginTime,totalAward);
    }

    function clearBetMap() private{
        for(uint32 i=0;i<betId;i++){
            delete betMap[i];
        }
    }

    function closeThisTurn() private{
        bytes32 oid = oraclize_query("URL","https://www.random.org/integers/?num=1&min=1&max=1000000&col=1&base=10&format=plain&rnd=new",oraclizeGas);
    }

    function getLunckyInfo() returns(uint32,address,bool){
        return (luckyIndex,betMap[luckyIndex].srcAddress,stopContract);
    }

    function getOverPLayer() returns(address,uint){
        return (overBetPlayer.srcAddress,overBetPlayer.betValue);
    }
    /***********操作合约**********/

    function closeTurnByHand(uint32 no) onlyOwner{
        if(turnId != no) throw;
        if(address(this).balance == 0) throw;
        stopBet = true;
        closeThisTurn();
    }

    function killContract() onlyOwner {
        selfdestruct(owner);
    }

    function destroyContract() onlyOwner{
        stopContract = true;
    }

    function changeOwner(address newOwner) onlyOwner{
        owner = newOwner;
    }
}