### codetube
    Copyright (C) 2011 payload payload@lavabit.com
    Copyright (C) 2011 dodo dodo.the.last@gmail.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/>
###

fs = require('fs')
Git = require('treeeater').Git#'../git') # FIXME until socket.io-node-client works
Path = require('path')
{ CacheBackend, Flexcache } = require('../cache')
{ Stream } = require 'stream'

Repocache = new Flexcache CacheBackend, ttl: 1000000, debug:2, debug_serializer: true

class Repository
    constructor: (opts) ->
        throw Error("give me a path to a git repo!") unless opts?.path?
        (this[key] = value) for own key, value of opts
        @git ?= new Git { cwd: @path }
        @branch ?= "master"
        @empty ?= true
        @tree ?= {}
        @commit_tree ?= {}
        @clone_url ?= ""
        @description ?= ""
        @commits ?= []
        key = () => return @path

        # we need a special emitter, because treeeater api is eventemmiter or callback
        # as flexcache always return a event emitter when emitter is provided, we
        # emulate the callback on the end event
        emitter = (args...) ->
            ee = new Stream()
            if typeof args[args.length-1] == 'function'
                cb = args.pop()
            if cb
                accu = []
                ee.on 'data', (x) ->
                    accu.push x
                ee.on 'end', ->
                    cb accu
            ee

        @gitcache =
           pull:Repocache.cache(@git.pull, {group:key, name:"pull", multi:true, emitter:emitter})
           commits:Repocache.cache(@git.commits, {group:key, name:"commits", multi:true, emitter:emitter})
           trees:Repocache.cache(@git.trees, {group:key, name:"trees", multi:true, emitter:emitter})
           diffs: Repocache.cache(@git.diffs, {group:key, name:"diffs", multi:true, emitter:emitter})
           cat: Repocache.cache(@git.cat, {group:key, name:"cat", multi:true, emitter:emitter})
           branch: Repocache.cache(@git.branch, {group:key, name:"branch", multi:true, emitter:emitter})

    load_empty: (callback) =>
        @gitcache.branch ([lines]) =>
            lines or= ""
            @branches = []
            for line in lines.toString().split('\n')
                line = line.toString()
                line = line[2 ..] if line[1] is " "
                @branches.push(line) if line
            @empty = not lines.length
            callback(null, @empty)

    type_filetype_map: {
        'blob': 'file'
        'tree': 'folder'
    }

    load_trees: (callback) =>
        if @empty
            @trees = []
            callback(null, @trees)
        else
            @gitcache.trees 'HEAD', (trees) =>
                trees.by_path = by_path = {}
                for tree in trees
                    tree.filetype = "#{@type_filetype_map[tree.type]}"
                    tree.basename = Path.basename(tree.path)
                    tree.dirname  = Path.dirname (tree.path)
                    by_path[tree.path] = tree
                # size calculation
                size = 0
                next = [].concat(trees)
                first = true
                while (next[0] and next[0].dirname != '.') or first
                    first = false
                    next = []
                    for tree in next
                        if tree.type == 'blob'
                            dir       = by_path[tree.dirname]
                            dir.size += tree.size
                            size     += tree.size
                            tree.size = (tree.size or 0) + size
                            next.push(dir)
                @trees = trees
                callback(null, @trees)

    load_commits: (callback) =>
        @load_empty =>
            if @empty
                @trees = []
                callback(null, @trees)
        @gitcache.commits (commits) =>
            @commits = commits
            callback(null, @commits)

    merge_trees_and_commits: (trees, commits) =>
        todo = 0
        path_tree = {}
        for tree in trees
            unless tree.commit
                path_tree[tree.path] = tree
                todo += 1
        for commit in commits
            for path of commit.changes
                tree = path_tree[path]
                if tree
                    tree.commit = commit
                    delete path_tree[path]
                    --todo
            break unless todo

    cat: (path, callback) =>
        @gitcache.cat path, callback

    init: (callback) =>
        fs.mkdir @path, 16877, =>
            post_update_path = Path.join(@path, "hooks", "post-update")
            desc_path = Path.join(@path, 'description')
            post_update = "#!/bin/sh\nexec git update-server-info"
            done = 2
            update_server_info = =>
                @git.update_server_info() unless done -= 1
                callback(null)
            @git.init bare: null, =>
                fs.writeFile post_update_path, post_update, =>
                    fs.chmod(post_update_path, 0755)
                    update_server_info()
                fs.writeFile desc_path, @description, update_server_info

# exports
module.exports = Repository

