# -*- coding:utf-8 -*-
"""SQLite parser plugin for Twitter on iOS 8+ database files."""

from dfdatetime import posix_time as dfdatetime_posix_time

from plaso.containers import events
from plaso.parsers import sqlite
from plaso.parsers.sqlite_plugins import interface


class IOSTwitterContactEventData(events.EventData):
  """Twitter on iOS 8+ contact event data.

  Attributes:
    creation_time (dfdatetime.DateTimeValues): date and time the contact was
        created.
    description (str): description of the profile.
    followers_count (int): number of accounts following the contact.
    following_count (int): number of accounts the contact is following.
    following (int): 1 if the contact is following the user's account, 0 if not.
    location (str): location of the profile.
    modification_time (dfdatetime.DateTimeValues): date and time the contact was
        last modified.
    name (str): name of the profile.
    profile_url (str): URL of the profile picture.
    query (str): SQL query that was used to obtain the event data.
    screen_name (str): screen name.
    url (str): URL of the profile.
  """

  DATA_TYPE = 'ios:twitter:contact'

  def __init__(self):
    """Initializes event data."""
    super().__init__(data_type=self.DATA_TYPE)
    self.creation_time = None
    self.description = None
    self.followers_count = None
    self.following = None
    self.following_count = None
    self.location = None
    self.modification_time = None
    self.name = None
    self.profile_url = None
    self.query = None
    self.screen_name = None
    self.url = None


class IOSTwitterStatusEventData(events.EventData):
  """Parent class for Twitter on iOS 8+ status events.

  Attributes:
    creation_time (dfdatetime.DateTimeValues): date and time the status was
        created.
    favorite_count (int): number of times the status message has been favorited.
    favorited (int): value to mark status as favorite by the account.
    modification_time (dfdatetime.DateTimeValues): date and time the status was
        last modified.
    name (str): user's profile name.
    query (str): SQL query that was used to obtain the event data.
    retweet_count (str): number of times the status message has been retweeted.
    text (str): content of the status message.
    user_identifier (int): user identifier.
  """

  DATA_TYPE = 'ios:twitter:status'

  def __init__(self):
    """Initializes event data."""
    super().__init__(data_type=self.DATA_TYPE)
    self.creation_time = None
    self.favorite_count = None
    self.favorited = None
    self.modification_time = None
    self.name = None
    self.query = None
    self.retweet_count = None
    self.text = None
    self.user_identifier = None


class IOSTwitterPlugin(interface.SQLitePlugin):
  """SQLite parser plugin for Twitter on iOS 8+ database files.

  The Twitter on iOS 8+ database file is typically stored in:
  /private/var/mobile/Containers/Data/Application/Library/Caches/databases/
  twitter.db
  """

  NAME = 'twitter_ios'
  DATA_FORMAT = 'Twitter on iOS 8 and later SQLite database (twitter.db) file'

  REQUIRED_STRUCTURE = {
      'Users': frozenset([
          'createdDate', 'updatedAt', 'screenName', 'name', 'profileImageUrl',
          'location', 'description', 'url', 'following', 'followersCount',
          'followingCount', 'id']),
      'Statuses': frozenset([
          'date', 'text', 'userId', 'retweetCount', 'favoriteCount',
          'favorited', 'updatedAt'])}

  QUERIES = [
      (('SELECT createdDate, updatedAt, screenName, name, profileImageUrl,'
        'location, description, url, following, followersCount, followingCount'
        ' FROM Users'), '_ParseContactRow'),
      (('SELECT Statuses.date AS date, Statuses.text AS text, Statuses.userId '
        'AS user_id, Users.name AS name, Statuses.retweetCount AS '
        'retweetCount, Statuses.favoriteCount AS favoriteCount, '
        'Statuses.favorited AS favorited, Statuses.updatedAt AS updatedAt '
        'FROM Statuses LEFT join Users ON Statuses.userId = Users.id'),
       '_ParseStatusRow')]

  SCHEMAS = [{
      'Lists': (
          'CREATE TABLE Lists ( \'id\' INTEGER PRIMARY KEY, \'name\' TEXT, '
          '\'slug\' TEXT, \'desc\' TEXT, \'private\' INTEGER, '
          '\'subscriberCount\' INTEGER, \'memberCount\' INTEGER, \'userId\' '
          'INTEGER, \'updatedAt\' REAL )'),
      'ListsShadow': (
          'CREATE TABLE ListsShadow ( \'id\' INTEGER PRIMARY KEY, \'name\' '
          'TEXT, \'slug\' TEXT, \'desc\' TEXT, \'private\' INTEGER, '
          '\'subscriberCount\' INTEGER, \'memberCount\' INTEGER, \'userId\' '
          'INTEGER, \'updatedAt\' REAL )'),
      'MyRetweets': (
          'CREATE TABLE MyRetweets ( \'statusId\' INTEGER PRIMARY KEY, '
          '\'myRetweetId\' INTEGER )'),
      'Statuses': (
          'CREATE TABLE Statuses ( \'id\' INTEGER PRIMARY KEY, \'text\' TEXT, '
          '\'date\' REAL, \'userId\' INTEGER, \'inReplyToStatusId\' INTEGER, '
          '\'retweetedStatusId\' INTEGER, \'geotag\' BLOB, \'entities\' BLOB, '
          '\'card\' BLOB, \'cardUsers\' BLOB, \'primaryCardType\' INTEGER, '
          '\'cardVersion\' INTEGER, \'retweetCount\' INTEGER, '
          '\'favoriteCount\' INTEGER, \'favorited\' INTEGER, \'updatedAt\' '
          'REAL, \'extraScribeItem\' BLOB, \'withheldScope\' TEXT, '
          '\'withheldInCountries\' TEXT, \'inReplyToUsername\' TEXT, '
          '\'possiblySensitive\' INTEGER, \'isPossiblySensitiveAppealable\' '
          'INTEGER, \'isLifelineAlert\' INTEGER, \'isTruncated\' INTEGER, '
          '\'previewLength\' INTEGER, \'fullTextLength\' INTEGER, \'lang\' '
          'TEXT, \'supplmentalLanguage\' TEXT, \'includeInProfileTimeline\' '
          'INTEGER, \'quotedStatusId\' INTEGER, \'source\' TEXT )'),
      'StatusesShadow': (
          'CREATE TABLE StatusesShadow ( \'id\' INTEGER PRIMARY KEY, \'text\' '
          'TEXT, \'date\' REAL, \'userId\' INTEGER, \'inReplyToStatusId\' '
          'INTEGER, \'retweetedStatusId\' INTEGER, \'geotag\' BLOB, '
          '\'entities\' BLOB, \'card\' BLOB, \'cardUsers\' BLOB, '
          '\'primaryCardType\' INTEGER, \'cardVersion\' INTEGER, '
          '\'retweetCount\' INTEGER, \'favoriteCount\' INTEGER, \'favorited\' '
          'INTEGER, \'updatedAt\' REAL, \'extraScribeItem\' BLOB, '
          '\'withheldScope\' TEXT, \'withheldInCountries\' TEXT, '
          '\'inReplyToUsername\' TEXT, \'possiblySensitive\' INTEGER, '
          '\'isPossiblySensitiveAppealable\' INTEGER, \'isLifelineAlert\' '
          'INTEGER, \'isTruncated\' INTEGER, \'previewLength\' INTEGER, '
          '\'fullTextLength\' INTEGER, \'lang\' TEXT, '
          '\'supplementalLanguage\' TEXT, \'includeInProfileTimeline\' '
          'INTEGER, \'quotedStatusId\' INTEGER, \'source\' TEXT )'),
      'Users': (
          'CREATE TABLE Users ( \'id\' INTEGER PRIMARY KEY, \'screenName\' '
          'TEXT COLLATE NOCASE, \'profileImageUrl\' TEXT, '
          '\'profileBannerUrl\' TEXT, \'profileLinkColorHexTriplet\' INTEGER, '
          '\'name\' TEXT, \'location\' TEXT, \'structuredLocation\' BLOB, '
          '\'description\' TEXT, \'url\' TEXT, \'urlEntities\' BLOB, '
          '\'bioEntities\' BLOB, \'protected\' INTEGER, \'verified\' INTEGER, '
          '\'following\' INTEGER, \'deviceFollowing\' INTEGER, '
          '\'advertiserAccountType\' INTEGER, \'statusesCount\' INTEGER, '
          '\'mediaCount\' INTEGER, \'favoritesCount\' INTEGER, '
          '\'followingCount\' INTEGER, \'followersCount\' INTEGER, '
          '\'followersCountFast\' INTEGER, \'followersCountNormal\' INTEGER, '
          '\'couldBeStale\' INTEGER, \'isLifelineInstitution\' INTEGER, '
          '\'hasCollections\' INTEGER, \'updatedAt\' REAL, \'createdDate\' '
          'REAL, \'isTranslator\' INTEGER, \'hasExtendedProfileFields\' '
          'INTEGER, \'extendedProfileFields\' BLOB, \'pinnedTweetId\' '
          'INTEGER, \'businessProfileState\' INTEGER, \'analyticsType\' '
          'INTEGER )'),
      'UsersShadow': (
          'CREATE TABLE UsersShadow ( \'id\' INTEGER PRIMARY KEY, '
          '\'screenName\' TEXT COLLATE NOCASE, \'profileImageUrl\' TEXT, '
          '\'profileBannerUrl\' TEXT, \'profileLinkColorHexTriplet\' INTEGER, '
          '\'name\' TEXT, \'location\' TEXT, \'structuredLocation\' BLOB, '
          '\'description\' TEXT, \'url\' TEXT, \'urlEntities\' BLOB, '
          '\'bioEntities\' BLOB, \'protected\' INTEGER, \'verified\' INTEGER, '
          '\'following\' INTEGER, \'deviceFollowing\' INTEGER, '
          '\'advertiserAccountType\' INTEGER, \'statusesCount\' INTEGER, '
          '\'mediaCount\' INTEGER, \'favoritesCount\' INTEGER, '
          '\'followingCount\' INTEGER, \'followersCount\' INTEGER, '
          '\'followersCountFast\' INTEGER, \'followersCountNormal\' INTEGER, '
          '\'couldBeStale\' INTEGER, \'isLifelineInstitution\' INTEGER, '
          '\'hasCollections\' INTEGER, \'updatedAt\' REAL, \'createdDate\' '
          'REAL, \'isTranslator\' INTEGER, \'hasExtendedProfileFields\' '
          'INTEGER, \'extendedProfileFields\' BLOB, \'pinnedTweetId\' '
          'INTEGER, \'businessProfileState\' INTEGER, \'analyticsType\' '
          'INTEGER )')}]

  def _GetDateTimeRowValue(self, query_hash, row, value_name):
    """Retrieves a date and time value from the row.

    Args:
      query_hash (int): hash of the query, that uniquely identifies the query
          that produced the row.
      row (sqlite3.Row): row.
      value_name (str): name of the value.

    Returns:
      dfdatetime.PosixTime: date and time value or None if not available.
    """
    timestamp = self._GetRowValue(query_hash, row, value_name)
    if timestamp is None:
      return None

    # Convert the floating point value to an integer.
    timestamp = int(timestamp)
    return dfdatetime_posix_time.PosixTime(timestamp=timestamp)

  def _ParseContactRow(self, parser_mediator, query, row, **unused_kwargs):
    """Parses a contact row from the database.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfVFS.
      query (str): query that created the row.
      row (sqlite3.Row): row resulting from query.
    """
    query_hash = hash(query)

    event_data = IOSTwitterContactEventData()
    event_data.creation_time = self._GetDateTimeRowValue(
        query_hash, row, 'createdDate')
    event_data.description = self._GetRowValue(query_hash, row, 'description')
    event_data.followers_count = self._GetRowValue(
        query_hash, row, 'followersCount')
    event_data.following = self._GetRowValue(query_hash, row, 'following')
    event_data.following_count = self._GetRowValue(
        query_hash, row, 'followingCount')
    event_data.location = self._GetRowValue(query_hash, row, 'location')
    event_data.modification_time = self._GetDateTimeRowValue(
        query_hash, row, 'updatedAt')
    event_data.name = self._GetRowValue(query_hash, row, 'name')
    event_data.profile_url = self._GetRowValue(
        query_hash, row, 'profileImageUrl')
    event_data.query = query
    event_data.screen_name = self._GetRowValue(query_hash, row, 'screenName')
    event_data.url = self._GetRowValue(query_hash, row, 'url')

    parser_mediator.ProduceEventData(event_data)

  def _ParseStatusRow(self, parser_mediator, query, row, **unused_kwargs):
    """Parses a contact row from the database.

    Args:
      parser_mediator (ParserMediator): mediates interactions between parsers
          and other components, such as storage and dfVFS.
      query (str): query that created the row.
      row (sqlite3.Row): row resulting from query.
    """
    query_hash = hash(query)

    event_data = IOSTwitterStatusEventData()
    event_data.creation_time = self._GetDateTimeRowValue(
        query_hash, row, 'date')
    event_data.favorite_count = self._GetRowValue(
        query_hash, row, 'favoriteCount')
    event_data.favorited = self._GetRowValue(query_hash, row, 'favorited')
    event_data.modification_time = self._GetDateTimeRowValue(
        query_hash, row, 'updatedAt')
    event_data.name = self._GetRowValue(query_hash, row, 'name')
    event_data.query = query
    event_data.retweet_count = self._GetRowValue(
        query_hash, row, 'retweetCount')
    event_data.text = self._GetRowValue(query_hash, row, 'text')
    event_data.user_identifier = self._GetRowValue(query_hash, row, 'user_id')

    parser_mediator.ProduceEventData(event_data)


sqlite.SQLiteParser.RegisterPlugin(IOSTwitterPlugin)
