/* appjet:version 0.1 */
/*<script>
This work is licensed under a Creative Commons Attribution-Noncommercial 3.0 United States License
http://creativecommons.org/licenses/by-nc/3.0/us/

...Vortices v2.0
yet another social news network...
*/
import('lib-clean-urls','storage','lib-general','lib-account','lib-jquery','lib-json','lib-atom','lib-tags')
if(!appjet.isPreview && request.headers.Host != 'vortices.vezquex.com' && appjet.appName != 'vtest')
    response.redirect('http://vortices.vezquex.com'+request.path+paramGen(null, true))

if(!storage.laOps.newUser) storage.laOps.newUser = {
    values: {
        upvotes: 0,
        downvotes: 0,
        postCount: 0,
    },
    collections: 'votes,contacts'
}
//storage.laOps = 0
/*//storage.users.remove({})*/

collectionsAre(['nodes'])
if (!storage.showdownlib) storage.showdownlib = wget('http://vezquex.com/scripts/showdown.js')
eval(storage.showdownlib)
var currentTime = new Date()
var url = request.path
var algorithm = (rp.sort || request.cookies.sort || 'Hot')

/*    Feeds
-----------------------------------------------------*/

function get_feed(filt)
{
    var base
    if(filt)
        rp = filt
    else
        delete rp.feed

    if(rp)
    {
        ['parent','deleted'].forEach(function(prop){
            if(rp[prop] == 'undefined') rp[prop] = undefined
        })
        base = storage.nodes.filter(rp)
    }
    else base = storage.nodes.filter({parent: undefined, deleted: undefined}) //main feed

    base = base.sort(sorting['New']).limit(20)

    var entries = {
        base: base,
        i: 0,
        forEach: function(f) {
            this.base.forEach(function(n) {
                return f({
                    author: n.author,
                    title: n.title,
                    link: n.link||'http://vortices.vezquex.com/node/'+n.id,
                    id: 'tag:vortices.vezquex.com,2008:obj/'+n.id,
                    updated: n.time,
                    summary: '',
                    content: markdown(n.text)
            })})}
        }
    page.setMode('plain')
    response.setHeader('Content-Type', 'application/atom+xml')
    print(raw(getMainFeed(
                'Vortices',
                'http://vortices.vezquex.com/feed',
                'tag:vortices.vezquex.com,2008:/1',
                base.first().time,
                entries
    )))
    response.stop(true)
}

/*    Voting
-----------------------------------------------------*/

function post_ajaxvote()
{
    page.setMode('plain')
    if(!user) r = {status: 'Sign in or register before voting.'}
    else
    {
        var r = {status: false, score: vote()}
    }
    print(raw(JSON.stringify(r)))
    response.stop()
}

function vote()
{
    var thisvote
    thisvote = user.votes.filter({node: rp.on}).first()
    var node = getStorable(rp.on)
    if(!thisvote)
    {
        user.votes.add({node: rp.on, type: rp.vote})
        node[rp.vote + 'votes'] += 1
    }
    else if(thisvote.type != rp.vote)
    {
        if(rp.vote == 'down')
        {
            thisvote.type = 'down'
            node.downvotes += 1
            node.upvotes -= 1
        }
        else
        {
            thisvote.type = 'up'
            node.upvotes += 1
            node.downvotes -= 1
        }
    }
    else //undo vote
    {
        node[rp.vote+'votes'] -= 1
        user.votes.remove({node: rp.on})
    }
    return {up: node.upvotes, down: node.downvotes}
}

//Header
if(!(rp.plain || request.path == '/feed' || request.path == '/subFeed'))
{
    page.setTitle('Vortices // freedom, democracy, and sublimity')

page.head.write("""
<link rel="icon" href="http://vezquex.com/images/icons/prism.ico"/>
"""
)
    var sorts = ['Hot', 'New', 'Top', 'Replies']
//    var sorts = ['Hot', 'New', 'Top', 'Replies', 'A-Z']
    sorts.splice(sorts.indexOf(algorithm), 1)
    var resort = UL()
    sorts.forEach(function(s){
        resort.push(LI({id: s}, A({href: paramGen({'sort': s}, true), onclick: "setCookie('sort','"+s+"')", rel: 'nofollow'}, s)))
    })
    resort = LI({id: 'sort'},
        A({href: paramGen({'sort': algorithm}, true), onclick: "setCookie('sort','"+algorithm+"')", rel: 'nofollow'}, algorithm, '...'),
        resort
    )

    var menu =
        UL({id: 'menu'},
            LI(A({href: '/'}, IMG({alt:'Vortices', src: 'http://imagethief.appjet.net/img/LRWqqCtHV.png'}))),
            resort,
            LI(link('/tags','Tags'))
        )

    if(user)
    {
        menu.splice(2,0,LI({id: 'post'}, link('/post','Post')))
        menu.push(
            LI(link('/subscriptions/'+user.id, 'Subscriptions')),
            LI(B(link('/user/'+user.alias+'/blog', user.alias + ''+ (user.upvotes - user.downvotes)))),
            LI(la.signOut())
        )

        //Deleting
        function get_delete()
        {
            var node = getStorable(rp['delete'])
            if(node.author == user.alias && !node.deleted)
            {
                var fields = ['title','link']
                fields.forEach(function(field)
                {
                    node[field] = ''
                })
                node.text = '' //[Deleted]
                node.edit = currentTime
                node.deleted = true
                var nodeTree = [node]
                while(nodeTree[nodeTree.length-1].parent)
                {
                    nodeTree[nodeTree.length] = getStorable(nodeTree[nodeTree.length-1].parent)
                    nodeTree[nodeTree.length-1].replies -= 1
                }
                response.redirect(rp.at)
            }
        }
    }
    else
    {
        menu.push(LI(la.signIn()))
    }

    print(
        menu,
        DIV({id: 'up'}, A({href: '#', title: 'Jump to Top'}, '^'))
    )
}

//Editing
function get_edit()
{
    var node = getStorable(rp.edit)
    if(request.method == 'GET' && (node.wiki == 'true' || node.author == user.alias) && !node.deleted)
    {
        var catEdit = node.parent ? '' : LABEL(SPAN('Tags'),INPUT({value: accessTags(node).join(''), name:'tags', type: 'text'}))
        print(
            H2('Edit'),
            FORM({action:'/edit', 'class': 'submit', method:'post'},
                LABEL(SPAN('Title'),INPUT({id: 'title', name:'title', value:node.title||'',maxlength:200, type: 'text'})),BR(),
                LABEL(SPAN('URL'),INPUT({id: 'link', name:'link', value:node.link||'', type: 'text'})),BR(),
                LABEL(SPAN('Text'),TEXTAREA({id:'text', name:'text'}, raw(node.text))),BR(),
                catEdit,
                INPUT({type:'submit',value:'Edit'}),
                INPUT({type:'hidden',name:'node',value:node.id})
            )
        )
    }
    else
    {
        status('Error: You cannot edit this post. Make sure that you are logged in and that you authored this or it is a wiki.')
    }
}

function get_vote()
{
    if(rp.vote && rp.on)
    {
        var thisvote
        thisvote = user.votes.filter({node: rp.on}).first()
        var node = getStorable(rp.on)
        if(!thisvote)
        {
            user.votes.add({node: rp.on, type: rp.vote})
            node[rp.vote + 'votes'] += 1
        }
        else if(thisvote.type != rp.vote)
        {
            if(rp.vote == 'down')
            {
                thisvote.type = 'down'
                node.downvotes += 1
                node.upvotes -= 1
            }
            else
            {
                thisvote.type = 'up'
                node.upvotes += 1
                node.downvotes -= 1
            }
        }
        else //undo vote
        {
            node[rp.vote+'votes'] -= 1
            user.votes.remove({node: rp.on})
        }
    }
}

/*    Main Posting/Reply Form
-----------------------------------------------------*/
function get_reply(parent, fields)
{
    at = rp.at
    if(!fields) fields = {action: 'submit', title: '',link: '',text: ''}
    if(!parent) var parent = rp.reply
    if(at == 'undefined') at = rp.reply
    var header = 'Reply'
    if(parent) get_node(parent,true)
    else header = 'Post'
    print(H2({id:'reply'},header))
    var form = FORM({action:'/'+fields.action, 'class': 'submit', method:'post'})
    if(fields.title != undefined)
        form.push(LABEL(SPAN('Title'),INPUT({value: fields.title||'', id: 'title', name:'title',maxlength:200, type: 'text'})),BR())
    if(fields.link != undefined)
        form.push(LABEL(SPAN('URL'),INPUT({value: fields.link||'', name:'link', type: 'text'})),BR())
    form.push(LABEL(SPAN('Text'),TEXTAREA({name:'text'},fields.text||'')),BR())
    if(fields.tags != undefined)
        form.push(LABEL(SPAN('Tags'),INPUT({value: fields.tags||'', name:'tags', type: 'text'})),BR())
    if(fields.wiki == '')
        form.push(LABEL(INPUT({type:'checkbox', name:'wiki', value: 'true'}),'Wiki'))
    else if(fields.wiki == 'true')
        form.push(LABEL(INPUT({checked: 'checked', type:'checkbox', name:'wiki', value: 'true'}),'Wiki'))
    form.push(
        INPUT({type:'submit',value:'Post'}),
        INPUT({type:'hidden',name:'parent',value:parent}),
        INPUT({type:'hidden',name:'at',value:at})
    )
    print(form)
}

function get_post()
{
    page.setTitle('Post - Vortices')
    get_reply(undefined, {action: 'submit', title: '',link: '',text: '',tags: '',wiki: ''})
}

function post_submit()
{
    var node = new StorableObject()
    var fields = ['title','parent','link','wiki']
    fields.forEach(function(param)
    {
        if(rp[param]) node[param] = trim(rp[param])
    })
    node.text = sanitize(trim(rp.text))
//    if(!node.parent) node.parent = null
    node.author = user.alias
    node.downvotes = 0
    node.upvotes = 1
    node.replies = 0
    node.time = currentTime
    storage.nodes.add(node)
    user.votes.add({node: node.id, type: 'up'})
    user.postCount += 1
    if(rp.tags)
        addTags(node, rp.tags)
    if(node.parent)
    {
        var parent = node.parent
        while(parent)
        {
            parent = getStorable(parent)
            parent.replies += 1
            parent = parent.parent
        }
    }
    if(!rp.at) rp.at = node.id
    response.redirect('node/'+rp.at+'#'+node.id)
}

function post_edit()
{
    var node = getStorable(rp.node)
    var fields = ['title','link']
    fields.forEach(function(param)
    {
        node[param] = rp[param] ? trim(rp[param]) : ''
    })
    if(rp.tags)
    {
        addTags(node, rp.tags)
    }
    node.text = sanitize(trim(rp.text))
    node.edit = currentTime
    var where = rp.at
    if(!where) where = node.id
    rp = []
    response.redirect('node/'+where)
}

var general =
{

    'A-Z': function(a, b, prop)
    {
        (a[prop]||'').toLowerCase().localeCompare((b[prop]||'').toLowerCase())
    },

    'greatest': function(a, b, prop) { return b[prop] - a[prop] },
    'longest': function(a, b, prop) { return b[prop].size() - a[prop].size() }
}

var sorting = {

    user: {

        'A-Z': function(a, b) { return general['A-Z'](a, b, 'login')},

        Replies: function(a, b) { return general['greatest'](a, b, 'postCount')},

        Hot: function(a, b) { return (b.upvotes - b.downvotes) * Math.abs(b.upvotes - b.downvotes) / (currentTime - b.registered.getTime()) - (a.upvotes - a.downvotes) * Math.abs(a.upvotes - a.downvotes) / (currentTime - a.registered.getTime()) }
    },

    'A-Z': function(a, b) { return general['A-Z'](a, b, 'title')},

    Top: function(a, b) { return b.upvotes - b.downvotes - a.upvotes + a.downvotes },

    Hot: function(a, b) { return (b.upvotes - b.downvotes) * Math.abs(b.upvotes - b.downvotes) / (currentTime - b.time.getTime()) - (a.upvotes - a.downvotes) * Math.abs(a.upvotes - a.downvotes) / (currentTime - a.time.getTime()) },

    RedditHot: function(a, b) { return RedditHot(b) - RedditHot(a) },

    New: function(a, b) { return a.id > b.id },
    Old: function(a, b) { return a.id < b.id },

    Replies: function(a, b) { return general.greatest(a, b, 'replies')},
}

function RedditHot(b)
{
    s = b.upvotes - b.downvotes
    order = Math.log(Math.max(Math.abs(s), 1), 10)
    if (s > 0) sign = 1
    else if(s < 0) sign = -1
    else sign = 0
    seconds = b.time.getTime()/1000 - 1199174400
    return order + sign * seconds / 45000
}

//Home page
function get_main()
{
    h1 = UL({'class':'headlines'})
    pageLinks = get_filter({parent: undefined, deleted: undefined}, h1)
    print(
        h1,
        pageLinks,
        P({id: 'footer'},
            link('/users', pluralize(storage.users.size(), 'user', 'users')), ' | ',
            pluralize(storage.nodes.size(), 'post', 'posts'), ' | ',
            link('/node/obj-LGrYTPdrt', 'help')
        )
    )
}

function get_tags()
{
    var tags = UL({'class':'headlines'})
    var pageLinks = paginate(storage.tags.sortBy('-count'), function(tag){
        tags.push(LI(A({'class': 'headline', href: '/tag/'+tag.name}, tag.name, ' ', SPAN(tag.count))))
    })
    print(H1('Tags'), tags, pageLinks())
}

function get_tag(tag)
{
    tag = tag || rp.tag
    h1 = UL({'class':'headlines'})
    pL = get_filter(tagged(tag), h1)
    print(H1(tag), h1, pL)
}

//Custom page
function get_filter(params, headlinesOnly)
{
    if(!params)
    {
        delete rp.filter
        var params = rp
    }

    var list = storage.nodes.filter(params).sort(sorting[algorithm])

    page.head.write('<link rel="alternate" href="/feed'+paramGen(params)+'" type="application/atom+xml"/>')

    fn = get_node
    if(headlinesOnly) fn = function(n){ headlinesOnly.push(renderHeadline(n)) }
    var pageLinks = paginate(list, function(n){fn(n, true)})
    x = pageLinks()
    if(!headlinesOnly) print(x)
    else return x
}

function get_wiki()
{
    h1 = UL({'class':'headlines'})
    get_filter({wiki: 'true'}, h1)
    print(H1('Wikis'), h1)
}

//Individual Node
function get_node(node,noChildrenView)
{
    if(!node) var node = getStorable(rp.node)
    if(typeof node == 'string') var node = getStorable(node)
    if(rp.raw) print(node)
    else
    {
        if(!noChildrenView) page.setTitle((node.title||'[Untitled]')+' - Vortices')
        if(!noChildrenView) noChildrenView = false
        print(renderNode(node,noChildrenView))
    }
}

function subscriptions(subs)
{
    who = getStorable(subs)
    var contactsList = []
    myFriends = who.contacts.filter({relationship: ['friend', 'mutual']})
    myFriends.forEach(function(contact){
        contactsList.push(contact.name)
    })
    return {who: who, contactsList: contactsList}
}

function get_subFeed()
{
    s = subscriptions(rp.id)
    get_feed({author: s.contactsList})
}

function get_subscriptions()
{
    s = subscriptions(rp.subscriptions)
    page.setTitle((s.who.alias || '[Anonymous]') +'\'s Subscriptions | Vortices')
    print(H1((s.who.alias || '[Anonymous]')+'\'s Subscriptions'))
    page.head.write('<link rel="alternate" href="/subFeed?id='+s.who.id+'" type="application/atom+xml"/>')
    get_filter({author: s.contactsList})
}

//User page
function get_users()
{
    var list = TABLE({'class': 'users'},
            COL(),
            TR(
                TH(link('?sort=A-Z','User')),
                TH(link('?sort=Top','Points')),
                TH(link('?sort=Replies','Posts'))
            )
        )

    userSort = sorting.user[algorithm] ? sorting.user[algorithm] : sorting[algorithm]
    var us = storage