from datetime import date
from flask import Flask
from flask import redirect, g
from flask import render_template
from flask import url_for
from flask.ext.mail import Mail
from flask.ext.security import Security, SQLAlchemyUserDatastore, \
    UserMixin, RoleMixin, login_required, roles_required
from flask.ext.security import user_registered
from flask_security.core import current_user
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////data/rollerverbrauch.db'
app.config['SECURITY_PASSWORD_HASH'] = 'pbkdf2_sha512'
app.config['SECURITY_REGISTERABLE'] = True
app.config['SECURITY_CHANGEABLE'] = True
app.config['SECURITY_RECOVERABLE'] = True
app.config.from_envvar('config')
app.config.from_object(__name__)

db = SQLAlchemy(app)
mail = Mail(app)


from rollerverbrauch.forms import \
    CreatePitstopForm, \
    EditVehicleForm, \
    DeleteVehicleForm

from rollerverbrauch.entities import \
    User, \
    Role, \
    Pitstop, \
    Vehicle


user_datastore = SQLAlchemyUserDatastore(db, User, Role)
security = Security(app, user_datastore)


@user_registered.connect_via(app)
def user_registered_sighandler(app, user, confirm_token):
    """
    Called after a user was created
    """
    role = user_datastore.find_role('user')
    user_datastore.add_role_to_user(user, role)
    v = Vehicle('default vehicle')
    db.session.add(v)
    user.vehicles.append(v)
    db.session.commit()


@app.before_first_request
def before_first_request():
    db.create_all()
    user_datastore.find_or_create_role(name='admin', description='Role for administrators')
    user_datastore.find_or_create_role(name='user', description='Role for all users.')
    db.session.commit()


@app.before_request
def before_request():
    g.data = {}


@app.route('/')
@login_required
def index():
    return redirect(url_for('get_pit_stops'))


@app.route('/account/edit_vehicle/<int:vid>', methods=['GET', 'POST'])
@login_required
def edit_vehicle(vid):
    vehicle = Vehicle.query.filter(Vehicle.id == vid).first()
    form = EditVehicleForm()

    if form.validate_on_submit():
        vehicle.name = form.name.data;
        db.session.commit()
        return redirect(url_for('get_account_page'))

    form.name.default = vehicle.name
    form.process()
    return render_template('editVehicleForm.html', form=form)


@app.route('/account/delete_vehicle/<int:vid>', methods=['GET', 'POST'])
@login_required
def delete_vehicle(vid):
    vehicle = Vehicle.query.filter(Vehicle.id == vid).first()

    # prevent deletion of foreign vehicles
    if not vehicle in current_user.vehicles:
        return redirect(url_for('get_account_page'))

    form = DeleteVehicleForm()

    if form.validate_on_submit():
        db.session.delete(vehicle)
        db.session.commit()
        return redirect(url_for('get_account_page'))

    return render_template('deleteVehicleForm.html', form=form, vehicle=vehicle)


@app.route('/account/create_vehicle', methods=['GET', 'POST'])
@login_required
def create_vehicle():
    form = EditVehicleForm()

    if form.validate_on_submit():
        new_vehicle = Vehicle(form.name.data)
        db.session.add(new_vehicle)
        current_user.vehicles.append(new_vehicle)
        db.session.commit()
        return redirect(url_for('get_account_page'))

    return render_template('createVehicleForm.html', form=form)


@app.route('/pitstops/createForm', methods=['GET', 'POST'])
@login_required
def create_pit_stop_form():
    last_pitstop = Pitstop.query.order_by(Pitstop.id.desc()).first()
    if last_pitstop is None:
        last_pitstop = Pitstop(0, 0, date.today())

    form = CreatePitstopForm()
    form.set_pitstop(last_pitstop)
    form.vehicle.choices = [(g.id, g.name) for g in current_user.vehicles]

    if form.validate_on_submit():
        v = Vehicle.query.filter(Vehicle.id == form.vehicle.data).first()
        new_stop = Pitstop(form.odometer.data, form.litres.data, form.date.data)
        db.session.add(new_stop)
        v.pitstops.append(new_stop)
        db.session.commit()
        return redirect(url_for('get_pit_stops'))

    form.odometer.default = last_pitstop.odometer
    form.litres.default = last_pitstop.litres
    form.date.default = date.today()
    form.process()
    return render_template('newPitStopForm.html', form=form)


@app.route('/pitstops', methods=['GET'])
@login_required
def get_pit_stops():
    data = prepare_pit_stops(Pitstop.query.all())
    g.data['pitstops'] = data
    return render_template('pitstops.html', data=g.data)


@app.route('/manual', methods=['GET'])
@login_required
def get_manual():
    return render_template('manual.html', data=g.data)


@app.route('/admin', methods=['GET'])
@roles_required('admin')
def get_admin_page():
    g.data['users'] = User.query.all()
    return render_template('admin.html', data=g.data)


@app.route('/account', methods=['GET'])
@login_required
def get_account_page():
    print(current_user)
    return render_template('account.html', data=g.data)


@app.route('/statistics', methods=['GET'])
@login_required
def get_statistics():
    pitstops = Pitstop.query.all()
    count = len(pitstops)
    distance = 0
    sum_litres = 0
    average_distance = 0
    average_litres_fuelled = 0
    average_litres_used = 0

    if count > 0:
        sum_litres = 0
        for pitstop in pitstops:
            sum_litres += pitstop.litres
        average_litres_fuelled = sum_litres/count
    if count > 1:
        distance = pitstops[-1].odometer - pitstops[0].odometer
        average_distance = distance/(count - 1)
        average_litres_used = 100 * (sum_litres-pitstops[0].litres)/distance
    g.data['distance'] = distance
    g.data['count'] = count
    g.data['litres'] = sum_litres
    g.data['averageDistance'] = average_distance
    g.data['averageListresFuelled'] = average_litres_fuelled
    g.data['averageListresUsed'] = average_litres_used
    return render_template('statistics.html', data=g.data)

def prepare_pit_stops(pss):
    pitstops = []
    for pitstop_index in range(0, len(pss)):
        p = dict()
        p['odometer'] = pss[pitstop_index].odometer
        p['litres'] = pss[pitstop_index].litres
        p['date'] = pss[pitstop_index].date
        pitstops.append(p)
    for pitstop_index in range(1, len(pitstops)):
        last = pitstops[pitstop_index - 1]
        curr = pitstops[pitstop_index]
        curr['distance'] = curr['odometer'] - last['odometer']
        curr['average'] = 100 * curr['litres']/curr['distance']
        last_date = last['date']
        curr_date = curr['date']
        curr['days'] = (curr_date - last_date).days
    pitstops.reverse()
    return pitstops