"""Wrapper module for a database."""

from __future__ import print_function

from datetime import datetime
import json

import peewee

from beerlog import errors
from beerlog import constants


database_proxy = peewee.Proxy()
# pylint: disable=no-init
class BeerModel(peewee.Model):
  """Model for the database."""
  class Meta():
    """Sets Metadata for the database."""
    database = database_proxy


def BeerPerCharacter(character, amount):
  """Helper function to generate the SQL expression for the total amount
  of beer drunk."""
  return peewee.Expression(character.glass, '*', amount)


class Character(BeerModel):
  """class for one Character in the BeerLog database."""
  hexid = peewee.CharField()
  glass = peewee.IntegerField(default=constants.DEFAULT_GLASS_SIZE)

  @property
  def name(self):
    """Gets the corresponding name from hexid using the parent db object
    method."""
    return self._meta.database.GetNameFromHexID(self.hexid)

  def GetAmountDrunk(self):
    """Gets the amount of beer drunk."""
    return self._meta.database.GetAmountFromHexID(self.hexid, self.glass)


class Entry(BeerModel):
  """class for one Entry in the BeerLog database."""
  character = peewee.ForeignKeyField(Character, backref='entries')
  timestamp = peewee.DateTimeField(default=datetime.now)
  pic = peewee.CharField(null=True)

class BeerLogDB():
  """Wrapper for the database."""

  def __init__(self, database_path):
    self.database_path = database_path
    sqlite_db = peewee.SqliteDatabase(self.database_path)
    database_proxy.initialize(sqlite_db)

    # This is used for the Character.name property
    sqlite_db.GetNameFromHexID = self.GetNameFromHexID
    # This is used for the Character.GetAmountDrink() method
    sqlite_db.GetAmountFromHexID = self.GetAmountFromHexID

    sqlite_db.create_tables([Character, Entry], safe=True)

    self.known_tags_list = None

  def Connect(self):
    """Connects to the database."""
    database_proxy.connect()

  def Close(self):
    """Closes the database."""
    database_proxy.close()

  def AddEntry(self, character_hexid, pic, time=None):
    """Inserts an entry in the database.

    Args:
      character_hexid(str): the hexid of the character in the tag.
      pic(str): the path to a picture.
      time(datetime): the optional time to set when adding the Entry.

    Returns:
      Entry: the Entry that was stored in the database.
    """
    glass = self.GetGlassFromHexID(character_hexid)
    character, _ = Character.get_or_create(hexid=character_hexid, glass=glass)
    if time:
      entry = Entry.create(
          character=character,
          timestamp=time,
          pic=pic
      )
    else:
      entry = Entry.create(
          character=character,
          pic=pic
      )
    return entry

  def GetCharacterFromHexID(self, character_hexid):
    """Returns a Character from its hexid.

    Args:
      character_hexid(str): the character's hexid.
    Returns:
      Character: a Character object, or None.
    """
    return Character.get_or_none(Character.hexid == character_hexid)

  def GetEntryById(self, entry_id):
    """Returns an Entry by its primary key.

    Args:
      entry_id(int): the id we're looking for.

    Returns:
      Entry: the corresponding Entry, or None if it wasn't found.
    """
    try:
      entry = Entry.get(Entry.id == entry_id)
    except peewee.DoesNotExist as _:
      return None
    return entry

  def CountAll(self):
    """Returns the number of Entry

    Returns:
      int: the total number of Entry lines in the database.
    """
    return Entry.select().count(None)

  def GetScoreBoard(self):
    """Returns a query with the scoreboard.

    Returns:
      peewee.ModelSelect: the query.
    """
    query = Entry.select(
        Entry,
        Character,
        peewee.fn.MAX(Entry.timestamp).alias('last'),
        BeerPerCharacter(Character, peewee.fn.COUNT()).alias('amount')
    ).join(Character).group_by(Entry.character).order_by(
        BeerPerCharacter(Character, peewee.fn.COUNT()).desc(),
        (peewee.fn.MAX(Entry.timestamp)).desc()
    )

    return query

  def GetGlassFromHexID(self, uid):
    """Returns the corresponding glass from a uid

    Args:
      uid(str): the uid in form 0x0580000000050002
    Returns:
      int: the corresponding glass for that tag uid, or None if no name is found.
    """
    tag_object = self.known_tags_list.get(uid)
    if not tag_object:
      return None

    return tag_object.get('glass')

  def LoadTagsDB(self, known_tags_path):
    """Loads the external known tags list.

    Args:
      known_tags_path(str): path to the known_tags.json file.
    Raises:
      errors.BeerLogError: if we couldn't load the file.
    """
    try:
      with open(known_tags_path, 'r') as json_file:
        self.known_tags_list = json.load(json_file)
    except IOError as e:
      raise errors.BeerLogError(
          'Could not load known tags file {0} with error {1!s}'.format(
              known_tags_path, e))
    except ValueError as e:
      raise errors.BeerLogError(
          'Known tags file {0} is invalid: {1!s}'.format(
              known_tags_path, e))

  def GetNameFromHexID(self, uid):
    """Returns the corresponding name from a uid
    Entry.where()

    Args:
      uid(str): the uid in form 0x0580000000050002
    Returns:
      str: the corresponding name for that tag uid, or None if no name is found.
    """
    tag_object = self.known_tags_list.get(uid)
    if not tag_object:
      return None

    return tag_object.get('realname') or tag_object.get('name')

  def GetAmountFromHexID(self, hexid, glass_size):
    """Returns the amount of beer drunk for a Character.

    Args:
      hexid(str): the hexid of a character.
      glass_size(int): the size of the character's glass.
    Returns:
      int: the amount of beer.
    """
    character = self.GetCharacterFromHexID(hexid)
    amount_cl = 0
    if character:
      entries = Entry.select(Entry).where(Entry.character == character).count()
      amount_cl = entries * glass_size
    return amount_cl

# vim: tabstop=2 shiftwidth=2 expandtab

"""Wrapper module for a database."""

from __future__ import print_function

from datetime import datetime
import json

import peewee

from beerlog import errors
from beerlog import constants


database_proxy = peewee.Proxy()
# pylint: disable=no-init
class BeerModel(peewee.Model):
  """Model for the database."""
  class Meta():
    """Sets Metadata for the database."""
    database = database_proxy


def BeerPerCharacter(character, amount):
  """Helper function to generate the SQL expression for the total amount
  of beer drunk."""
  return peewee.Expression(character.glass, '*', amount)


class Character(BeerModel):
  """class for one Character in the BeerLog database."""
  hexid = peewee.CharField()
  glass = peewee.IntegerField(default=constants.DEFAULT_GLASS_SIZE)

  @property
  def name(self):
    """Gets the corresponding name from hexid using the parent db object
    method."""
    return self._meta.database.GetNameFromHexID(self.hexid)

  def GetAmountDrunk(self):
    """Gets the amount of beer drunk."""
    return self._meta.database.GetAmountFromHexID(self.hexid, self.glass)


class Entry(BeerModel):
  """class for one Entry in the BeerLog database."""
  character = peewee.ForeignKeyField(Character, backref='entries')
  timestamp = peewee.DateTimeField(default=datetime.now)
  pic = peewee.CharField(null=True)

class BeerLogDB():
  """Wrapper for the database."""

  def __init__(self, database_path):
    self.database_path = database_path
    sqlite_db = peewee.SqliteDatabase(self.database_path)
    database_proxy.initialize(sqlite_db)

    # This is used for the Character.name property
    sqlite_db.GetNameFromHexID = self.GetNameFromHexID
    # This is used for the Character.GetAmountDrink() method
    sqlite_db.GetAmountFromHexID = self.GetAmountFromHexID

    sqlite_db.create_tables([Character, Entry], safe=True)

    self.known_tags_list = None

  def Connect(self):
    """Connects to the database."""
    database_proxy.connect()

  def Close(self):
    """Closes the database."""
    database_proxy.close()

  def AddEntry(self, character_hexid, pic, time=None):
    """Inserts an entry in the database.

    Args:
      character_hexid(str): the hexid of the character in the tag.
      pic(str): the path to a picture.
      time(datetime): the optional time to set when adding the Entry.

    Returns:
      Entry: the Entry that was stored in the database.
    """
    glass = self.GetGlassFromHexID(character_hexid)
    character, _ = Character.get_or_create(hexid=character_hexid, glass=glass)
    if time:
      entry = Entry.create(
          character=character,
          timestamp=time,
          pic=pic
      )
    else:
      entry = Entry.create(
          character=character,
          pic=pic
      )
    return entry

  def GetCharacterFromHexID(self, character_hexid):
    """Returns a Character from its hexid.

    Args:
      character_hexid(str): the character's hexid.
    Returns:
      Character: a Character object, or None.
    """
    return Character.get_or_none(Character.hexid == character_hexid)

  def GetEntryById(self, entry_id):
    """Returns an Entry by its primary key.

    Args:
      entry_id(int): the id we're looking for.

    Returns:
      Entry: the corresponding Entry, or None if it wasn't found.
    """
    try:
      entry = Entry.get(Entry.id == entry_id)
    except peewee.DoesNotExist as _:
      return None
    return entry

  def CountAll(self):
    """Returns the number of Entry

    Returns:
      int: the total number of Entry lines in the database.
    """
    return Entry.select().count(None)

  def GetScoreBoard(self):
    """Returns a query with the scoreboard.

    Returns:
      peewee.ModelSelect: the query.
    """
    query = Entry.select(
        Entry,
        Character,
        peewee.fn.MAX(Entry.timestamp).alias('last'),
        BeerPerCharacter(Character, peewee.fn.COUNT()).alias('amount')
    ).join(Character).group_by(Entry.character).order_by(
        BeerPerCharacter(Character, peewee.fn.COUNT()).desc(),
        (peewee.fn.MAX(Entry.timestamp)).desc()
    )

    return query

  def GetGlassFromHexID(self, uid):
    """Returns the corresponding glass from a uid

    Args:
      uid(str): the uid in form 0x0580000000050002
    Returns:
      int: the corresponding glass for that tag uid, or None if no name is found.
    """
    tag_object = self.known_tags_list.get(uid)
    if not tag_object:
      return None

    return tag_object.get('glass')

  def LoadTagsDB(self, known_tags_path):
    """Loads the external known tags list.

    Args:
      known_tags_path(str): path to the known_tags.json file.
    Raises:
      errors.BeerLogError: if we couldn't load the file.
    """
    try:
      with open(known_tags_path, 'r') as json_file:
        self.known_tags_list = json.load(json_file)
    except IOError as e:
      raise errors.BeerLogError(
          'Could not load known tags file {0} with error {1!s}'.format(
              known_tags_path, e))
    except ValueError as e:
      raise errors.BeerLogError(
          'Known tags file {0} is invalid: {1!s}'.format(
              known_tags_path, e))

  def GetNameFromHexID(self, uid):
    """Returns the corresponding name from a uid
    Entry.where()

    Args:
      uid(str): the uid in form 0x0580000000050002
    Returns:
      str: the corresponding name for that tag uid, or None if no name is found.
    """
    tag_object = self.known_tags_list.get(uid)
    if not tag_object:
      return None

    return tag_object.get('realname') or tag_object.get('name')

  def GetAmountFromHexID(self, hexid, glass_size):
    """Returns the amount of beer drunk for a Character.

    Args:
      hexid(str): the hexid of a character.
      glass_size(int): the size of the character's glass.
    Returns:
      int: the amount of beer.
    """
    character = self.GetCharacterFromHexID(hexid)
    amount_cl = 0
    if character:
      entries = Entry.select(Entry).where(Entry.character == character).count()
      amount_cl = entries * glass_size
    return amount_cl

# vim: tabstop=2 shiftwidth=2 expandtab

Wrapper module for a database. # pylint: disable=no-init Model for the database. Sets Metadata for the database. Helper function to generate the SQL expression for the total amount   of beer drunk. class for one Character in the BeerLog database. Gets the corresponding name from hexid using the parent db object     method. Gets the amount of beer drunk. class for one Entry in the BeerLog database. Wrapper for the database. # This is used for the Character.name property # This is used for the Character.GetAmountDrink() method Connects to the database. Closes the database. Inserts an entry in the database.      Args:       character_hexid(str): the hexid of the character in the tag.       pic(str): the path to a picture.       time(datetime): the optional time to set when adding the Entry.      Returns:       Entry: the Entry that was stored in the database. Returns a Character from its hexid.      Args:       character_hexid(str): the character's hexid.     Returns:       Character: a Character object, or None. Returns an Entry by its primary key.      Args:       entry_id(int): the id we're looking for.      Returns:       Entry: the corresponding Entry, or None if it wasn't found. Returns the number of Entry      Returns:       int: the total number of Entry lines in the database. Returns a query with the scoreboard.      Returns:       peewee.ModelSelect: the query. Returns the corresponding glass from a uid      Args:       uid(str): the uid in form 0x0580000000050002     Returns:       int: the corresponding glass for that tag uid, or None if no name is found. Loads the external known tags list.      Args:       known_tags_path(str): path to the known_tags.json file.     Raises:       errors.BeerLogError: if we couldn't load the file. Returns the corresponding name from a uid     Entry.where()      Args:       uid(str): the uid in form 0x0580000000050002     Returns:       str: the corresponding name for that tag uid, or None if no name is found. Returns the amount of beer drunk for a Character.      Args:       hexid(str): the hexid of a character.       glass_size(int): the size of the character's glass.     Returns:       int: the amount of beer. # vim: tabstop=2 shiftwidth=2 expandtab