contract Big2018Token {
    ////////////////////////////////////////////////////////
    //Intitial Parameters
    /**********Admin**********/
    address public creator;  //keep track of creator
    /**********DailyRelease**********/
    uint256 public tokensDaily = 10000; //max tokens available each day
    uint256 tokensToday = 0; //no tokens given out today
    uint256 public leftToday = 10000; //tokens left to sell today
    uint startPrice = 100000000000000; //COMPOUND INCREASE: Wei starter price that will be compounded
    uint q = 37; //COMPOUND INCREASE: for (1+1/q) multipler rate of 1.027 per day
    uint countBuy = 0; //count times bought
    uint start2018 = 1514764800; //when tokens become available 
    uint end2018 = 1546300799; //last second tokens available
    uint day = 1; //what day is it
    uint d = 86400; //sedonds in a day
    uint dayOld = 1; //counter to kep track of last day tokens were given
    /**********GameUsage**********/
    address public game;  //address of Game later in year
    mapping (address => uint) public box; //record each persons box choice
    uint boxRand = 0; //To start with random assignment of box used later in year, ignore use for token
    uint boxMax = 5; //Max random random assignment to box used later in year, ignore use for token
    event BoxChange(address who, uint newBox); //event that notifies of a box change in game
    /**********ERC20**********/
    string public name;
    string public symbol;
    uint8 public decimals = 18;
    uint256 public totalSupply;
    mapping (address => uint256) public balanceOf; //record who owns what
    event Transfer(address indexed from, address indexed to, uint256 value);// This generates a public event on the blockchain that will notify clients
    event Burn(address indexed from, uint256 value); // This notifies clients about the amount burnt
    mapping (address => mapping (address => uint256)) public allowance;
    /**********EscrowTrades**********/
    struct EscrowTrade {
        uint256 value; //value of number or tokens for sale
        uint price; //min purchase price from seller
        address to; //specify who is to purchase the tokens
        bool open; //anyone can purchase rather than named buyer. false = closed. true = open to all.
    }
    mapping (address => mapping (uint => EscrowTrade)) public escrowTransferInfo;
    mapping (address => uint) userEscrowCount;
    event Escrow(address from, uint256 value, uint price, bool open, address to); // This notifies clients about the escrow
    struct EscrowTfr {
        address from; //who has defined this escrow trade
        uint tradeNo; //trade number this user has made
    }
    EscrowTfr[] public escrowTransferList; //make an array of the escrow trades to be looked up
    uint public escrowCount = 0;

    ////////////////////////////////////////////////////////
    //Run at start
    function Big2018Token() public {
        creator = msg.sender; //store sender as creator
        game = msg.sender; //to be updated once game released with game address
        totalSupply = 3650000 * 10 ** uint256(decimals);  // Update total supply with the decimal amount
        balanceOf[this] = totalSupply;     // Give the creator all initial tokens
        name = "BIG2018TOKEN";                // Set the name for display purposes
        symbol = "B18";                       // Set the symbol for display purposes
    }

    ////////////////////////////////////////////////////////
    //The Price of the Token Each Day. 0 = today
    function getPriceWei(uint _day) public returns (uint) {
        require(now >= start2018 && now <= end2018); //must be in 2018
        day = (now - start2018)/d + 1; //count number of days since opening
        if (day > dayOld) {  //resent counter if first tx per day
            uint256 _value = ((day - dayOld - 1)*tokensDaily + leftToday) * 10 ** uint256(decimals);
            _transfer(this, creator, _value); //give remaining tokens from previous day to creator
            tokensToday = 0; //reset no of tokens sold today, this wont stick as 'veiw' f(x). will be saved in buy f(x)
            dayOld = day; //reset dayOld counter
        }
        if (_day != 0) { //if _day = 0, calculate price for today
        day = _day; //which day should be calculated
        }
        // Computes 'startPrice * (1+1/q) ^ n' with precision p, needed as solidity does not allow decimal for compounding
            //q & startPrice defined at top
            uint n = day - 1; //n of days to compound the multipler by
            uint p = 3 + n * 5 / 100; //itterations to calculate compound daily multiplier. higher is greater precision but more expensive
            uint s = 0; //output. itterativly added to for result
            uint x = 1; //multiply side of binomial expansion
            uint y = 1; //divide side of binomial expansion
            //itterate top q lines binomial expansion to estimate compound multipler
            for (uint i = 0; i < p; ++i) { //each itteration gets closer, higher p = closer approximation but more costly
                s += startPrice * x / y / (q**i); //iterate adding each time to s
                x = x * (n-i); //calc multiply side
                y = y * (i+1); //calc divide side
            }
            return (s); //return priceInWei = s
    }

    ////////////////////////////////////////////////////////
    //Giving New Tokens To Buyer
    function () external payable {
        // must buy whole token when minting new here, but can buy/sell fractions between eachother
        require(now >= start2018 && now <= end2018); //must be in 2018
        uint priceWei = this.getPriceWei(0); //get todays price
        uint256 giveTokens = msg.value / priceWei; //rounds down to no of tokens that can afford
            if (tokensToday + giveTokens > tokensDaily) { //if asking for tokens than left today
                giveTokens = tokensDaily - tokensToday;    //then limit giving to remaining tokens
                }
        countBuy += 1; //count usage
        tokensToday += giveTokens; //count whole tokens issued today
        box[msg.sender] = this.boxChoice(0); //assign box number to buyer
        _transfer(this, msg.sender, giveTokens * 10 ** uint256(decimals)); //transfer tokens from this contract
        uint256 changeDue = msg.value - (giveTokens * priceWei) * 99 / 100; //calculate change due, charged 1% to disincentivise high volume full refund calls.
        require(changeDue < msg.value); //make sure refund is not more than input
        msg.sender.transfer(changeDue); //give change
        
    }

    ////////////////////////////////////////////////////////
    //To Find Users Token Ammount and Box number
    function getValueAndBox(address _address) view external returns(uint, uint) {
        return (balanceOf[_address], box[_address]);
    }

    ////////////////////////////////////////////////////////
    //For transfering tokens to others
    function _transfer(address _from, address _to, uint _value) internal {
        require(_to != 0x0); // Prevent transfer to 0x0 address. Use burn() instead
        require(balanceOf[_from] >= _value); // Check if the sender has enough
        require(balanceOf[_to] + _value > balanceOf[_to]); // Check for overflows
        uint previousbalanceOf = balanceOf[_from] + balanceOf[_to]; // Save this for an assertion in the future
        balanceOf[_from] -= _value; // Subtract from the sender
        balanceOf[_to] += _value; // Add the same to the recipient
        Transfer(_from, _to, _value);
        assert(balanceOf[_from] + balanceOf[_to] == previousbalanceOf); // Asserts are used to use static analysis to find bugs in your code. They should never fail
    }

    ////////////////////////////////////////////////////////
    //Transfer tokens
    function transfer(address _to, uint256 _value) public {
        _transfer(msg.sender, _to, _value);
    }

    ////////////////////////////////////////////////////////
    //Transfer tokens from other address
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
        require(_value <= allowance[_from][msg.sender]); // Check allowance
        allowance[_from][msg.sender] -= _value;
        _transfer(_from, _to, _value);
        return true;
    }

    ////////////////////////////////////////////////////////
    //Set allowance for other address
    function approve(address _spender, uint256 _value) public returns (bool success) {
        allowance[msg.sender][_spender] = _value;
        return true;
    }

    ////////////////////////////////////////////////////////
    //Set allowance for other address and notify
    function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
        tokenRecipient spender = tokenRecipient(_spender);
        if (approve(_spender, _value)) {
            spender.receiveApproval(msg.sender, _value, this, _extraData);
            return true;
        }
    }

    ////////////////////////////////////////////////////////
    //Decide or change box used in game
    function boxChoice(uint _newBox) public returns (uint) { 
        //for _newBox = 0 assign random 
        boxRand += 1; //count up for even start box assingment
        if (boxRand > boxMax) { //stop box assignment too high
                    boxRand = 1; //return to box 1
            }
        if (_newBox == 0) {
            box[msg.sender] = boxRand; //give new random assignment to owner (or this if buying)
        } else {
        box[msg.sender] = _newBox; //give new assignment to owner (or this if buying)
        }
        BoxChange(msg.sender, _newBox); //let everyone know
            return (box[msg.sender]); //output to console
    }

    ////////////////////////////////////////////////////////
    //Release the funds for expanding project
    //Payable to re-top up contract
    function fundsOut() payable public { 
        require(msg.sender == creator); //only alow creator to take out
        creator.transfer(this.balance); //take the lot, can pay back into this via different address if wished re-top up
    }

    ////////////////////////////////////////////////////////
    //Used to tweak and update for Game
    function update(uint _option, uint _newNo, address _newAddress) public returns (string, uint) {
        require(msg.sender == creator || msg.sender == game); //only alow creator or game to use
        //change Max Box Choice
        if (_option == 1) {
            require(_newNo > 0);
            boxMax = _newNo;
            return ("boxMax Updated", boxMax);
        }
        //change address of game smart contract
        if (_option == 2) {
            game = _newAddress;
            return ("Game Smart Contract Updated", 1);
        }
    }

    ////////////////////////////////////////////////////////
    //Destroy tokens
    function burn(uint256 _value) public returns (bool success) {
        require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
        balanceOf[msg.sender] -= _value;            // Subtract from the sender
        totalSupply -= _value;                      // Updates totalSupply
        Burn(msg.sender, _value);
        return true;
    }

    ////////////////////////////////////////////////////////
    //Destroy tokens from other account
    function burnFrom(address _from, uint256 _value) public returns (bool success) {
        require(balanceOf[_from] >= _value);                // Check if the targeted balance is enough
        require(_value <= allowance[_from][msg.sender]);    // Check allowance
        balanceOf[_from] -= _value;                         // Subtract from the targeted balance
        allowance[_from][msg.sender] -= _value;             // Subtract from the sender's allowance
        totalSupply -= _value;                              // Update totalSupply
        Burn(_from, _value);
        return true;
    }

    ////////////////////////////////////////////////////////
    //For trsnsfering tokens to others using this SC to enure they pay    
    function setEscrowTransfer(address _to, uint _value, uint _price, bool _open) external returns (bool success) {
            //_to to specify a address who can purchase
            //_open if anyone can purchase (set _to to any address)
            //_price is min asking value for full value of tokens
            //_value is number of tokens available
            //_to, who will purchase value of tokens
        _transfer(msg.sender, this, _value); //store _value in this contract
        userEscrowCount[msg.sender] += 1;
        var escrowTrade = escrowTransferInfo[msg.sender][userEscrowCount[msg.sender]]; //record transfer option details
        escrowTrade.value += _value;//add value into retaining store for trade
        escrowTrade.price = _price; //set asking price
        escrowTrade.to = _to; //who will purchase
        escrowTrade.open = _open; //is trade open to all. false = closed. true = open to anyone.
        escrowCount += 1;
        escrowTransferList.push(EscrowTfr(msg.sender, userEscrowCount[msg.sender]));
        Escrow(msg.sender, _value, _price, _open, _to); // This notifies clients about the escrow
        return (true); //success!
    }
    
    ////////////////////////////////////////////////////////
    //For purchasing tokens from others using this SC to give trust to purchase
    function recieveEscrowTransfer(address _sender, uint _no) external payable returns (bool success) { 
            //_sender is person buying from
            require(escrowTransferInfo[_sender][_no].value != 0); //end if trade already completed
        box[msg.sender] = this.boxChoice(box[msg.sender]); //assign box number to buyer
        if (msg.sender == _sender) {
            _transfer(this, msg.sender, escrowTransferInfo[_sender][_no].value); //put tokens back to sender account
            escrowTransferInfo[_sender][_no].value = 0; //reset counter for escrow token
            Escrow(_sender, 0, msg.value, escrowTransferInfo[_sender][_no].open, msg.sender); // This notifies clients about the escrow
            return (true);
        } else {
            require(msg.value >= escrowTransferInfo[_sender][_no].price); //Check _to is Paying Enough
            if (escrowTransferInfo[_sender][_no].open == false) {
                require(msg.sender == escrowTransferInfo[_sender][_no].to); //Check _to is the intended purchaser
                }
            _transfer(this, msg.sender, escrowTransferInfo[_sender][_no].value);   
            _sender.transfer(msg.value); //Send the sender the value of the trade
            escrowTransferInfo[_sender][_no].value = 0; //no more in retaining store
            Escrow(_sender, 0, msg.value, escrowTransferInfo[_sender][_no].open, msg.sender); // This notifies clients about the escrow
            return (true); //success!
        }
    }
}
function burn(uint256 _value) public returns (bool success) {
    require(balanceOf[msg.sender] >= _value);   // Check if the sender has enough
    balanceOf[msg.sender] -= _value;            // Subtract from the sender
    totalSupply -= _value;                      // Updates totalSupply
    Burn(msg.sender, _value);
    return true;
}

//Destroy tokens