/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you under the Apache License, Version 2.0 (the
 *  "License"); you may not use this file except in compliance
 *  with the License.  You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing,
 *  software distributed under the License is distributed on an
 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *  KIND, either express or implied.  See the License for the
 *  specific language governing permissions and limitations
 *  under the License.
 */
/*
 * BloglinesClient.groovy - an example of the Bloglines Web Services
 *
 * Written by Marc Hedlund <marc@precipice.org>, September 2004.
 * 
 * Mangled by John Wilson September 2004
 *
 * Small adaptions to JSR Version by Dierk Koenig, June 2005
 *
 * Used in Marc's article at:
 *    http://www.oreillynet.com/pub/a/network/2004/09/28/bloglines.html
 *
 * Requirements:
 *   - install Groovy as detailed at <http://groovy.codehaus.org/>.
 *   - put commons-httpclient-3.0-rc3.jar into GROOVY_HOME/lib
 *       see <http://jakarta.apache.org/commons/httpclient/>.
 *       note: this is currently designed for HttpClient2.x and not HttpClient3.x
 *
 * To Launch:
 *   groovy BloglinesClient.groovy
 *
 * This work is licensed under the Creative Commons Attribution
 * License. To view a copy of this license, visit
 * <http://creativecommons.org/licenses/by/2.0/> or send a letter to
 * Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
 */

import groovy.swing.SwingBuilder
import java.awt.BorderLayout
import javax.swing.JOptionPane
import javax.swing.JSplitPane
import javax.swing.JTree
import javax.swing.ListSelectionModel
import javax.swing.WindowConstants
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.TreeSelectionModel
import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.UsernamePasswordCredentials
import org.apache.commons.httpclient.methods.GetMethod

//Set up global variables and data types
server = 'rpc.bloglines.com'

class Feed {
    def name;
    def id;
    def unread;

    String toString() { (unread == "0" ? name : "${name} (${unread})") }
}

class Item {
    def title;
    def description;

    String toString() { title }
}

// Ask the user for account information (using simple dialogs)
email = JOptionPane.showInputDialog(null, "Email address:", "Log in to Bloglines",
        JOptionPane.QUESTION_MESSAGE)
password = JOptionPane.showInputDialog(null, "Password:", "Log in to Bloglines",
        JOptionPane.QUESTION_MESSAGE)

//Use HTTPClient for web requests since the server requires authentication
client = new HttpClient()
credentials = new UsernamePasswordCredentials(email, password)
client.state.setCredentials("Bloglines RPC", server, credentials)

abstractCallBloglines = { method, parameters ->
    url = "http://${server}/${method}${parameters}"
    try {
        get = new GetMethod(url)
        get.doAuthentication = true
        client.executeMethod(get)
        return get.responseBodyAsStream
    } catch (Exception e) {
        println "Error retrieving <${url}>: ${e}"
    }
}

callBloglinesListsub = abstractCallBloglines.curry('listsubs', '')
callBloglinesGetItems = abstractCallBloglines.curry('getitems')

//Get the list of subscriptions and parse it into a GPath structure
opml = new XmlSlurper().parse(callBloglinesListsub())

//Descend into the subscription outline, adding to the feed tree as we go
treeTop = new DefaultMutableTreeNode("My Feeds")
parseOutline(opml.body.outline.outline, treeTop)

def parseOutline(parsedXml, treeLevel) {
    parsedXml.each { outline ->
        if (outline['@xmlUrl'] != null) {  // this is an individual feed
            feed = new Feed(name: outline['@title'], id: outline['@BloglinesSubId'],
                    unread: outline['@BloglinesUnread'])
            treeLevel.add(new DefaultMutableTreeNode(feed))
        } else {  // this is a folder of feeds
            folder = new DefaultMutableTreeNode(outline['@title'])
            parseOutline(outline.outline, folder)
            treeLevel.add(folder)
        }
    }
}

//Build the base user interface objects and configure them
swing = new SwingBuilder()
feedTree = new JTree(treeTop)
itemList = swing.list()
itemText = swing.textPane(contentType: 'text/html', editable: false)
model = feedTree.selectionModel
model.selectionMode = TreeSelectionModel.SINGLE_TREE_SELECTION
itemList.selectionMode = ListSelectionModel.SINGLE_SELECTION

//Set up the action closures that will react to user selections
listItems = { feed ->
    rssStream = callBloglinesGetItems("?s=${feed.id}&n=0")
    if (rssStream != null) {
        try {
            rss = new XmlSlurper().parse(rssStream)
            itemList.listData = rss.channel.item.collect(new Vector()) {
                new Item(title: it.title, description: it.description)
            }
            feed.unread = "0"  // update the unread item count in the feed list
        } catch (Exception e) {
            println "Error during <${feed.name}> RSS parse: ${e}"
        }
    }
}

feedTree.valueChanged = { event ->
    itemText.text = ""  // clear any old item text
    node = (DefaultMutableTreeNode) feedTree.getLastSelectedPathComponent()
    if (node != null) {
        feed = node.userObject
        if (feed instanceof Feed && feed.unread != "0") {
            listItems(feed)
        }
    }
}

itemList.valueChanged = { event ->
    item = event.source.selectedValue
    if (item instanceof Item && item?.description != null) {
        itemText.text = "<html><body>${item.description}</body></html>"
    }
}

//Put the user interface together and display it
gui = swing.frame(title: 'Bloglines Client', location: [100, 100], size: [800, 600],
        defaultCloseOperation: WindowConstants.EXIT_ON_CLOSE) {
    panel(layout: new BorderLayout()) {
        splitPane(orientation: JSplitPane.HORIZONTAL_SPLIT, dividerLocation: 200) {
            scrollPane {
                widget(feedTree)
            }
            splitPane(orientation: JSplitPane.VERTICAL_SPLIT, dividerLocation: 150) {
                scrollPane(constraints: BorderLayout.CENTER) {
                    widget(itemList)
                }
                scrollPane(constraints: BorderLayout.CENTER) {
                    widget(itemText)
                }
            }
        }
    }
}

gui.show()
