from flask import Flask
from flask import render_template
from flask import request, redirect, url_for
import os
import json
from pprint import pprint
import codecs
from sqlalchemy import create_engine
from sqlalchemy.sql import text
from datetime import datetime, date, time, timedelta
from time import time, sleep
from flask import session, app
from flask import jsonify

app = Flask(__name__)
app.secret_key = 'naeilbook web reader session'
app.config['PERMANENT_SESSION_LIFETIME'] =  timedelta(minutes=1)


class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv

@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    
    return render_template('error.html', errorMessage="bad request")
    #return response


# app.static_folder

@app.route('/')
def hello_world():
  #return 'Hello World!'
  return list()

# ----------------------------------
# nob 폴더 리스트 화면을 출력한다.
@app.route('/list')
def list():
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    # static의 nob 폴더 안의 모든 파일의 목록을 리스트로 저장한다.
    listDir = os.listdir(os.path.join(app.static_folder, 'nob'))
    return render_template('list.html', listDir=listDir)

def getJsonUTF16(jsonPath):
    filePath = os.path.join(app.static_folder, jsonPath)
    if os.path.exists(filePath):
        with open(filePath, encoding='utf-16') as json_data:
            dict = json.load(json_data, strict=False)
            jsonText = json.dumps(dict)
    else:
        jsonText = "{}"
    return jsonText

def isValidRequested(token):
    engine = create_engine('mysql://sdl:sdl!!!!@localhost/sdl?charset=utf8', convert_unicode=False)
    sql = 'select token, user_guid, request_datetime from reader_token where token = :val'
    result = engine.execute(text(sql), val=token)
    record = result.fetchone()
    result.close()

    if record["request_datetime"] is None:
        return False

    # DB의 request_datetime을 문자열로 받아서
    str_datetime = str(record["request_datetime"])
    # format : 2019-05-16 02:03:02으로 날짜문자열을 datetime type casting한다.
    req_datetime = datetime.strptime(str_datetime, "%Y-%m-%d %H:%M:%S")
    deltaSec = datetime.now() - req_datetime

    # 시간 차이를 seconds로 계산한다.
    #return str(deltaSec.seconds)

    # delta is seconds
    if deltaSec.seconds > 20 and session.get('token') is None:
        session.pop('token', None)
        session.permanent = False
        return False
        #raise InvalidUsage('인증이 필요합니다. 로그인 후 다시 시도해주십시오.', status_code=410)
        #return render_template('error.html', errorMessage="인증이 필요합니다. 로그인 후 다시 시도해주십시오.  (" + str(deltaSec.seconds)+")")

    return True


@app.route('/redirectError')
def redirectError():
    return render_template('error.html', errorMessage="bad request")

@app.route('/open/<path>/<token>')
def openNob(path, token):
    if isValidRequested(token) == False:
        return render_template('error.html', errorMessage="인증이 필요합니다. 로그인 후 다시 시도해주십시오.  (" + str(deltaSec.seconds)+")")

    session['token'] = token
    session.permanent = True

    # bookInfo.json에서 책 정보를 가져온다.
    bookInfoFilePath = os.path.join(app.static_folder, "nob/" + path + "/bookInfo.json")
    with open(bookInfoFilePath) as json_data:
        bookDict = json.load(json_data)
        bookInfoJson = json.dumps(bookDict)

    totalPageCount = bookDict["totalPageCount"]
    # 전체 페이지 그룹 정보 json를 로딩한다.
    pageGroupFilePath = os.path.join(app.static_folder, "nob/" + path + "/pageGroup.json")
    with open(pageGroupFilePath) as json_data:
        pageGroupDict = json.load(json_data)
        pageGroupJson = json.dumps(pageGroupDict)

    # 멀티미디어 정보 json를 로딩한다.
    multimediaJson = getJsonUTF16("nob/" + path + "/multimedia/multimedia.json")

    #link json을 로딩한다.
    linkJson = getJsonUTF16("nob/" + path + "/link.json")

    #anchor json을 로딩한다.
    anchorJson = getJsonUTF16("nob/" + path + "/anchor.json")

    # 검색 json 로딩한다.
    # searchTextFilePath = os.path.join(app.static_folder, "nob/" + path + "/searchText.json")
    # searchTextJson = ""
    # if os.path.exists(searchTextFilePath):
    #     with open(searchTextFilePath, encoding='utf-16') as json_data:
    #         searchTextDict = json.load(json_data, strict=False)
    #         searchTextJson = json.dumps(searchTextDict, ensure_ascii=False)
    return render_template('open.html',path=path,
                            totalPageCount = totalPageCount,
                            bookInfoJson=bookInfoJson,
                            pageGroupJson=pageGroupJson,
                            multimediaJson=multimediaJson,
                            linkJson=linkJson,
                            anchorJson=anchorJson)

# ----------------------------------
# path에 해당하는 static의 'video' contents 파일 경로를 return한다.
@app.route('/multimedia_files/<path>/<name>')
def videoFile(path, name):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    filePath = "nob/" + path + "/multimedia/" + name
    return app.send_static_file(filePath)


# ----------------------------------
# path에 해당하는 static의 검색 json 파일 경로를 return한다.
@app.route('/search/<path>')
def searchJson(path):
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return "Bad request"
    
    filePath = "nob/" + path + "/searchText.json"
    return app.send_static_file(filePath)

# ----------------------------------
# path/no에 해당하는 static의 nob 페이지 이미지를 return한다.
@app.route('/page/<path>/<no>')
def readImage(path, no):
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
        #raise InvalidUsage('인증이 필요합니다. 로그인 후 다시 시도해주십시오.', status_code=410)
    
    pageImagePath = "nob/" + path + "/page_" + no + ".jpg"
    return app.send_static_file(pageImagePath)
    # return render_template('page.html', path=path, pageImagePath=pageImagePath, pageNo=no)

@app.route('/orgpage/<path>/<no>')
def readOrgImage(path, no):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    pageImagePath = "nob/" + path + "/o_page_" + no + ".jpg"
    return app.send_static_file(pageImagePath)

# ----------------------------------
# path/{cover_front | cover_back} 의 해당하는 표지 이미지를 return한다.
@app.route('/cover/<path>/<coverName>')
def readCover(path, coverName):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    coverFilePath = "nob/" + path + "/" + coverName + ".jpg"
    return app.send_static_file(coverFilePath)

# ----------------------------------
# frustum 이미지를 return.
@app.route('/frustum/<path>/<name>')
def readFrustum(path, name):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    filePath = "nob/" + path + "/" + name + ".jpg"
    return app.send_static_file(filePath)

# ----------------------------------
# TOC xml 파일 path를 리턴한다.
@app.route('/toc/<path>')
def readTOC(path):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    filePath = "nob/" + path + "/toc.xml"
    return app.send_static_file(filePath)

# ----------------------------------
# Meta xml 파일 path를 리턴한다.
@app.route('/meta/<path>')
def readMeta(path):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    filePath = "nob/" + path + "/meta.xml"
    return app.send_static_file(filePath)

# ----------------------------------
# nob 파일의 reflow 내용을 출력한다.
@app.route('/reflow/<path>')
def reflow(path):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return redirect(url_for('redirectError'))
    
    staticPath = "/static/nob/" + path
    return render_template('reflow.html',path=path, staticPath=staticPath)

# ----------------------------------
# 인쇄하기
@app.route('/print/<path>/', methods = ['GET'])
def printBook(path):
    # session에 token이 없다면 에러로 처리한다. 
    if session.get('token') is None:
        return redirect(url_for('redirectError'))
    
    if isValidRequested(session['token']) == False:
        return render_template('error.html', errorMessage="인증이 필요합니다. 로그인 후 다시 시도해주십시오.  (" + str(deltaSec.seconds)+")")
    
    staticPath = "/static/nob" + path
    startNo = request.args.get('startNo')
    endNo = request.args.get('endNo')
    includeCover = request.args.get('includeCover')
    return render_template('print.html', path=path, staticPath=staticPath,
                            startNo=startNo, endNo=endNo, includeCover=includeCover)

if __name__ == '__main__':
  #app.debug = True
  app.run(host='121.254.217.237', port=5000)
