"""
Flask Documentation:     http://flask.pocoo.org/docs/
Jinja2 Documentation:    http://jinja.pocoo.org/2/documentation/
Werkzeug Documentation:  http://werkzeug.pocoo.org/documentation/
This file creates your application.
"""

from app import app, db, socketio, DOMAIN
from flask import Flask, Response, session, render_template, request, redirect, url_for, flash, send_from_directory, jsonify, abort
from flask_weasyprint import HTML, render_pdf
import json
import os
from  sqlalchemy.sql.expression import func, select
from app.forms import UploadForm, EditForm, SearchForm, ChatForm, StackForm, AddtoStackForm, EditStackForm
from app.models import Book, BookSchema, Author, AuthorSchema, Stack, StackSchema, UserIns, Chat, ChatSchema
from app.cover import get_cover
from app.getannot import get_annotations
from urllib.parse import quote as urlquote
from os import environ
from flask_socketio import SocketIO, emit
import datetime
import time
import autocomplete
import sys
from werkzeug.utils import secure_filename

# import sqlite3
ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'epub', 'chm', 'mobi'])

author_schema = AuthorSchema()
authors_schema = AuthorSchema(many=True)
book_schema = BookSchema()
books_schema = BookSchema(many=True)
stack_schema = StackSchema()
stacks_schema = StackSchema(many=True)
chat_schema = ChatSchema()
chats_schema = ChatSchema(many=True)

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
###
# Routing for your application.
###

@app.route('/', methods= ['POST','GET'])
def home():
    chat_form = ChatForm()
    chat_messages = db.session.query(Chat).all()

    # if request.method == 'POST':
    #     if chat_form.validate_on_submit():
    #         message = chat_form.message.data
    #         msg = Chat(message)
    #         db.session.add(msg)
    #         db.session.commit()

    return render_template('home.html',domain=DOMAIN,chat=chat_messages, channel = 1, username="librarian")

@app.route('/hello/<name>')
def hello(name):
    return "Hello " + name

@app.route('/about/')
def about():
    """Render the website's about page."""
    return render_template('about.html', name="Mary Jane")

@app.route('/uploads/<filename>')
def uploaded_file(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER'],
                               filename)

@app.route('/viewpdf/<filename>')
def viewtestfile1_file(filename):
    return redirect("/static/viewer/web/viewer.html?file=%2Fuploads%2F"+urlquote(filename))

@app.route('/uploads/cover/<filename>')
def uploaded_file_cover(filename):
    return send_from_directory(app.config['UPLOAD_FOLDER_COVER'],
                               filename)

@app.route('/updates', methods=['POST', 'GET'])
def get_updates():
    userin = UserIns.query.filter_by(title="lastViewed").first()
    allbooks = db.session.query(Book).all()
    id = len(allbooks)
    latest_upload = allbooks[-1]
    return "This is the XPPL ~ Library XPUB ~ Updates / / / / / / / Last viewed: " + userin.info + " / / / / / / / " + str(len(allbooks)) + " Books online "+ " / / / / / / / " + "Latest upload: " + latest_upload.title

@app.route('/scape', methods=['POST', 'GET'])
def scape():
    if request.method == 'POST':
        data = request.form
        book = Book.query.get(data['id'])
        print(book.scapeX)
        book.scapeX = data['x']
        book.scapeY = data['y']
        db.session.commit()
    books = db.session.query(Book).all() # or you could have used User.query.all()
    return render_template('scape.html', books=books)


@app.route('/books_grid')
def show_books_grid():
    books = db.session.query(Book).all() # or you could have used User.query.all()
    return render_template('show_books_grid.html', books=books)

@app.route('/books/<int:id>')
def show_book_by_id(id):
    book = Book.query.get(id)
    userin = UserIns.query.filter_by(title="lastViewed").first()
    if userin != None:
        userin.info = book.title
        db.session.commit()
    else:
        user_info = UserIns("lastViewed", book.title)
        db.session.add(user_info)
        db.session.commit()
    if not book:
        return render_template('red_link.html', id=id)
    else:
        return render_template('show_book_detail.html', book=book)


@app.route('/books/<int:id>/delete', methods=['POST', 'GET'])
def remove_book_by_id(id):
    book_to_edit = Book.query.filter_by(id=id).first()
    title = book_to_edit.title
    Book.query.filter_by(id=id).delete()
    #author_table = Author.query.filter_by(book_id=book_to_edit.id).delete()
    db.session.commit()
    flash("%s deleted from library" % (title))
    return redirect(url_for('show_books'))

@app.route('/books/<int:id>/edit', methods=['POST', 'GET'])
def edit_book_by_id(id):
    book_to_edit = Book.query.filter_by(id=id).first()
    user_form = EditForm(title = book_to_edit.title, author =book_to_edit.authors, category = book_to_edit.category, year_published= book_to_edit.year_published)

    if request.method == 'POST':
        if user_form.validate_on_submit():
            # check if the post request has the file part
            title = user_form.title.data # You could also have used request.form['name']
            input_authors = user_form.author.data # You could also have used request.form['email']
            category = user_form.category.data
            year_published = user_form.year_published.data
            if year_published=="":
                year_published = None
            # save user to database
            #book = Book(title, author, filename, cover, file_extension)

            book = Book.query.filter_by(id=id).first()
            book.title = title
            book.category = category
            book.year_published = year_published

            #authors update
            book.authors.clear()
            for i, author in enumerate(input_authors):
                author_name = author.get("author_name")
                if author_name:
                    a = db.session.query(Author).filter_by(author_name=author_name).first()
                    if a == None:
                        a = Author(author_name=author_name)
                        db.session.add(a)
                    book.authors.append(a)
            db.session.commit()
            flash("%s updated" % (title))
            return redirect(url_for('show_book_by_id', id=id))

    return render_template('edit_book_detail.html', book=book_to_edit, form=user_form)


@app.route('/add-book', methods=['POST', 'GET'])
def add_book():
    upload_form = UploadForm()

    if request.method == 'POST':
        if upload_form.validate_on_submit():
            #get data from form
            title = upload_form.title.data
            authors = upload_form.author.data
            category = upload_form.category.data
            year_published = upload_form.year_published.data
            if year_published=="":
                year_published = None

            #if upload with file
            if upload_form.upload.data:
                # check if the post request has the file part
                if 'file' not in request.files:
                    flash('No file part')
                    return redirect(request.url)
                file = request.files['file']
                # if user does not select file, browser also
                # submit a empty part without filename
                if file.filename == '':
                    flash('No selected file')
                    return redirect(request.url)
                if file and allowed_file(file.filename):
                    filename = secure_filename(file.filename)
                    allbooks = db.session.query(Book).all()
                    id = len(allbooks)+1
                    new_filename = str(id) +"_"+ filename
                    fullpath = os.path.join(app.config['UPLOAD_FOLDER'], new_filename)
                    name, file_extension = os.path.splitext(new_filename)
                    file.save(fullpath)
                    cover = get_cover(fullpath, name)
                else:
                    flash('allowed file formats: %s' % ALLOWED_EXTENSIONS)
            #if upload without file -> wishform, with potential PDF
            if upload_form.wish.data:
                #TO DO: make pdf generator
                #file = open('app/uploads/potential.pdf')
                #filename = 'potential.pdf'
                #file_extension = '.pdf'
                filename = ''
                file_extension = ''
                cover = ''

            book = Book(title, filename, cover, file_extension, category,year_published)
            db.session.add(book)
            for author in authors:
                author_name = author.get("author_name")
                if author_name:
                    a = db.session.query(Author).filter_by(author_name=author_name).first()
                    if a == None:
                        a = Author(author_name=author_name)
                        db.session.add(a)
                    book.authors.append(a)
            db.session.commit()

            flash("%s added to the library" % (title))
            return redirect(url_for('show_books'))

    flash_errors(upload_form)
    return render_template('add_book.html', form=upload_form)


# Flash errors from the form if validation fails
def flash_errors(form):
    for field, errors in form.errors.items():
        for error in errors:
            flash(u"Error in the %s field - %s" % (
                getattr(form, field).label.text,
                error
            ))

#Authors

@app.route('/authors/<int:id>')
def show_author_by_id(id):
    author = Author.query.get(id)
    if not author:
        abort (404)
    else:
        return render_template('show_author_detail.html', author=author)


@app.route('/authors/<int:id>/edit', methods=['POST', 'GET'])
def edit_author_by_id(id):
    return "Ask the programmer."

##stacks

@app.route('/stacks')
def show_stacks():
    stacks = db.session.query(Stack).all()
    return render_template('show_stacks.html', stacks=stacks)

@app.route('/stacks/add_stack', methods=['POST', 'GET'])
def add_stack():
    form = StackForm()
    stacks = db.session.query(Stack).all()

    if form.validate_on_submit():
        stack_name = form.stack_name.data
        stack_description = form.stack_description.data
        stack = Stack(stack_name, stack_description)
        if form.stack_name.data:
            stack = Stack(stack_name, stack_description)
            db.session.add(stack)
        stacks = db.session.query(Stack).all()
        return redirect(url_for('show_stacks'))
        flash("%s stack created" % (stack_name))
    return render_template('add_stack.html', stacks=stacks, form=form)

@app.route('/stacks/tab/<int:id>', methods=['POST', 'GET'])
def show_stack_in_tab(id):
    return show_stack_by_id(id, is_tab=True)


@app.route('/stacks/<int:id>', methods=['POST', 'GET'])
def show_stack_by_id(id, is_tab=False):

    stack = Stack.query.get(id)
    if not stack:
        abort (404)
    else:
        if is_tab == False:
            return render_template('show_stack_detail.html', stack=stack)
        else:
            return render_template('show_stack_detail_tab.html', stack=stack)

@app.route('/stacks/<int:id>/delete', methods=['POST', 'GET'])
def remove_stack_by_id(id):
    Stack.query.filter_by(id=id).delete()
    db.session.commit()
    return redirect(url_for('show_stacks'))

@app.route('/stacks/<int:id>/edit', methods=['POST', 'GET'])
def edit_stack_by_id(id):
    stack = Stack.query.filter_by(id=id).first()
    form = EditStackForm(edit_stack_name = stack.stack_name, edit_stack_description = stack.stack_description)

    if request.method == 'POST':
        if form.validate_on_submit():
            stack_name = form.edit_stack_name.data
            stack_description = form.edit_stack_description.data
        stack.stack_name = stack_name
        stack.stack_description = stack_description
        db.session.commit()

        return redirect(url_for('show_stack_by_id', id=id))
    return render_template('edit_stack_detail.html', stack=stack, form=form)
## search

## search

@app.route('/books', methods= ['POST','GET'])
def show_books():
    autocomplete.load() #Train markov model once, for autocomplete in search
    books = db.session.query(Book).all()
    search = SearchForm(request.form)
    if request.method == 'POST':
        return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data)))

    return render_template('show_books.html', books=books, form=search)

@app.route('/search/<searchtype>/<query>/', methods=['POST', 'GET'])
def search_results(searchtype, query):
    search = SearchForm(request.form)
    random_order=Book.query.order_by(func.random()).limit(10)
    results=Book.query.filter(Book.title.contains(query)).all()

    if searchtype == 'Title':
        results=Book.query.filter(Book.title.contains(query)).all()

    if searchtype == 'Category':
        results=Book.query.filter(Book.category.contains(query)).all()

    if searchtype== 'All':
        results=Book.query.whoosh_search(query).all()

    # if searchtype== 'Annotation':
    #     results=Book.query.filter(Book.category.contains(query)).all()

    if not results:
        upload_form = UploadForm(title= query, author='')
        return render_template('red_link.html', form=upload_form, title=query)

    if request.method == 'POST':
        query = search.search.data
        results = []
        return redirect((url_for('search_results', searchtype=search.select.data, query=search.search.data)))

    return render_template('results.html', form=search, books=results, books_all=random_order, searchtype=search.select.data, query=query)

## Search - autocomplete
autocomplete_suggestions = []
autocomplete.load() #Train markov model once, for autocomplete in search

@app.route('/autocomplete_suggestions', methods=['GET', 'POST'])
def test1():
    if request.method == 'POST':
        query = request.form['search']
        query_tokenized = query.lower().split()
        print(query_tokenized)
        word_1 = query_tokenized[-2]
        word_2 = query_tokenized[-1]
        #print(word_1)
        autocomplete_output = autocomplete.predict(word_1 , word_2)
        autocomplete_suggestions.clear()
        for suggestion, score in autocomplete_output:
            autocomplete_suggestions.append(suggestion)

        session['autocomplete_suggestions'] = str(autocomplete_suggestions)

        print(session['autocomplete_suggestions'])

    return Response(json.dumps(session['autocomplete_suggestions']), mimetype='application/json')

## STACKS!

@app.route('/add_to_stack/<int:id>', methods=['GET', 'POST'])
def add_to_stack(id):
    stacks = db.session.query(Stack).all()
    add_form = AddtoStackForm(request.form)
    add_form.select_stack.choices = [(stack.id, stack.stack_name) for stack in stacks]
    if request.method == 'GET':
        book = Book.query.get(id)
        return render_template('add_to_stacks.html', id=id, stacks=stacks, book=book, add_form=add_form)
    else:
        stack = Stack.query.get(int(add_form.select_stack.data))
        book = Book.query.get(id)
        stack.books.append(book)
        db.session.commit()
        return render_template('show_stack_detail.html', stack=stack)

from csv import DictWriter
import io

@app.route('/export/csv', methods=['GET'])
def export_csv ():
    output = io.StringIO()
    fieldnames = ['title', 'authors']
    csv = DictWriter(output,fieldnames)
    csv.writeheader()
    # for i in range(10):
    #     csv.writerow({'ID': i, 'fruit': "Tomato"})
    for book in Book.query.order_by("title"):
        authors = ", ".join([x.author_name for x in book.authors])
        csv.writerow({"title": book.title, "authors": authors})
    resp = Response(output.getvalue(), mimetype="text/plain")
    # resp.headers["Content-Disposition"] = "attachment;filename=export.csv"
    return resp
###
# The API
###

@app.route('/api/books', methods=['GET'])
def get_books():
    books = Book.query.all()
    data = books_schema.dump(books)
    #print(errors)
    return jsonify({'books': data})

@app.route('/api/books/<int:id>', methods=['GET'])
def get_book_by_id(id):
    book = Book.query.get(id)
    data = book_schema.dump(book)
    if not data:
        return jsonify({"message": "Book could not be found."}), 400
    else:
        return jsonify({'book': data })

@app.route('/api/chats', methods=['GET'])
def get_chat():
    chats = Chat.query.all()
    data = chats_schema.dump(chats)
    #print(errors)
    return jsonify({'chat': data})



###
# The functions below should be applicable to all Flask apps.
###

@app.route('/<file_name>.txt')
def send_text_file(file_name):
    """Send your static text file."""
    file_dot_text = file_name + '.txt'
    return app.send_static_file(file_dot_text)

# annotations
@app.route('/annotations')
def annotations():
    """Render annotations page."""
    annot = get_annotations()
    print(annot)
    dump = json.dumps(annot, indent=2)
    return render_template('annotations.html', annot=annot, dump=dump)

# PDF from annotations
@app.route('/annotations.pdf')
def annotations_pdf():
    annot = get_annotations()
    # Make a PDF straight from HTML in a string.
    html = render_template(('annotations.html'), annot=annot)
    return render_pdf(HTML(string=html))


@app.after_request
def add_header(response):
    """
    Add headers to both force latest IE rendering engine or Chrome Frame,
    and also to cache the rendered page for 10 minutes.
    """
    response.headers['X-UA-Compatible'] = 'IE=Edge,chrome=1'
    response.headers['Cache-Control'] = 'public, max-age=600'
    return response


@app.errorhandler(404)
def page_not_found(error):
    """Custom 404 page."""
    return render_template('404.html'), 404

@socketio.on('new_message')
def new_message(message):
    # Send message to alls users
    print("new message")
    emit('channel-' + str(message['channel']), {
        'username': message['username'],
        'text': message['text'],
        'time': str(datetime.datetime.utcnow().strftime("%d.%m.%Y %H:%M"))
    },
        broadcast=True
    )
    # Save message
    my_new_chat = Chat(
        message=message['text']
    )
    db.session.add(my_new_chat)
    try:
        db.session.commit()
    except:
        db.session.rollback()



if __name__ == '__main__':
    # socketio.run(app)
    app.run(debug=True,host="0.0.0.0",port="8080")
