### 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/>
###

db = require('../db')
appconfig = require('../appconfig')

class Skeleton
    mapping: {}
    defaults: {}
    blacklist: []
    whitelist: off
    constructor: (args...) ->
        @set(@defaults)
        @set(object) for object in args

    set: (obj) =>
        for own k, v of obj
            continue if k in @blacklist
            this[k] = v

    compact: () =>
        my = this
        copy = (parent) ->
            if Array.isArray(parent)
                return (copy(entry) for entry in parent)
            data = {}
            for own key, value of parent
                continue unless value
                continue unless not my.whitelist or key in my.whitelist
                continue if typeof value is 'function' or key in my.blacklist
                value = my.mapping[key](value) if my.mapping[key]
                if value.toISOString
                    data[key] = value.toISOString()
                else
                    data[key] = value
                data[key] = copy(data[key]) if typeof data[key] is 'object'
            data
        copy(this)

    save: (callback) =>
        db.save(@compact(), callback or () -> null)


# takes a couchdb list of objects, looks up any _id entries recursivly and
# injects the corrosponding objects inside the list, if they are known
Skeleton.compile = (got) ->
    objects = all: {length:0}, by_type: {}, missing: {length:0}
    return objects unless got and typeof got is 'object'
    data = (x.value for x in got)
    objects.by_type[model] = [] for model in appconfig.models
    # initialize data objects
    for object in data
        if object.type
            Model = models[object.type]
            throw Error("Model #{object.type} not found.") unless Model
            object = new Model(object)
            objects.by_type[object.type].push(object)
        objects.all[object._id] = object
        objects.all.length++
    # replace references and find missing _id's
    replace_references = (object) ->
        for own key, value of object
            continue unless typeof value is 'object'
            if Array.isArray(value)
                object[key] = (replace_references({obj}).obj for obj in value)
            else if '_id' of value and not ('type' of value)
                # a db references is simply just: {_id:<id>}
                # when already replaced, there will be a type
                if (referenced = objects.all[value._id])
                    object[key] = referenced
                    replace_references(referenced)
                else
                    objects.missing[value._id] = yes
                    objects.missing.length++
        object
    replace_references(object) for own _, object of objects.all
    objects

#  load all models

models = {}

process.nextTick () ->
    for model in appconfig.models
        try models[model] = require("./#{model}")
        catch err
            throw Error("Can't load Module #{model} (#{err}).")


# exports


module.exports = Skeleton

