contract LCDToken is StandardToken {

    // Token metadata
    string public constant name = "Lucyd";
    string public constant symbol = "LCD";
    uint256 public constant decimals = 18;

    uint256 public constant TOKEN_COMPANY_OWNED = 10 * (10**6) * 10**decimals; // 10 million LCDs
    uint256 public constant TOKEN_MINTING = 30 * (10**6) * 10**decimals;       // 30 million LCDs
    uint256 public constant TOKEN_BUSINESS = 10 * (10**6) * 10**decimals;       // 10 million LCDs

    // wallet that is allowed to distribute tokens on behalf of the app store
    address public APP_STORE;

    // Administrator for multi-sig mechanism
    address public admin1;
    address public admin2;

    // Accounts that are allowed to deliver tokens
    address public tokenVendor1;
    address public tokenVendor2;

    // Keep track of holders and icoBuyers
    mapping (address => bool) public isHolder; // track if a user is a known token holder to the smart contract - important for payouts later
    address[] public holders;                  // array of all known holders - important for payouts later

    // store the hashes of admins' msg.data
    mapping (address => bytes32) private multiSigHashes;

    // to track if management already got their tokens
    bool public managementTokensDelivered;

    // current amount of disbursed tokens
    uint256 public tokensSold;

    // Events used for logging
    event LogLCDTokensDelivered(address indexed _to, uint256 _value);
    event LogManagementTokensDelivered(address indexed distributor, uint256 _value);
    event Auth(string indexed authString, address indexed user);

    modifier onlyOwner() {
        // check if transaction sender is admin.
        require (msg.sender == admin1 || msg.sender == admin2);
        // if yes, store his msg.data. 
        multiSigHashes[msg.sender] = keccak256(msg.data);
        // check if his stored msg.data hash equals to the one of the other admin
        if ((multiSigHashes[admin1]) == (multiSigHashes[admin2])) {
            // if yes, both admins agreed - continue.
            _;

            // Reset hashes after successful execution
            multiSigHashes[admin1] = 0x0;
            multiSigHashes[admin2] = 0x0;
        } else {
            // if not (yet), return.
            return;
        }
    }

    modifier onlyVendor() {
        require((msg.sender == tokenVendor1) || (msg.sender == tokenVendor2));
        _;
    }

    /**
     * @dev Create a new LCDToken contract.
     *
     *  _admin1 The first admin account that owns this contract.
     *  _admin2 The second admin account that owns this contract.
     *  _tokenVendor1 The first token vendor
     *  _tokenVendor2 The second token vendor
     */
    function LCDToken(
        address _admin1,
        address _admin2,
        address _tokenVendor1,
        address _tokenVendor2,
        address _appStore,
        address _business_development)
    public
    {
        // admin1 and admin2 address must be set and must be different
        require (_admin1 != 0x0);
        require (_admin2 != 0x0);
        require (_admin1 != _admin2);

        // tokenVendor1 and tokenVendor2 must be set and must be different
        require (_tokenVendor1 != 0x0);
        require (_tokenVendor2 != 0x0);
        require (_tokenVendor1 != _tokenVendor2);

        // tokenVendors must be different from admins
        require (_tokenVendor1 != _admin1);
        require (_tokenVendor1 != _admin2);
        require (_tokenVendor2 != _admin1);
        require (_tokenVendor2 != _admin2);
        require (_appStore != 0x0);

        admin1 = _admin1;
        admin2 = _admin2;
        tokenVendor1 = _tokenVendor1;
        tokenVendor2 = _tokenVendor2;

        // Init app store balance
        APP_STORE = _appStore;
        balances[_appStore] = TOKEN_MINTING;
        trackHolder(_appStore);

        // Init business development balance to admin1 
        balances[_admin1] = TOKEN_BUSINESS;
        trackHolder(_business_development);

        totalSupply = SafeMath.add(TOKEN_MINTING, TOKEN_BUSINESS);
    }

    // Allows to figure out the amount of known token holders
    function getHolderCount()
    public
    constant
    returns (uint256 _holderCount)
    {
        return holders.length;
    }

    // Allows for easier retrieval of holder by array index
    function getHolder(uint256 _index)
    public
    constant
    returns (address _holder)
    {
        return holders[_index];
    }

    function trackHolder(address _to)
    private
    returns (bool success)
    {
        // Check if the recipient is a known token holder
        if (isHolder[_to] == false) {
            // if not, add him to the holders array and mark him as a known holder
            holders.push(_to);
            isHolder[_to] = true;
        }
        return true;
    }

    /// @dev Transfer LCD tokens
    function deliverTokens(address _buyer, uint256 _amount) // amount input will  be in cents
    external
    onlyVendor
    returns(bool success)
    {
        // check if the function is called before May 1, 2018
        require(block.timestamp <= 1525125600);

        // Calculate the number of tokens from the given amount in cents
        uint256 tokens = SafeMath.mul(_amount, 10**decimals / 100);

        // update state
        uint256 oldBalance = balances[_buyer];
        balances[_buyer] = SafeMath.add(oldBalance, tokens);
        tokensSold = SafeMath.add(tokensSold, tokens);
        totalSupply = SafeMath.add(totalSupply, tokens);
        trackHolder(_buyer);

        // Log the transfer of these tokens
        Transfer(msg.sender, _buyer, tokens);
        LogLCDTokensDelivered(_buyer, tokens);
        return true;
    }

    // @dev Transfer tokens to management wallet
    function deliverManagementTokens(address _managementWallet)
    external
    onlyOwner
    returns (bool success)
    {
        // check if management tokens are already unlocked, if the function is called after March 31., 2019
        require(block.timestamp >= 1553990400);

        // Deliver management tokens only once
        require(managementTokensDelivered == false);

        // update state
        balances[_managementWallet] = TOKEN_COMPANY_OWNED;
        totalSupply = SafeMath.add(totalSupply, TOKEN_COMPANY_OWNED);
        managementTokensDelivered = true;
        trackHolder(_managementWallet);

        // Log the transfer of these tokens
        Transfer(address(this), _managementWallet, TOKEN_COMPANY_OWNED);
        LogManagementTokensDelivered(_managementWallet, TOKEN_COMPANY_OWNED);
        return true;
    }

    // Using this for creating a reference between ETH wallets and accounts in the Lucyd backend
    function auth(string _authString)
    external
    {
        Auth(_authString, msg.sender);
    }
}
/*is StandardToken */
function deliverTokens(address _buyer, uint256 _amount) // amount input will  be in cents
    external
    onlyVendor
    returns(bool success)
    {
        // check if the function is called before May 1, 2018
        require(block.timestamp <= 1525125600);

        // Calculate the number of tokens from the given amount in cents
        uint256 tokens = SafeMath.mul(_amount, 10**decimals / 100);

        // update state
        uint256 oldBalance = balances[_buyer];
        balances[_buyer] = SafeMath.add(oldBalance, tokens);
        tokensSold = SafeMath.add(tokensSold, tokens);
        totalSupply = SafeMath.add(totalSupply, tokens);
        trackHolder(_buyer);

        // Log the transfer of these tokens
        Transfer(msg.sender, _buyer, tokens);
        LogLCDTokensDelivered(_buyer, tokens);
        return true;
    }
/// @dev Transfer LCD tokens