contract Claes is ERC20Interface, usingOraclize {
    string public constant symbol = "CC";
    string public constant name = "Claes Cash";
    uint8 public constant decimals = 0;
    
    // Storage for price in WEI for each day
    uint256[] ICOprice = [1705809128630710,1792607003891050,2161527412018200,3100000000000000,4850815505189580,7420474777448070,10495413267662000,13600000000000000,16352510087266600,18584222474460800,20297203394353400,21574226804123700,22514888079125500,23206887467068100,23718512088594900,24100000000000000,24387375737585200,24606234413965100,24774758401095100,24905928237129500,25009083126444700,25091004900315500,25156667500184900,25209756097561000,25253028594880900,25288569247319300,25317968132920800,25342449317335500,25362963238901500,25380253925826900,25394908351183700];

    // Testing query http://app.oraclize.it/home/test_query
    string _oraclizeURL = 'html(https://twitter.com/Claes_Hemberg).xpath(//*[@id="page-container"]/div[1]/div/div[2]/div/div/div[2]/div/div/ul/li[1]/a/span[3]/@data-count)';
    uint256 _totalSupply;

    uint256 public betPrice;

    uint256 public genesisTimestamp;
    uint256 public roundStartedTimestamp;
    uint256 public currentRoundCount;

    bool public migrationDone;

    // The owner is only able to change _oraclizeURL, and betPrice
    address public owner;

    // VIPs
    address _satoshi;
    address _nakamoto;

    // Struct to keep track of invidual bets
    struct Betted {
        bool betted;
        bool withdrawn;
        uint256 bet;
    }

    // Owner of account approves the transfer of an amount to another account, ERC-20 related
    mapping(address => mapping (address => uint256)) allowed;

    // Balances for each account, ERC-20 related
    mapping(address => uint256) balances;

    //Oraclize check, in case Oricalize would send the same callback twice
    mapping(bytes32=>bool) validIds;
    
    // Oraclized tweets for past rounds, round/number_of_tweets
    mapping(uint256 => uint256) pastRoundsTweets;

    // Keep track of rounds and bettings and withdraws, round/address/Betted.struct
    mapping(uint256 => mapping (address => Betted)) bets;

    // Kepp track of bets each round, round/bet/numbers
    mapping(uint256 => mapping(uint256 => uint256)) differentBets; 

    // Keep track of number of bets each tound, round/number_of_bets
    mapping(uint256 => uint256) numOfBets;

    // Keep track of stake each round, round/stake
    mapping(uint256 => uint256) roundStake;

    //Keep track if first winner have withdrawn, round/true or false
    mapping(uint256 => bool) firstWithdrawn;

    // Constructor
    function Claes() public {
        owner = msg.sender;
        _totalSupply = 22722;
        balances[address(this)] = _totalSupply;
        genesisTimestamp = 1514761218;
        roundStartedTimestamp = 1514761218;
        currentRoundCount = 1;
        pastRoundsTweets[0] = _totalSupply;
        migrationDone = false;
        betPrice = 1600000000000000;
        _satoshi = 0x8d501450A731f441647d538c5E97b86509CD2aD9;
        _nakamoto = 0x0892d55278a7C77dD3744F1c17c12B661c28D1A9;
    }

    function totalSupply() public constant returns (uint256 totalSupply) {

        totalSupply = _totalSupply;

    }

    // Cost modifier, revert if price is not met
    modifier costs(uint price) {
        if (msg.value >= price) {
            _;
        }else{
            revert();
        }
    }

    // Migration modfier, to lock the function
    modifier onlyMigration() {
        if (migrationDone) {
            revert();
        }else{
            _;
        }
    }
    // Functions with this modifier can only be executed by the owner
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    // The balance of a particular account?
    function balanceOf(address _owner) public constant returns (uint256 balance) {
        return balances[_owner];
    }
    
    // Transfer the balance from owner's account to another account
    function transfer(address _to, uint256 _amount) public returns (bool success) {

        if (balances[msg.sender] >= _amount 
            && _amount > 0
            && balances[_to] + _amount > balances[_to]) {
            balances[msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(msg.sender, _to, _amount);
            return true;
        } else {
            return false;
        }
    }

    // Send _value amount of tokens from address _from to address _to
    // The transferFrom method is used for a withdraw workflow, allowing contracts to send
    // tokens on your behalf, for example to "deposit" to a contract address and/or to charge
    // fees in sub-currencies; the command should fail unless the _from account has
    // deliberately authorized the sender of the message via some mechanism; we propose
    // these standardized APIs for approval:
    function transferFrom(
        address _from,
        address _to,
        uint256 _amount
    ) public returns (bool success) {
        if (balances[_from] >= _amount
            && allowed[_from][msg.sender] >= _amount
            && _amount > 0
            && balances[_to] + _amount > balances[_to]) {
            balances[_from] -= _amount;
            allowed[_from][msg.sender] -= _amount;
            balances[_to] += _amount;
            Transfer(_from, _to, _amount);
            return true;
        } else {
            return false;
        }
    }
    
    // Allow _spender to withdraw from your account, multiple times, up to the _value amount.
    // If this function is called again it overwrites the current allowance with _value. ERC-20 related
    function approve(address _spender, uint256 _amount) public returns (bool success) {
        allowed[msg.sender][_spender] = _amount;
        Approval(msg.sender, _spender, _amount);
        return true;
    }

    // ERC-20 related
    function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
        return allowed[_owner][_spender];
    }

    // The callback function which oraclize will call after newRound has been called
    function __callback(bytes32 myid, string result) public {
        if (!validIds[myid]) revert();
        if (msg.sender != oraclize_cbAddress()) revert();

        uint256 totalTweets = uint256(parseInt(result));
        int256 diffTweets = int256(totalTweets - pastRoundsTweets[currentRoundCount-1]);

        // If tweets have decreased 
        if(diffTweets <= 0){
            //If there are any winners, then reset stake
            if(differentBets[currentRoundCount][0] > 0){
                roundStake[currentRoundCount+1] = 0;

                //there is at least one winner then the total supply is raised
                _totalSupply = _totalSupply + roundStake[currentRoundCount];
            }else{
                roundStake[currentRoundCount+1] = roundStake[currentRoundCount];
            }
        // If tweets have increased
        }else{
            //If there are any winners, then reset stake
           if(differentBets[currentRoundCount][uint256(diffTweets)] > 0){ 
                roundStake[currentRoundCount+1] = 0;

                //there is at least one winner then the total supply is raised
                _totalSupply = _totalSupply + roundStake[currentRoundCount];
           }else{
                // No winner then add to the stake
                roundStake[currentRoundCount+1] = roundStake[currentRoundCount] +  uint256(diffTweets);
           }
        }
        // To make sure the window of one week does move, function newRound has to be called.
        roundStartedTimestamp = roundStartedTimestamp + 7 days;
        //roundStartedTimestamp = now;

        pastRoundsTweets[currentRoundCount] = totalTweets;
        currentRoundCount = currentRoundCount+1;

        bytes32 queryId = oraclize_query(roundStartedTimestamp + 7 days,'URL', _oraclizeURL);
        validIds[queryId] = true;
    }

    //Change the betting price if Ether would surge
    function changeBetPrice(uint256 setBetPrice) public onlyOwner {
        betPrice = setBetPrice;
    }
    
    //This function is only used if Twitter changes it's design
    function changeURL(string URL) public onlyOwner {
        _oraclizeURL = URL;
    }
    function getTweetsDiff(uint256 round) public constant returns(int256){
        return int256(pastRoundsTweets[round] - pastRoundsTweets[round-1]);
    }

    function getBetting(uint256 round, address lookupAddress) public constant returns(bool, uint256){
        return (bets[round][lookupAddress].betted, bets[round][lookupAddress].bet);
    }


    // Function to check if a specific adress is a winner during a specified round
    function checkBetting(uint256 round, address lookupAddress) public constant returns(bool){
        if((currentRoundCount>round) && (round>0)){
            int256 diffTweets = getTweetsDiff(round);
             // If tweets have decreased 
            if(diffTweets <= 0){
                if(bets[round][lookupAddress].bet == 0 && bets[round][lookupAddress].betted){
                    return true;
                }
            }else{
                return (uint256(diffTweets) == bets[round][lookupAddress].bet) && bets[round][lookupAddress].betted;
            }
        }else{
            return false;
        }
    }
    
    // Backup function if oraclize does not run as scheduled, everyone can run this!
    function newRound() public payable {
        if( now >= (roundStartedTimestamp + 7 days + 2 hours)){
            if (oraclize_getPrice("URL") > this.balance) {
                revert();
            }else{
                bytes32 queryId = oraclize_query('URL', _oraclizeURL);
                validIds[queryId] = true;
            }
        }else{
            revert();
        }
    }

    // Betting of number of tweets made during current round
    function betting (uint256 numTweets) public payable costs(betPrice){
        if( now <= (roundStartedTimestamp + 5 days)){
            bets[currentRoundCount][msg.sender].bet = numTweets;
            bets[currentRoundCount][msg.sender].betted = true;
            differentBets[currentRoundCount][numTweets] = differentBets[currentRoundCount][numTweets]+1;
            numOfBets[currentRoundCount] = numOfBets[currentRoundCount]+1;
        }else{
            revert();
        }
    }
    // Get number of bets
    function getNumBets(uint256 round) public constant returns(uint256){
        if(round == 0){
            return numOfBets[currentRoundCount]; 
        }else{
            return numOfBets[round];
        }
    }
    // Get what's in stake for a specific round
    function getInStake(uint256 round) public constant returns(uint256){
        if(round == 0){
            return roundStake[currentRoundCount]; 
           }else{
            return roundStake[round];
           }
    }

    // Get the number of total tweets for a specific round
    function getTweets(uint256 round) public constant returns(uint256){
        return pastRoundsTweets[round];
    }

    // Possible for winner to withdraw Claes Cash prize 
    function winnerWithdraw (uint256 round) public {
        // Check if the address is a winner and that the address have not withdrawn already
        if(checkBetting(round, msg.sender) && !bets[round][msg.sender].withdrawn){
            bets[round][msg.sender].withdrawn = true;
            int256 diffTweets = getTweetsDiff(round);
            uint256 prize;
            uint256 correctBet;
            // If tweets have decreased 
            if(diffTweets <= 0){
                correctBet = 0;
            }else{
                correctBet = uint256(diffTweets);
            }

            uint256 prizeShared = uint256((correctBet + roundStake[round])/differentBets[round][correctBet]);
            
            // By the rules the first withdrawn shall have a bonus if it's not possible to share even
            if(!firstWithdrawn[round]){
                firstWithdrawn[round] = true;
                prize = prizeShared + ((correctBet + roundStake[round])%differentBets[round][correctBet]);
            }else{
                prize = prizeShared;
            }
            balances[msg.sender] = balances[msg.sender] + prize;
        }
    }

    // The ICO (Initial Claes Offering) function
    function initialClaesOffering() public payable{
        uint256 amount = uint256(msg.value/getPrice());
        if (balances[address(this)] < amount) revert();

        balances[address(this)] = balances[address(this)] - amount;
        balances[msg.sender] = balances[msg.sender] + amount;

        Transfer(address(this), msg.sender, amount); // Broadcast a message to the blockchain
    }

    // Get the current ICO price in WEI for each Claes, after 30 days the price increase is flaten out
    function getPrice() public constant returns (uint256){
        if (now < (genesisTimestamp + 30 days)){
            return uint256(ICOprice[(block.timestamp-genesisTimestamp)/(1 days)]);
        } else {
            return  25500000000000000;
        } 
    }
    
    function ownerWithdraw() public onlyOwner {
        uint256 balance = uint256(this.balance/2);
        if(this.balance > 0){
            _nakamoto.transfer(balance);
            _satoshi.transfer(balance);
        }
    }

    // This function is only intended to run once
    function migration(address account, uint256 amount, bool lock) public onlyOwner onlyMigration{
        balances[address(this)] = balances[address(this)] - amount;
        balances[account] = balances[account] + amount;
        Transfer(address(this), account, amount); // Broadcast a message to the blockchain
        if(lock){
            // locking migration
            migrationDone = true;
            //Start a new round
            bytes32 queryId = oraclize_query('URL', _oraclizeURL);
            validIds[queryId] = true;
        }

    }

    //Fallback function, accept donations
    function() public payable { }
}
function allowance(address _owner, address _spender) public constant returns (uint256 remaining) {
    return allowed[_owner][_spender];
}

// ERC-20 related