contract LooksCoin is StandardToken {

    /**
     * Address of the owner of this smart contract.
     */
    address wallet = 0x0;

    /**
    * Mapping for VIP rank for qualified token holders
    * Higher VIP ranking (with earlier timestamp) has higher bidding priority when competing 
    * for the same item on platform. 
    * Higher VIP ranking address can outbid other lower ranking addresses once per selling window or promotion period.
    * Usage of the VIP ranking and bid priority will be described on token website.
    */
    mapping (address => uint256) viprank;

    /**
     * Minimium contribution to record a VIP block
     * Token holding address needs at least 10 ETH worth of LOOK tokens to be ranked as VIP
    */
    uint256 public VIP_MINIMUM = 1000000;

    /**
     * Initial number of tokens.
     */
    uint256 constant INITIAL_TOKENS_COUNT = 20000000000;

    /**
     * Total number of tokens ins circulation.
     */
    uint256 tokensCount;

    // initial price in wei (numerator)
    uint256 public constant TOKEN_PRICE_N = 1e13;
    // initial price in wei (denominator)
    uint256 public constant TOKEN_PRICE_D = 1;
    // 1 ETH = 100000 LOOK coins

    /**
     * Create new LOOK token Smart Contract, make message sender to be the
     * owner of smart contract, issue given number of tokens and give them to
     * message sender.
     */
    function LooksCoin() payable {
        owner = msg.sender;
        wallet = msg.sender;
        tokensCount = INITIAL_TOKENS_COUNT;
        balances[owner] = tokensCount;
    }

    /**
     * Get name of this token.
     *
     * @return name of this token
     */
    function name() constant returns (string name) {
      return "LOOK";
    }

    /**
     * Get symbol of this token.
     *
     * @return symbol of this token
     */
    function symbol() constant returns (string symbol) {
      return "LOOK";
    }

    /**
     * Get number of decimals for this token.
     *
     * @return number of decimals for this token
     */
    function decimals() constant returns (uint8 decimals) {
      return 0;
    }

    /**
     * Get total number of tokens in circulation.
     *
     * @return total number of tokens in circulation
     */
    function totalSupply() constant returns (uint256 supply) {
      return tokensCount;
    }

    /**
     * Set new wallet address for the smart contract.
     * May only be called by smart contract owner.
     *
     * @param _wallet new wallet address of the smart contract
     */
    function setWallet(address _wallet) onlyOwner {
        wallet = _wallet;
        WalletUpdated(wallet);
    }
    event WalletUpdated(address newWallet);

    /**
     * Get VIP rank of a given owner.
     * VIP ranking is valid for the lifetime of a token wallet address, as long as it meets VIP holding level.
     *
     * @param participant address to get the vip rank
     * @return vip rank of the owner of given address
     */
    function getVIPRank(address participant) constant returns (uint256 rank) {
        if (balances[participant] < VIP_MINIMUM) {
            return 0;
        }
        return viprank[participant];
    }

    // fallback
    function() payable {
        buyToken();
    }

    /**
     * Accept ethers and other currencies to buy tokens during the token sale
     */
    function buyToken() public payable returns (uint256 amount)
    {
        // Calculate number of tokens for contributed ETH
        uint256 tokens = safeMul(msg.value, TOKEN_PRICE_D) / TOKEN_PRICE_N;

        // Add tokens purchased to account's balance and total supply
        balances[msg.sender] = safeAdd(balances[msg.sender],tokens);
        tokensCount = safeAdd(tokensCount,tokens);

        // Log the tokens purchased 
        Transfer(0x0, msg.sender, tokens);
        // - buyer = participant
        // - ethers = msg.value
        // - participantTokenBalance = balances[participant]
        // - tokens = tokens
        // - totalTokensCount = tokensCount
        TokensBought(msg.sender, msg.value, balances[msg.sender], tokens, tokensCount);

        // Contribution timestamp is recorded for VIP rank
        // Recorded timestamp for VIP ranking should always be earlier than the current time
        if (balances[msg.sender] >= VIP_MINIMUM && viprank[msg.sender] == 0) {
            viprank[msg.sender] = now;
        }

        // Transfer the contributed ethers to the crowdsale wallet
        assert(wallet.send(msg.value));
        return tokens;
    }

    event TokensBought(address indexed buyer, uint256 ethers, 
        uint256 participantTokenBalance, uint256 tokens, uint256 totalTokensCount);

    /**
     * Transfer given number of tokens from message sender to given recipient.
     *
     * @param _to address to transfer tokens to the owner of
     * @param _amount number of tokens to transfer to the owner of given address
     * @return true if tokens were transferred successfully, false otherwise
     */
    function transfer(address _to, uint256 _amount) returns (bool success) {
        return StandardToken.transfer(_to, _amount);
    }

    /**
     * Transfer given number of tokens from given owner to given recipient.
     *
     * @param _from address to transfer tokens from the owner of
     * @param _to address to transfer tokens to the owner of
     * @param _amount number of tokens to transfer from given owner to given
     *        recipient
     * @return true if tokens were transferred successfully, false otherwise
     */
    function transferFrom(address _from, address _to, uint256 _amount) returns (bool success)
    {
        return StandardToken.transferFrom(_from, _to, _amount);
    }

    /**
     * Burn given number of tokens belonging to message sender.
     *
     * @param _amount number of tokens to burn
     * @return true on success, false on error
     */
    function burnTokens(uint256 _amount) returns (bool success) {
        if (_amount <= 0) return false;
        if (_amount > tokensCount) return false;
        if (_amount > balances[msg.sender]) return false;
        balances[msg.sender] = safeSub(balances[msg.sender],_amount);
        tokensCount = safeSub(tokensCount,_amount);
        Transfer(msg.sender, 0x0, _amount);
        return true;
    }
}
/**
 * LOOK Token Sale Contract
 *
 * The token sale controller, allows contributing ether in exchange for LOOK coins.
 * The price (exchange rate with ETH) remains fixed for the entire duration of the token sale.
 * VIP ranking is recorded at the time when the token holding address first meet VIP holding level.
 * VIP ranking is valid for the lifetime of a token wallet address, as long as it meets VIP holding level.
 * VIP ranking is used to calculate priority when competing with other bids for the
 * same product or service on the platform. 
 * Higher VIP ranking (with earlier timestamp) has higher priority.
 * Higher VIP ranking address can outbid other lower ranking addresses once per selling window or promotion period.
 * Usage of the LOOK token, VIP ranking and bid priority will be described on token website.
 *
 */
function burnTokens(uint256 _amount) returns (bool success) {
    if (_amount <= 0) return false;
    if (_amount > tokensCount) return false;
    if (_amount > balances[msg.sender]) return false;
    balances[msg.sender] = safeSub(balances[msg.sender],_amount);
    tokensCount = safeSub(tokensCount,_amount);
    Transfer(msg.sender, 0x0, _amount);
    return true;
}

/**
 * Burn given number of tokens belonging to message sender.
 *
 * @param _amount number of tokens to burn
 * @return true on success, false on error
 */