##############################################################
# Module:       Server
# Author:       Jacob Calvert (jacob+github@jacobncalvert.com)
# Revision:     1.3.1 (major.minor.maint)
# Description:  This is the entry module for the app. The
# server is set up here and all the handlers are initialized.
###############################################################


import tornado
import tornado.gen
import tornado.httputil
import tornado.ioloop
import tornado.websocket
import tornado.web
import json
import os
from urlparse import parse_qs as qs_decode
import Enums
import Database



class APIRoot(tornado.web.RequestHandler):
    """
    class APIRoot -- a dummy class for the APIs root url, will be filled in later
    """
    def get(self, *args, **kwargs):
        """
        :param args: args trapped from the regex of the APIRoot handler assignment
        :param kwargs: no idea what kwargs are in this instance
        :return: None
        """
        print args
        print kwargs


class APIHandler(tornado.web.RequestHandler):
    """
    class APIHandler -- main handler for calls to the API.
    """
    def get(self, *args):
        """
        writes to the client a json representation of the response generated by the request
        made by 'args'
        :param args: args contains the regex trapped out beyond the '/api/' part of the url
        :return: None
        """
        self.write(json.dumps(APIHandler.parse_uri_to_action(args, self.request.query)))

    @staticmethod
    def parse_uri_to_action(url, qstring):
        """
        parses an API request to an actionable item in the subsystem
        :param url: url of the request
        :param qstring: query string of the request
        :return: a response object to the request made by 'url' and 'qstring'
        """
        qstring = qs_decode(qstring)
        url_parts = url[0].split("/")
        if len(url_parts) > 2:
            # get id
            if len(qstring):
                #filtering

                pass
            else:
                # not filtering

                pass
        else:
            #get all
            if len(qstring) > 0:
                if "media_type" in qstring:
                    if qstring["media_type"][0] == "audio":
                        return Database.DB.get_music()
                    elif qstring["media_type"][0] == "video":
                        return Database.DB.get_video()
                    elif qstring["media_type"][0] == "image":
                        return Database.DB.get_images()
                elif "query" in qstring:
                    return Database.DB.search(qstring["query"][0])

            else:
                # not filtering

                return Database.DB.get_all()


class MyStaticFileHandler(tornado.web.StaticFileHandler):
    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self, path, include_body=True):
        #Assume that path is correct, validation will be handeled elsewhere
        absolute_path = self.get_absolute_path(self.root, path)
        self.absolute_path = self.validate_absolute_path(self.root, absolute_path)
        if self.absolute_path is None:
            return
        self.path = self.absolute_path

        self.modified = self.get_modified_time()
        self.set_headers()

        if self.should_return_304():
            self.set_status(304)
            return

        request_range = None
        range_header = self.request.headers.get("Range")
        if range_header:
            # As per RFC 2616 14.16, if an invalid Range header is specified,
            # the request will be treated as if the header didn't exist.
            request_range = tornado.httputil._parse_request_range(range_header)

        if request_range:
            start, end = request_range
            size = self.get_content_size()
            if (start is not None and start >= size) or end == 0:
                # As per RFC 2616 14.35.1, a range is not satisfiable only: if
                # the first requested byte is equal to or greater than the
                # content, or when a suffix with length 0 is specified
                self.set_status(416)  # Range Not Satisfiable
                self.set_header("Content-Type", "text/plain")
                self.set_header("Content-Range", "bytes */%s" % (size, ))
                return
            if start is not None and start < 0:
                start += size
            if end is not None and end > size:
                # Clients sometimes blindly use a large range to limit their
                # download size; cap the endpoint at the actual file size.
                end = size

            self.set_status(206)  # Partial Content
            self.set_header("Content-Range",
                            tornado.httputil._get_content_range(start, end, size))
        else:
            start = end = None

        content = self.get_content(self.absolute_path, start, end)

        for chunk in content:
            self.write(chunk)
            yield tornado.gen.Task(self.flush)
        self.finish()


def main():
    """
    Entry point for the application. Sets up the webserver and handlers.
    :return: None
    """
    app = tornado.web.Application([
        (r"/api/?", APIRoot),
        (r"/api/(.+)/?", APIHandler),
        (r"/media/(.*)", MyStaticFileHandler, {'path': Enums.Paths.MEDIA_BASE}),
        (r"/(.*)", tornado.web.StaticFileHandler, {'path': Enums.Paths.HTML_BASE}),
    ]
    )
    fh = Database.FileHelper(Enums.Paths.MEDIA_BASE)
    fh.filter()

    app.listen(Enums.Server.PORT)
    tornado.ioloop.IOLoop.instance().start()  # This is a blocking operation...

main()