# -*- coding: utf-8 -*-
 
from flamejam import app, db, login_manager
from flamejam.utils import hash_password, verify_password, findLocation
from flamejam.models import Participation, Team, Game
from flask import url_for, Markup
from datetime import datetime
from hashlib import md5
import scrypt
 
class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    password = db.Column(db.LargeBinary())
    token = db.Column(db.BigInteger, nullable=True, default=None)
    email = db.Column(db.String(191), unique=True)
    new_email = db.Column(db.String(191), unique=True)
    is_admin = db.Column(db.Boolean, default=False)
    is_verified = db.Column(db.Boolean)
    is_deleted = db.Column(db.Boolean, default = False)
    registered = db.Column(db.DateTime)
    ratings = db.relationship('Rating', backref='user', lazy = "dynamic")
    comments = db.relationship('Comment', backref='user', lazy = "dynamic")
    invitations = db.relationship("Invitation", backref = "user", lazy = "dynamic")
    participations = db.relationship("Participation", backref = db.backref("user", lazy="joined"), lazy = "subquery")
 
    ability_programmer = db.Column(db.Boolean)
    ability_gamedesigner = db.Column(db.Boolean)
    ability_2dartist = db.Column(db.Boolean)
    ability_3dartist = db.Column(db.Boolean)
    ability_composer = db.Column(db.Boolean)
    ability_sounddesigner = db.Column(db.Boolean)
    abilities_extra = db.Column(db.String(128))
    location = db.Column(db.String(128))
    location_coords = db.Column(db.String(128))
    location_display = db.Column(db.String(128))
    location_flag = db.Column(db.String(16), default = "unknown")
    real_name = db.Column(db.String(128))
    about = db.Column(db.Text)
    website = db.Column(db.String(128))
    avatar = db.Column(db.String(128))
 
    pm_mode = db.Column(db.Enum("email", "form", "disabled"), default = "form")
 
    notify_new_jam = db.Column(db.Boolean, default = True)
    notify_jam_start = db.Column(db.Boolean, default = True)
    notify_jam_finish = db.Column(db.Boolean, default = True)
    notify_game_comment = db.Column(db.Boolean, default = True)
    notify_team_invitation = db.Column(db.Boolean, default = True)
    notify_newsletter = db.Column(db.Boolean, default = True)
 
    def __init__(self, username, password, email, is_admin = False, is_verified = False):
        self.username = username
        self.password = hash_password(password)
        self.email = email
        self.new_email = email
        self.is_admin = is_admin
        self.is_verified = is_verified
        self.registered = datetime.utcnow()
 
    def __repr__(self):
        return '<User %r>' % self.username
 
    def get_id(self):
        return self.id
 
    def is_active(self):
        return self.is_verified
 
    def is_anonymous(self):
        return False
 
    def is_authenticated(self):
        return True
 
    def getVerificationHash(self):
        # combine a few properties, hash it
        # take first 16 chars for simplicity
        # make it email specific
        hash = scrypt.hash(str(self.username) + str(self.new_email), app.config['SECRET_KEY'])
        return hash.encode('hex')[:16]
 
    def getResetToken(self):
        # combine a few properties, hash it
        # take first 16 chars for simplicity
        hash = scrypt.hash(str(self.token), app.config['SECRET_KEY'])
        return hash.encode('hex')[:16]
 
    def ratedGame(self, game):
        return self.ratings.filter_by(game = game).first() != None
 
    def getRatingCount(self, jam):
        i = 0
        for r in self.ratings:
            if r.game.jam == jam:
                i += 1
        return i
 
    @property
    def games(self):
        g = []
        for p in self.participations:
            if p.team:
                for game in p.team.games:
                    if not game.is_deleted:
                        g.append(game)
 
        import operator
        g.sort(key = operator.attrgetter("created"))
 
        return g
 
    def url(self, **values):
        return url_for('show_user', username = self.username, **values)
 
    def getAvatar(self, size = 32):
        if self.avatar:
            return self.avatar.replace("%s", str(size))
        return "//gravatar.com/avatar/{0}?s={1}&d=identicon".format(md5(self.email.lower()).hexdigest(), size)
 
    def setLocation(self, location):
        if not location:
            self.location = ""
            self.location_display = ""
            self.location_coords = ""
            self.location_flag = "unknown"
            return True
 
        new_loc, new_coords, new_flag = findLocation(location)
        if not new_loc:
            return False
        self.location = location
        self.location_display = new_loc
        self.location_coords = new_coords
        self.location_flag = new_flag
        return True
 
    def getLocation(self):
        return Markup('<span class="location"><span class="flag %s"></span> <span class="city">%s</span></span>' % (self.location_flag, self.location_display or "n/a"))
 
    def getLink(self, class_ = "", real = True, avatar = True):
        if self.is_deleted:
            return Markup('<span class="user deleted">[DELETED]</span>')
 
        s = 16
        if self.is_admin:
            class_ += " admin"
 
        link = ''
        link += '<a class="user {0}" href="{1}">'.format(class_, self.url())
        if avatar:
            link += '<img width="{0}" height="{0}" src="{1}" class="icon"/> '.format(s, self.getAvatar(s))
        link += '<span class="name"><span class="username">{0}</span>'.format(self.username)
        link += u' <span class="real">({0})</span>'.format(self.real_name) if self.real_name and real else ''
        link += '</span></a>'
 
        return Markup(link)
 
    @property
    def abilities(self):
        a = []
        if self.ability_programmer:
            a.append("Programming")
        if self.ability_gamedesigner:
            a.append("Game Design")
        if self.ability_2dartist:
            a.append("Graphics / 2D Art")
        if self.ability_3dartist:
            a.append("Modelling / 3D Art")
        if self.ability_composer:
            a.append("Composing")
        if self.ability_sounddesigner:
            a.append("Sound Design")
        return a
 
    def abilityString(self):
        a = ", ".join(self.abilities)
        if self.abilities_extra:
            a += '<div class="ability-extra">' + self.abilities_extra + '</div>'
        return a
 
    def getParticipation(self, jam):
        return Participation.query.filter_by(user_id = self.id, jam_id = jam.id).first()
 
    def getTeam(self, jam):
        p = self.getParticipation(jam)
        return p.team if p and p.team else None
 
    def inTeam(self, team):
        return self in team.members
 
    def canRate(self, game):
        return not self.inTeam(game.team)
 
    def canEdit(self, game):
        return self.inTeam(game.team)
 
    def joinJam(self, jam, generateTeam = True):
        p = Participation(self, jam)
        db.session.add(p)
        db.session.commit() # need to commit so the team does not register us automatically
 
        if generateTeam:
            self.generateTeam(jam)
        else:
            db.session.commit()
 
    def generateTeam(self, jam):
        t = Team(self, jam)
        db.session.add(t)
        db.session.commit()
 
    def leaveJam(self, jam):
        # leave team
        if self.getTeam(jam):
            self.getTeam(jam).userLeave(self) #  will destroy the team if then empty
 
        # delete registration
        if self.getParticipation(jam):
            db.session.delete(self.getParticipation(jam))
 
    def numberOfGames(self):
        return len(self.games)
 
    @property
    def openInvitations(self):
        invitations = []
        for invitation in self.invitations:
            if invitation.canAccept():
                invitations.append(invitation)
        return invitations
 
# we need this so Flask-Login can load a user into a session
@login_manager.user_loader
def load_user(user_id):
    user = User.query.filter_by(id=user_id).first()
    if user:
        return user
    else:
        return None