contract TrueToken is ERC20, ERC223Interface, PoSTokenStandard, Pausable {
    using SafeMath for uint256;

    event CoinAgeRecordEvent(
        address indexed who,
        uint256 value,
        uint64 time
    );
    event CoinAgeResetEvent(
        address indexed who,
        uint256 value,
        uint64 time
    );

    string public constant name = "TRUE Token";
    string public constant symbol = "TRUE";
    uint8 public constant decimals = 18;

    mapping (address => uint256) balances;

    mapping (address => mapping (address => uint256)) internal allowed;

    uint256 totalSupply_;

    /**
    * @dev Total Number of TRUE tokens that can ever be created.
    *      200M TRUE Tokens
    */
    uint256 public MAX_TOTAL_SUPPLY = 200000000 *  10 ** uint256(decimals);

    /**
    * @dev Initial supply of TRUE tokens.
    *      70M TRUE Tokens
    *      35% of Maximum Total Supply
    *      Will be distributed as follows:
    *           5% : Platform Partners
    *           1% : Pre-Airdrop
    *          15% : Mega-Airdrop
    *           4% : Bounty (Vested over 6 months)
    *          10% : Development (Vested over 12 months)
    */
    uint256 public INITIAL_SUPPLY = 70000000 *  10 ** uint256(decimals);

    /**
    * @dev Time at which the contract was deployed
    */
    uint256 public chainStartTime;

    /**
    * @dev Ethereum Blockchain Block Number at time the contract was deployed
    */
    uint256 public chainStartBlockNumber;

    /**
    * @dev To keep the record of a single incoming token transfer
    */
    struct CoinAgeRecord {
        uint256 amount;
        uint64 time;
    }

    /**
    * @dev To keep the coin age record for all addresses
    */
    mapping(address => CoinAgeRecord[]) coinAgeRecordMap;

    /**
     * @dev Modifier to make contract mint new tokens only
     *      - Staking has started.
     *      - When total supply has not reached MAX_TOTAL_SUPPLY.
     */
    modifier canMint() {
        require(stakeStartTime > 0 && now >= stakeStartTime && totalSupply_ < MAX_TOTAL_SUPPLY);
        _;
    }

    constructor() public {
        chainStartTime = now;
        chainStartBlockNumber = block.number;

        stakeMinAge = 3 days;
        stakeMaxAge = 60 days;

        balances[msg.sender] = INITIAL_SUPPLY;
        totalSupply_ = INITIAL_SUPPLY;
    }

    /**
    * @dev total number of tokens in existence
    */
    function totalSupply() public view returns (uint256) {
        return totalSupply_;
    }

    /**
    * @dev Transfer the specified amount of tokens to the specified address.
    *      - Invokes the `tokenFallback` function if the recipient is a contract.
    *        The token transfer fails if the recipient is a contract
    *        but does not implement the `tokenFallback` function
    *        or the fallback function to receive funds.
    *      - Records coin age if the recipient is not a contract
    *
    * @param _to    Receiver address.
    * @param _value Amount of tokens that will be transferred.
    * @param _data  Transaction metadata.
    */
    function transfer(address _to, uint256 _value, bytes _data) public whenNotPaused returns (bool) {
        require(_to != address(0));

        if (msg.sender == _to) {
            return mint();
        }

        require(_value <= balances[msg.sender]);

        bool flag = isContract(_to);

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        if (flag) {
            ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
            receiver.tokenFallback(msg.sender, _value, _data);
        }
        emit Transfer(msg.sender, _to, _value, _data);

        logCoinAgeRecord(msg.sender, _to, _value, flag);

        return true;
    }

    /**
    * @dev Transfer the specified amount of tokens to the specified address.
    *      This function works the same with the previous one
    *      but doesn't contain `_data` param.
    *      Added due to backwards compatibility reasons.
    * @param _to The address to transfer to.
    * @param _value The amount to be transferred.
    */
    function transfer(address _to, uint256 _value) public whenNotPaused returns (bool) {
        require(_to != address(0));

        if (msg.sender == _to) {
            return mint();
        }

        require(_value <= balances[msg.sender]);

        bytes memory empty;
        bool flag = isContract(_to);

        balances[msg.sender] = balances[msg.sender].sub(_value);
        balances[_to] = balances[_to].add(_value);
        if (flag) {
            ERC223ReceivingContract receiver = ERC223ReceivingContract(_to);
            receiver.tokenFallback(msg.sender, _value, empty);
        }
        emit Transfer(msg.sender, _to, _value, empty);

        logCoinAgeRecord(msg.sender, _to, _value, flag);

        return true;
    }


    /**
     * @dev Transfer tokens from one address to another
     * @param _from address The address which you want to send tokens from
     * @param _to address The address which you want to transfer to
     * @param _value uint256 the amount of tokens to be transferred
     */
    function transferFrom(address _from, address _to, uint256 _value) public whenNotPaused returns (bool) {
        require(_to != address(0));
        require(_value <= balances[_from]);
        require(_value <= allowed[_from][msg.sender]);

        balances[_from] = balances[_from].sub(_value);
        balances[_to] = balances[_to].add(_value);
        allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
        emit Transfer(_from, _to, _value);

        // Coin age should not be recorded if receiver is the sender.
        if (_from != _to) {
            logCoinAgeRecord(_from, _to, _value, isContract(_to));
        }

        return true;
    }

    /**
     * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
     *
     * Beware that changing an allowance with this method brings the risk that someone may use both the old
     * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
     * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     * @param _spender The address which will spend the funds.
     * @param _value The amount of tokens to be spent.
     */
    function approve(address _spender, uint256 _value) public whenNotPaused returns (bool) {
        require(_spender != address(0));
        allowed[msg.sender][_spender] = _value;
        emit Approval(msg.sender, _spender, _value);
        return true;
    }

    /**
     * @dev Function to check the amount of tokens that an owner allowed to a spender.
     * @param _owner address The address which owns the funds.
     * @param _spender address The address which will spend the funds.
     * @return A uint256 specifying the amount of tokens still available for the spender.
     */
    function allowance(address _owner, address _spender) public view returns (uint256) {
        return allowed[_owner][_spender];
    }

    /**
     * @dev Increase the amount of tokens that an owner allowed to a spender.
     *
     * approve should be called when allowed[_spender] == 0. To increment
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * @param _spender The address which will spend the funds.
     * @param _addedValue The amount of tokens to increase the allowance by.
     */
    function increaseApproval(address _spender, uint256 _addedValue) public whenNotPaused returns (bool) {
        require(_spender != address(0));
        allowed[msg.sender][_spender] = (allowed[msg.sender][_spender].add(_addedValue));
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
     * @dev Decrease the amount of tokens that an owner allowed to a spender.
     *
     * approve should be called when allowed[_spender] == 0. To decrement
     * allowed value is better to use this function to avoid 2 calls (and wait until
     * the first transaction is mined)
     * From MonolithDAO Token.sol
     * @param _spender The address which will spend the funds.
     * @param _subtractedValue The amount of tokens to decrease the allowance by.
     */
    function decreaseApproval(address _spender, uint256 _subtractedValue) public whenNotPaused returns (bool) {
        require(_spender != address(0));
        uint256 oldValue = allowed[msg.sender][_spender];
        if (_subtractedValue > oldValue) {
            allowed[msg.sender][_spender] = 0;
        } else {
            allowed[msg.sender][_spender] = oldValue.sub(_subtractedValue);
        }
        emit Approval(msg.sender, _spender, allowed[msg.sender][_spender]);
        return true;
    }

    /**
    * @dev Gets the balance of the specified address.
    * @param _owner The address to query the the balance of.
    * @return An uint256 representing the amount owned by the passed address.
    */
    function balanceOf(address _owner) public view returns (uint256) {
        return balances[_owner];
    }

    /**
    * @dev Mints new TRUE token and rewards to caller as per the coin age.
    *      Deletes all previous coinage records and resets with new coin age record.
    */
    function mint() public whenNotPaused canMint returns (bool) {
        if (balances[msg.sender] <= 0) {
            return false;
        }

        if (coinAgeRecordMap[msg.sender].length <= 0) {
            return false;
        }

        uint256 reward = calculateRewardInternal(msg.sender, now);
        if (reward <= 0) {
            return false;
        }

        if (reward > MAX_TOTAL_SUPPLY.sub(totalSupply_)) {
            reward = MAX_TOTAL_SUPPLY.sub(totalSupply_);
        }

        totalSupply_ = totalSupply_.add(reward);
        balances[msg.sender] = balances[msg.sender].add(reward);
        emit Mint(msg.sender, reward);

        uint64 _now = uint64(now);
        delete coinAgeRecordMap[msg.sender];
        coinAgeRecordMap[msg.sender].push(CoinAgeRecord(balances[msg.sender], _now));
        emit CoinAgeResetEvent(msg.sender, balances[msg.sender], _now);

        return true;
    }

    /**
    * @dev Returns coinage for the caller address
    */
    function coinAge() public view returns (uint256) {
         return getCoinAgeInternal(msg.sender, now);
    }

    /**
    * @dev Returns current annual interest
    */
    function annualInterest() public view returns(uint256) {
        return getAnnualInterest(now);
    }

    /**
    * @dev Calculates and returns proof-of-stake reward
    */
    function calculateReward() public view returns (uint256) {
        return calculateRewardInternal(msg.sender, now);
    }

    /**
    * @dev Calculates and returns proof-of-stake reward for provided time
    *
    * @param _now timestamp The time for which the reward will be calculated
    */
    function calculateRewardAt(uint256 _now) public view returns (uint256) {
        return calculateRewardInternal(msg.sender, _now);
    }

    /**
    * @dev Returns coinage record for the given address and index
    *
    * @param _address address The address for which coinage record will be fetched
    * @param _index index The index of coinage record for that address
    */
    function coinAgeRecordForAddress(address _address, uint256 _index) public view onlyOwner returns (uint256, uint64) {
        if (coinAgeRecordMap[_address].length > _index) {
            return (coinAgeRecordMap[_address][_index].amount, coinAgeRecordMap[_address][_index].time);
        } else {
            return (0, 0);
        }
    }

    /**
    * @dev Returns coinage for the caller address
    *
    * @param _address address The address for which coinage will be calculated
    */
    function coinAgeForAddress(address _address) public view onlyOwner returns (uint256) {
         return getCoinAgeInternal(_address, now);
    }

    /**
    * @dev Returns coinage for the caller address
    *
    * @param _address address The address for which coinage will be calculated
    * @param _now timestamp The time for which the coinage will be calculated
    */
    function coinAgeForAddressAt(address _address, uint256 _now) public view onlyOwner returns (uint256) {
         return getCoinAgeInternal(_address, _now);
    }

    /**
    * @dev Calculates and returns proof-of-stake reward for provided address and time
    *
    * @param _address address The address for which reward will be calculated
    */
    function calculateRewardForAddress(address _address) public view onlyOwner returns (uint256) {
        return calculateRewardInternal(_address, now);
    }

    /**
    * @dev Calculates and returns proof-of-stake reward for provided address and time
    *
    * @param _address address The address for which reward will be calculated
    * @param _now timestamp The time for which the reward will be calculated
    */
    function calculateRewardForAddressAt(address _address, uint256 _now) public view onlyOwner returns (uint256) {
        return calculateRewardInternal(_address, _now);
    }

    /**
    * @dev Sets the stake start time
    */
    function startStakingAt(uint256 timestamp) public onlyOwner {
        require(stakeStartTime <= 0 && timestamp >= chainStartTime && timestamp > now);
        stakeStartTime = timestamp;
    }

    /**
    * @dev Returns true if the given _address is a contract, false otherwise.
    */
    function isContract(address _address) private view returns (bool) {
        uint256 length;
        assembly {
            //retrieve the size of the code on target address, this needs assembly
            length := extcodesize(_address)
        }
        return (length>0);
    }

    /**
    * @dev Logs coinage record for sender and receiver.
    *      Deletes sender's previous coinage records if any.
    *
    * @param _from address The address which you want to send tokens from
    * @param _to address The address which you want to transfer to
    * @param _value uint256 the amount of tokens to be transferred
    * @param _isContract bool if the receiver is a contract
    */
    function logCoinAgeRecord(address _from, address _to, uint256 _value, bool _isContract) private returns (bool) {
        if (coinAgeRecordMap[_from].length > 0) {
            delete coinAgeRecordMap[_from];
        }

        uint64 _now = uint64(now);

        if (balances[_from] != 0) {
            coinAgeRecordMap[_from].push(CoinAgeRecord(balances[_from], _now));
            emit CoinAgeResetEvent(_from, balances[_from], _now);
        }

        if (_value != 0 && !_isContract) {
            coinAgeRecordMap[_to].push(CoinAgeRecord(_value, _now));
            emit CoinAgeRecordEvent(_to, _value, _now);
        }

        return true;
    }

    /**
    * @dev Calculates and returns proof-of-stake reward for provided address
    *
    * @param _address address The address for which reward will be calculated
    * @param _now timestamp The time for which the reward will be calculated
    */
    function calculateRewardInternal(address _address, uint256 _now) private view returns (uint256) {
        uint256 _coinAge = getCoinAgeInternal(_address, _now);
        if (_coinAge <= 0) {
            return 0;
        }

        uint256 interest = getAnnualInterest(_now);

        return (_coinAge.mul(interest)).div(365 * 100);
    }

    /**
    * @dev Calculates the coin age for given address and time.
    *
    * @param _address address The address for which coinage will be calculated
    * @param _now timestamp The time for which the coinage will be calculated
    */
    function getCoinAgeInternal(address _address, uint256 _now) private view returns (uint256 _coinAge) {
        if (coinAgeRecordMap[_address].length <= 0) {
            return 0;
        }

        for (uint256 i = 0; i < coinAgeRecordMap[_address].length; i++) {
            if (_now < uint256(coinAgeRecordMap[_address][i].time).add(stakeMinAge)) {
                continue;
            }

            uint256 secondsPassed = _now.sub(uint256(coinAgeRecordMap[_address][i].time));
            if (secondsPassed > stakeMaxAge ) {
                secondsPassed = stakeMaxAge;
            }

            _coinAge = _coinAge.add((coinAgeRecordMap[_address][i].amount).mul(secondsPassed.div(1 days)));
        }
    }

    /**
    * @dev Returns the annual interest rate for given time
    *
    * @param _now timestamp The time for which the annual interest will be calculated
    */
    function getAnnualInterest(uint256 _now) private view returns(uint256 interest) {
        if (stakeStartTime > 0 && _now >= stakeStartTime && totalSupply_ < MAX_TOTAL_SUPPLY) {
            uint256 secondsPassed = _now.sub(stakeStartTime);
            // 1st Year = 30% annually
            if (secondsPassed <= 365 days) {
                interest = 30;
            } else if (secondsPassed <= 547 days) {  // 2nd Year, 1st Half = 25% annually
                interest = 25;
            } else if (secondsPassed <= 730 days) {  // 2nd Year, 2nd Half = 20% annually
                interest = 20;
            } else if (secondsPassed <= 911 days) {  // 3rd Year, 1st Half = 15% annually
                interest = 15;
            } else if (secondsPassed <= 1094 days) {  // 3rd Year, 2nd Half = 10% annually
                interest = 10;
            } else {  // 4th Year Onwards = 5% annually
                interest = 5;
            }
        } else {
            interest = 0;
        }
    }
}
/**
 * @title TRUE Token
 * @dev ERC20, ERC223, PoS Token for TrueDeck Platform
 */
function allowance(address _owner, address _spender) public view returns (uint256) {
    return allowed[_owner][_spender];
}

/**
 * @dev Function to check the amount of tokens that an owner allowed to a spender.
 * @param _owner address The address which owns the funds.
 * @param _spender address The address which will spend the funds.
 * @return A uint256 specifying the amount of tokens still available for the spender.
 */