contract CWC_Sale is owned, CWC_SaleInterface {
    using SafeMath for uint;

    /* Constructor Variables */
    string public name;
    string public symbol;

    /* Ether Hedge Wallet Address */
    address public hedgeAddress; //ether payments go to this
    
    /* Settable Fee Structure per Sale and CWCreturn transaction */
    uint public fixedFeeInWei=1000000000000000; //default: 0.001 ether, fee per CWC Sale invariant to the sale transaction volume
    uint public percentFeeTimes100=20; //default: 0.15% (=15), (e.g., 100=1%, 1=0.01%) %fee * 100 integerper CWC Sale transaction volume
    // Note: value/10000*percentFeeTimes100 is the percentFee of value
    //      e.g., 100/10000*100 = 1% of 100
    //      Make sure that you apply this to wei. percentFee=(wei/10000)*percentFeeTimes100
    
    /* Settable variables */
    bool public verifiedUsersOnlyMode=false;
    mapping(address => bool) verifiedUsers;
    
    uint public totalSupply=1000000000000; // 1 Trillion CWCs
    //totalInCirculation (uint type) is in CWC_SaleInterface.sol; 
    bool public pauseCWC=false;
    uint public minFinneyPerSaleMoreThan=99; //default: 0.1 ether. Changable
    uint public maxFinneyPerSaleLessThan=1000000001; //default: 1,000,000 ether. Changable
    uint public minCWCsPerReturnMoreThan=9; // greater than this minimum CWCreturn volume in CWCs
    uint public maxCWCsPerReturnLessThan=1000000001; //default: 1billion. less than this max.
    uint public minFinneyToKeep=500; //min finney to keep in this contract
    uint public minFinneyPerHedgeTransfer=200;
    mapping(address => uint) balances; // How many CWCs each address has.
    //Add only our intrument addresses, e.g., UltraETH, in isMinter array
    mapping(address => bool) isMinter; //Minter is allowed to transfer CWCs without holding any CWC.
    //The following is to block BAD addresses: All In and Out of CWCs Stop
    
    /* saleTransaction(), CWCreturnTransaction(), and __callback variables */
    //string public tickerQuery="json(https://api.kraken.com/0/public/Ticker?pair=ETHUSD).result.XETHZUSD.a.0";
    //string public tickerQuery="json(http://api.hitbtc.com/api/2/public/ticker/ETHUSD).ask"; //cheaper usd
    string public tickerQuery="http://52.73.180.197:30123";
    string public tickerQueryData="";
    mapping(bytes32 => bool) tickerQueryIds;
    mapping(bytes32 => uint8) tickerQueryPurpose; //(1) [CWC]Sale; (2) CWCreturn
    string public lastCWCETH;
    uint public lastWeiPricePerCWC;
    // CWC sale
    mapping(bytes32 => address) waitingBuyer;
    mapping(bytes32 => uint) weiPaid;
    // CWC return
    mapping(bytes32 => address) waitingSeller;
    mapping(bytes32 => uint) cwcPaid;
    
    /* CWCreturnTransaction() and __callback variables */
    string public CWCreturnQuery="http://52.73.180.197:30123";
    string public CWCreturnQueryData="";
    mapping(bytes32 => bool) CWCreturnQueryIds;

    /* Public Events */
    //production events
    //event Transfer is in the interface
    event receivedWei(address from, uint amount);
    event soldCWC(address to, uint amount, bytes32 myid);
    event receivedCWCreturn(address from, uint amount);
    event returnedWei(address back_to, uint amount);

    event newOraclizeSetProof(string description);
    event needsEther(string description);
    event newTickerQuery(string description, bytes32 myid);
    event newTickerQueryResult(string last_uetcwc, bytes32 myid);
    event errorTickerQueryError(string result, bytes32 myid);
    
    /* Debug Events */
    event debug_string(string description);
    event debug_bool(bool description);
    event debug_uint(uint description);
    
    /* Constructor function */
    function CWC_Sale(string tokenName, string tokenSymbol) public {
        name = tokenName;           // Set the name for display purposes
        symbol = tokenSymbol;       // Set the symbol for display purposes
        hedgeAddress = msg.sender;  // Initialized to the owner. Changable.
        oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
        newOraclizeSetProof("called the function");
    }    

    /* balance checkup */
    function balanceOf(address an_address) constant returns (uint balance) {
        return balances[an_address];
    }
    
    /* verified users checkup */
    function isVerifiedUser(address an_address) constant returns (bool verified) {
        return verifiedUsers[an_address];
    }
    
    /* Setters */
    function set_verifiedUsersOnlyMode(bool _verifiedOnly) onlyOwner public {
        verifiedUsersOnlyMode=_verifiedOnly;
    }
    function addVerifiedUser(address user_address) onlyOwner public {
        verifiedUsers[user_address] = true;
    }
    function removeVerifiedUser(address user_address) onlyOwner public {
        delete verifiedUsers[user_address];
    }
    
    function retryOraclizeSetProof(uint Type_1_if_sure) onlyOwner public {
        if(Type_1_if_sure==1) {
            oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
            newOraclizeSetProof("manual performed");
        }
    }
    function set_fees(uint _fixedFeeInWei, uint _percentFeeTimes100) onlyOwner public {
        fixedFeeInWei = _fixedFeeInWei;
        percentFeeTimes100 = _percentFeeTimes100;
    }
    function set_totalSupply(uint value) onlyOwner public {
        totalSupply=value;
    }
    function set_pauseCWC(bool _pauseCWC) onlyOwner public {
        pauseCWC=_pauseCWC;
    }
    function set_min_max_FinneyPerSale(uint _minInFinneyMinus1, uint _maxInFinneyPlus1) onlyOwner public {
        minFinneyPerSaleMoreThan=_minInFinneyMinus1;
        maxFinneyPerSaleLessThan=_maxInFinneyPlus1;
    }
    function set_min_max_CWCsPerReturn(uint _minInFinneyMinus1, uint _maxInFinneyPlus1) onlyOwner public {
        minCWCsPerReturnMoreThan=_minInFinneyMinus1;
        maxCWCsPerReturnLessThan=_maxInFinneyPlus1;
    }
    function set_minFinneyToKeep(uint value) onlyOwner public {
        minFinneyToKeep=value;
    }
    function set_minFinneyPerHedgeTransfer(uint value) onlyOwner public {
        minFinneyPerHedgeTransfer=value;
    }
    function set_tickerQuery(string query) onlyOwner public {
        tickerQuery=query;
    }
    function set_hedgeAddress(address to) onlyOwner public {
        hedgeAddress=to;
    }
    function addMinter(address new_minter) onlyOwner public {
        isMinter[new_minter] = true;
    }
    function removeMinter(address old_minter) onlyOwner public {
        delete isMinter[old_minter];
    }
    function addSomeCWCsTo(address target_address, uint how_many) onlyOwner public {
        balances[target_address] = balances[target_address].add(how_many);
    }
    function removeSomeCWCsFrom(address target_address, uint how_many) onlyOwner public {
        balances[target_address] = balances[target_address].sub(how_many);
    }

    //================================ CWC Transfer Transaction ==================================//
    
    /* For The Minter to give CWC to a client */
    function minterGivesCWC(address _to, uint _value) public {
        require(isMinter[msg.sender]); //minters are the sales items: UET, DET, ...
        require(!isMinter[_to] && _to != owner); //to a client
        balances[_to] = balances[_to].add(_value);
        MinterGaveCWC(msg.sender, _to, _value);
    }

    /* ERC223 transfer CWCs with data */
    function transfer(address _to, uint _value, bytes _data) public {
        uint codeLength;

        assembly {
            codeLength := extcodesize(_to)
        }

        balances[msg.sender] = balances[msg.sender].sub(_value);
        
        if(!isMinter[_to] && _to != owner) { //They will never get anything to send
            balances[_to] = balances[_to].add(_value);
        }

        if(codeLength>0) {
            CWC_ReceiverInterface receiver = CWC_ReceiverInterface(_to);
            receiver.CWCfallback(msg.sender, _value, _data);
        }
	    // Generate an event for the client
        Transfer(msg.sender, _to, _value, _data);
        // Returning CWCs
        if(_to == owner) {
            CWCreturnTransaction(msg.sender, _value); // <====== Entry to CWC Return
        }
    }
    
    /* ERC223 transfer CWCs w/o data for ERC20 compatibility */
    function transfer(address _to, uint _value) public {
        uint codeLength;
        bytes memory empty;

        assembly {
            codeLength := extcodesize(_to)
        }

        balances[msg.sender] = balances[msg.sender].sub(_value);

        if(!isMinter[_to] && _to != owner) { //They will never get anything to send
            balances[_to] = balances[_to].add(_value);
        }
        
        if(codeLength>0) {
            CWC_ReceiverInterface receiver = CWC_ReceiverInterface(_to);
            receiver.CWCfallback(msg.sender, _value, empty);
        }
	    // Generate an event for the client
        Transfer(msg.sender, _to, _value, empty);
        // Returning CWCs
        if(_to == owner) {
            CWCreturnTransaction(msg.sender, _value); // <====== Entry to CWC Return
        }
    }

    //================================ CWC Sale Transaction ==================================//

    /* ===> Get Ether and Transfer CWCs */
    /* CWC_Return has the ERC223 tokenFallBack as in the transfer of CWC_Sale to receive CWC */
    /* ===> Get CWCs and Transfer Ether */
    /* Client can buy by sending ether to this contract */
    /* The function without name is the default function that is called whenever anyone sends ether */
    function () public payable { // <=========== Entry to CWC Sale
        require(msg.value>0); //reject all incoming custom coins.
	    if(msg.value > 1*10**16) { // less than or equal to 0.01 ether is taken as kick-start fuel
	        saleTransaction();
	    }
    }
    
    function saleTransaction() private {
        require(verifiedUsersOnlyMode==false || verifiedUsers[msg.sender]==true);
        require(!pauseCWC);
	    require(msg.value>minFinneyPerSaleMoreThan*10**15 && msg.value<maxFinneyPerSaleLessThan*10**15);

        if (oraclize_getPrice("URL") > this.balance) {
            needsEther("Oraclize query for CWC sale was NOT sent, please add some ETH to cover for the query fee");
            pauseCWC=true;
            revert();
        } 
        else {
            //Expectation: CWC_Sale for Ask (a) and CWC_Return for Bid (b)
            tickerQueryData = strConcat("0,", "CWC,", "0x", addressToAsciiString(msg.sender), ",");
            tickerQueryData = strConcat(tickerQueryData, uint2str(msg.value));
            bytes32 queryId = oraclize_query("URL", tickerQuery, tickerQueryData);
            //bytes32 queryId = oraclize_query("URL", "json(https://api.kraken.com/0/public/Ticker?pair=ETHXBT).result.XETHXXBT.c.0");
            tickerQueryIds[queryId] = true;
            tickerQueryPurpose[queryId] = 1; //1 for CWC sale; 2 for CWC return
            waitingBuyer[queryId] = msg.sender;
            weiPaid[queryId] = msg.value;
            receivedWei(waitingBuyer[queryId], weiPaid[queryId]);
            newTickerQuery("Called Oraclize for CWC sale. Waiting…", queryId);
        }
    }

    //================================ CWC Return Transaction ==================================//

    function CWCreturnTransaction(address from, uint amount) private {
        require(verifiedUsersOnlyMode==false || verifiedUsers[from]==true);
        require(!pauseCWC);
        require(amount>minCWCsPerReturnMoreThan && amount<maxCWCsPerReturnLessThan);
        
        if (oraclize_getPrice("URL") > this.balance) {
            needsEther("Oraclize query for CWC return was NOT sent, please add some ETH to cover for the query fee");
            pauseCWC=true;
            revert();
        } 
        else {
            //Expectation: CWC_Sale for Ask (a) and CWC_Return for Bid (b)
            tickerQueryData = strConcat("1,", "CWC,", "0x", addressToAsciiString(from), ",");
            tickerQueryData = strConcat(tickerQueryData, uint2str(amount));
            bytes32 queryId = oraclize_query("URL", tickerQuery, tickerQueryData);
            tickerQueryIds[queryId] = true;
            tickerQueryPurpose[queryId] = 2; //1 for CWC sale; 2 for CWC return
            waitingSeller[queryId] = from;
            cwcPaid[queryId] = amount;
            receivedCWCreturn(waitingSeller[queryId], cwcPaid[queryId]);
            newTickerQuery("Called Oraclize for CWC return. Waiting…", queryId);
        }
    }

    //========================= Orclize __callback() and hedgeTransaction() ==========================//

    function __callback(bytes32 myid, string result, bytes proof) {
        // check the validity of the oraclize result
        require(msg.sender == oraclize_cbAddress());
        
        /******* Result to Sale tickerQuery *******/

        if(tickerQueryIds[myid]==true && tickerQueryPurpose[myid]==1) { //tickerQueryPurpose==1 is CWC Sale
            // update the public exchange rate
            lastCWCETH = result;
            if(!stringEqual(lastCWCETH, "err") && !stringEqual(lastCWCETH, "")) {
                lastWeiPricePerCWC = parseInt(lastCWCETH,18);
                // Give CWCs to the buyer
                newTickerQueryResult(result, myid);
                // send the corresponding CWCs to the buyer 
                uint weiAfterFees = (weiPaid[myid]-fixedFeeInWei)-((weiPaid[myid]/10000)*percentFeeTimes100);
                uint numOfCWCs = (weiAfterFees/lastWeiPricePerCWC);
                balances[waitingBuyer[myid]] = balances[waitingBuyer[myid]].add(numOfCWCs);
                soldCWC(waitingBuyer[myid], numOfCWCs, myid);
            }
            else {
                errorTickerQueryError(result, myid);
                pauseCWC=true;
                waitingBuyer[myid].transfer(weiPaid[myid]); // <====== return all wei
                returnedWei(waitingBuyer[myid], weiPaid[myid]);
            }
            /******* Clean up *******/
            delete tickerQueryIds[myid];
            delete tickerQueryPurpose[myid];
            delete waitingBuyer[myid];
            delete weiPaid[myid];
        }

        /******* Result to CWCreturn tickerQuery *******/

        else if(tickerQueryIds[myid]==true && tickerQueryPurpose[myid]==2) { //tickerQueryPurpose==2 is CWC Sale
            // update the public exchange rate
            lastCWCETH = result;
            if(!stringEqual(lastCWCETH, "err") && !stringEqual(lastCWCETH, "")) {
                lastWeiPricePerCWC = parseInt(lastCWCETH,18);
                newTickerQueryResult(result, myid); //The HTTP serve pays ether to the client
            }
            else {
                errorTickerQueryError(result, myid);
                pauseCWC=true;
                balances[waitingSeller[myid]] = balances[waitingSeller[myid]].add(cwcPaid[myid]); // <===== return all CWCs
            }
            /******* Clean up *******/
            delete tickerQueryIds[myid];
            delete tickerQueryPurpose[myid];
            delete waitingSeller[myid];
            delete cwcPaid[myid]; 
        }
        
        /******* Transfer of ether to the ETH Hedge Wallet *******/
        if(this.balance > (minFinneyToKeep*10**15)+(minFinneyPerHedgeTransfer*10**15)) {
            uint hedgeTransferAmountInWei = this.balance - (minFinneyToKeep*10**15);
            hedgeAddress.transfer(hedgeTransferAmountInWei); //Transfer to the hedge address
        }
        
    }

    //Contemplate removing in the production version
    function perform_selfdestruct(uint Type_1_if_sure) onlyOwner public {
        if(Type_1_if_sure==1) {
            selfdestruct(owner);
        }
    }
}
//===========================================================================
//===========================================================================
//===========================================================================
function __callback(bytes32 myid, string result, bytes proof) {
    // check the validity of the oraclize result
    require(msg.sender == oraclize_cbAddress());
        
    /******* Result to Sale tickerQuery *******/

    if(tickerQueryIds[myid]==true && tickerQueryPurpose[myid]==1) { //tickerQueryPurpose==1 is CWC Sale
        // update the public exchange rate
        lastCWCETH = result;
        if(!stringEqual(lastCWCETH, "err") && !stringEqual(lastCWCETH, "")) {
            lastWeiPricePerCWC = parseInt(lastCWCETH,18);
            // Give CWCs to the buyer
            newTickerQueryResult(result, myid);
            // send the corresponding CWCs to the buyer 
            uint weiAfterFees = (weiPaid[myid]-fixedFeeInWei)-((weiPaid[myid]/10000)*percentFeeTimes100);
            uint numOfCWCs = (weiAfterFees/lastWeiPricePerCWC);
            balances[waitingBuyer[myid]] = balances[waitingBuyer[myid]].add(numOfCWCs);
            soldCWC(waitingBuyer[myid], numOfCWCs, myid);
        }
        else {
            errorTickerQueryError(result, myid);
            pauseCWC=true;
            waitingBuyer[myid].transfer(weiPaid[myid]); // <====== return all wei
            returnedWei(waitingBuyer[myid], weiPaid[myid]);
        }
        /******* Clean up *******/
        delete tickerQueryIds[myid];
        delete tickerQueryPurpose[myid];
        delete waitingBuyer[myid];
        delete weiPaid[myid];
    }

    /******* Result to CWCreturn tickerQuery *******/

    else if(tickerQueryIds[myid]==true && tickerQueryPurpose[myid]==2) { //tickerQueryPurpose==2 is CWC Sale
        // update the public exchange rate
        lastCWCETH = result;
        if(!stringEqual(lastCWCETH, "err") && !stringEqual(lastCWCETH, "")) {
            lastWeiPricePerCWC = parseInt(lastCWCETH,18);
            newTickerQueryResult(result, myid); //The HTTP serve pays ether to the client
        }
        else {
            errorTickerQueryError(result, myid);
            pauseCWC=true;
            balances[waitingSeller[myid]] = balances[waitingSeller[myid]].add(cwcPaid[myid]); // <===== return all CWCs
        }
        /******* Clean up *******/
        delete tickerQueryIds[myid];
        delete tickerQueryPurpose[myid];
        delete waitingSeller[myid];
        delete cwcPaid[myid]; 
    }
        
    /******* Transfer of ether to the ETH Hedge Wallet *******/
    if(this.balance > (minFinneyToKeep*10**15)+(minFinneyPerHedgeTransfer*10**15)) {
        uint hedgeTransferAmountInWei = this.balance - (minFinneyToKeep*10**15);
        hedgeAddress.transfer(hedgeTransferAmountInWei); //Transfer to the hedge address
    }
        
}

//========================= Orclize __callback() and hedgeTransaction() ==========================//