#!/usr/bin/python
# -*- coding: UTF-8 -*-

###############################################################################
#                                                                             #
# Bazaar Browser - A simple web application for browsing Bazaar repository    #
#                                                                             #
# Copyright (C) 2011-2012  Fabien LOISON <http://www.flogisoft.com/>          #
#                                                                             #
# This program is free software: you can redistribute it and/or modify        #
# it under the terms of the GNU Affero General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or           #
# (at your option) any later version.                                         #
#                                                                             #
# This program is distributed in the hope that it will be useful,             #
# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
# GNU Affero General Public License for more details.                         #
#                                                                             #
# You should have received a copy of the GNU Affero General Public License    #
# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
#                                                                             #
###############################################################################


"""All controllers."""


from os.path import isfile, isdir, join

from bzrlib.branch import Branch
from bzrlib.export import export as tree_export
from bzrlib.errors import NoSuchRevision
from flask import render_template, abort, request, redirect, Response

from config import config
from application import app
from helpers import (isbranch, isproject, brz_infos_from_rev_id,
        get_readme, browse_dir)


@app.route("/")
@app.route("/<path:path>")
def request_handler(path=""):
    """Handdle every non-action (+xxx) request"""
    fullpath = join(config['repository/path'], path)
    #Force the trailing / for folders
    if isdir(fullpath) and len(path) > 0 and path[-1] != "/":
        return redirect("%s/" % path)
    #Avoid unauthorized path
    if not isfile(fullpath) and ".bzr" in path.split("/") or "+" in path:
        return abort(404)
    #If the path is a directory
    if isdir(fullpath):
        if isbranch(fullpath):
            return branch_infos(path)
        elif isproject(fullpath):
            return project_infos(path)
        else:
            return folder_infos(path)
    #If the path is a file
    elif isfile(fullpath):
        return file_request(fullpath)
    #Not found... New branch?
    else:
        return branch_new(path)


@app.route("/<path:path>/+export/rev<int:revno>.<string:archive_format>")
def export(path, revno, archive_format):
    """Export a revision in tgz or zip"""
    fullpath = join(config['repository/path'], path)
    if isbranch(fullpath):
        formats = {
                'tar.gz': {
                    'format': "tgz",
                    'mime': "application/x-compressed-tar",
                    },
                'zip': {
                    'format': "zip",
                    'mime': "application/zip",
                    },
                }
        #Check if the format is supported
        if archive_format not in formats:
            return abort(404)
        #Get the rev_id from the revno
        branch = Branch.open(fullpath)
        try:
            rev_id = branch.dotted_revno_to_revision_id([revno])
        except NoSuchRevision:
            return abort(404)
        #Folder name and File path
        foldername = path.replace("/", "_")
        filepath = "%s_rev%i.%s" % (foldername, revno, archive_format)
        filepath = join(config['cache/path'], filepath)
        #Export the revtree in tar.gz, zip,... if the file does not already exists
        if not isfile(filepath):
            revtree = branch.repository.revision_tree(rev_id)
            revtree.lock_read()
            tree_export(revtree, dest=filepath,
                    format=formats[archive_format]['format'], root=foldername)
            revtree.unlock()
        #Return the archive if it was successfully created
        if isfile(filepath):
            return Response(
                    open(filepath, "rb").read(),
                    mimetype=formats[archive_format]['mime'],
                    )
        else:
            return abort(500)
    else:
        return abort(404)


def branch_infos(path):
    """Returns a page with informations about a branch"""
    fullpath = join(config['repository/path'], path)
    branch = Branch.open(fullpath)
    rev_id = branch.last_revision()
    log = []
    for i in range(10):
        log.append(brz_infos_from_rev_id(branch, rev_id))
        if log[-1]['parent_ids']:
            rev_id = log[-1]['parent_ids'][0]
        else:
            break
    return render_template(
            "branch_view.html",
            title="/%s" % path,
            path="/%s" % path,
            log=log,
            readme=get_readme(branch),
            branch_readonly_url=request.url,
            branch_readwrite_url="%s%s" % (config['repository/pushurl'], path),
            )


def branch_new(path):
    """Returns a page with the informations for creating a new branch"""
    return render_template(
            "newbranch_view.html",
            title="/%s" % path,
            path="/%s" % path,
            branch_readwrite_url="%s%s" % (config['repository/pushurl'], path),
            ), 404


def folder_infos(path):
    """Returns a page with a folder explorer"""
    fullpath = join(config['repository/path'], path)
    browse = browse_dir(fullpath)
    return render_template(
            "folder_view.html",
            title="/%s" % path,
            path="/%s" % path,
            folders=browse['folders'],
            branches=browse['branches'],
            )


def project_infos(path):
    """Returns a page with the project informations"""
    fullpath = join(config['repository/path'], path)
    browse = browse_dir(fullpath)
    #Bazaar infos (from the trunk)
    branch = Branch.open(join(fullpath, "trunk"))
    rev_id = branch.last_revision()
    #get the trunk last commit
    lastcommit = brz_infos_from_rev_id(branch, rev_id)
    return render_template(
            "project_view.html",
            title="/%s" % path,
            path="/%s" % path,
            folders=browse['folders'],
            branches=browse['branches'],
            readme=get_readme(branch),
            lastcommit=lastcommit,
            branch_readonly_url="%strunk/" % request.url,
            branch_readwrite_url="%s%strunk/" % (config['repository/pushurl'], path),
            )


def file_request(fullpath):
    """Permit Bazaar to download a branch"""
    if ".bzr" in fullpath.split("/"):
        return open(fullpath, "rb").read()
    else:
        return abort(403)
