contract FiduciaToken is StandardToken {
    string public constant name = "Fiducia";
    string public constant symbol = "FDO";
    uint256 public constant decimals = 18;

    /// The owner of this address manages the token sale process.
    address public owner;

    /// The owner of this address will manage the sale process.
    address public saleTeamAddress;

    // this is the address of the timelock contract for the team tokens
    address public timelockContractAddress;

    uint64 contractCreatedDatetime;

    bool public tokenSaleClosed = false;

    /// Minimum amount of tokens to be sold for the sale to succeed.
    uint256 public constant GOAL = 20 * 5000 * 10**decimals;

    /// Maximum tokens to be allocated.
    uint256 public constant TOKENS_HARD_CAP = 2 * 100000 * 5000 * 10**decimals;

    /// Maximum amount of tokens to be sold for the sale to succeed.
    uint256 public constant TOKENS_SALE_HARD_CAP = 60000 * 5000 * 10**decimals;

    /// Issue event index starting from 0.
    uint256 public issueIndex = 0;

    /// Emitted for each successful token purchase.
    event Issue(uint _issueIndex, address addr, uint tokenAmount);

    event SaleSucceeded();

    event SaleFailed();

    modifier onlyOwner {
        assert(msg.sender == owner);
        _;
    }

    modifier onlyTeam {
        assert(msg.sender == saleTeamAddress || msg.sender == owner);
        _;
    }

    modifier inProgress {
        assert(!saleHardCapReached() && !tokenSaleClosed);
        _;
    }

    modifier beforeEnd {
        assert(!tokenSaleClosed);
        _;
    }

    function FiduciaToken(address _saleTeamAddress) public {
        require(_saleTeamAddress != address(0));
        owner = msg.sender;
        saleTeamAddress = _saleTeamAddress;
        contractCreatedDatetime = uint64(block.timestamp);
    }

    function close(uint256 _fiduciaTeamTokens) public onlyOwner beforeEnd {
        if (totalSupply < GOAL) {
            SaleFailed();
        } else {
            SaleSucceeded();
        }

        // compute without actually increasing it
        uint256 increasedTotalSupply = totalSupply.add(_fiduciaTeamTokens);
        // roll back if hard cap reached
        if(increasedTotalSupply > TOKENS_HARD_CAP) {
            revert();
        }

        // increase token total supply
        totalSupply = increasedTotalSupply;

        // locked for 2 days after the contract creation
        TokenTimelock lockedTeamTokens = new TokenTimelock(this, owner, contractCreatedDatetime + (60 * 60 * 24 * 2));
        // or 10 days from this moment of the close() call
        // TokenTimelock lockedTeamTokens = new TokenTimelock(this, owner, now + (60 * 60 * 24 * 10));

        timelockContractAddress = address(lockedTeamTokens);

        //update the investors balance to number of tokens sent
        balances[timelockContractAddress] = balances[timelockContractAddress].add(_fiduciaTeamTokens);

        //event is fired when tokens issued
        Issue(
        issueIndex++,
        timelockContractAddress,
        _fiduciaTeamTokens
        );

        tokenSaleClosed = true;
    }

    function issueTokens(address _investor, uint256 _tokensAmount) public onlyTeam inProgress {
        require(_investor != address(0));

        // compute without actually increasing it
        uint256 increasedTotalSupply = totalSupply.add(_tokensAmount);
        // roll back if hard cap reached
        if(increasedTotalSupply > TOKENS_SALE_HARD_CAP) {
            revert();
        }

        //increase token total supply
        totalSupply = increasedTotalSupply;
        //update the investors balance to number of tokens sent
        balances[_investor] = balances[_investor].add(_tokensAmount);
        //event is fired when tokens issued
        Issue(
        issueIndex++,
        _investor,
        _tokensAmount
        );
    }

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return balances[_owner];
    }

    /// @return true if the hard cap is reached.
    function saleHardCapReached() public view returns (bool) {
        return totalSupply >= TOKENS_SALE_HARD_CAP;
    }
}
function saleHardCapReached() public view returns (bool) {
    return totalSupply >= TOKENS_SALE_HARD_CAP;
}

/// @return true if the hard cap is reached.