package net.bblfish.triples.test;

import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.datatypes.xsd.XSDDatatype;

import java.io.*;
import java.util.*;
import java.text.SimpleDateFormat;


/**
 * Date: May 6, 2004
 * Time: 10:33:01 PM
 * author: hjs
 */
public class Blog2Example {
    Model worldModel = ModelFactory.createDefaultModel();
    static SimpleDateFormat df;

    static {
        df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS z");
        df.getTimeZone().setID("+00:00");
    }


    class Feed extends Entry {
        public Feed(File name) {
            super(name);
            init();
        }

        public Feed(File name, Resource r) {
            super(name, r);
            init();
        }

        void init() {
            entryRes.addProperty(Atom.version, "Atom 0.5");//does this make sense to have a version at this level? Why is that not at the top level of the xml?
            entryRes.addProperty(Atom.generator, "TripledFeeds version 0.5beta");
            entryRes.addProperty(RDF.type, Atom.Feed);
        }

        void setLanguage(String lang) {
            entryRes.addProperty(Atom.lang, "en"); //would it be possible to add xml:lang here (instead of Atom.lang)?
        }

        List entries = new ArrayList();
        void addFeedEntry(Entry entry) {
            entries.add(entry);
            entryRes.addProperty(Atom.entry, entry.entryRes);
        }

        void addNextLink(Entry e) {
            Link next = new Link("next", "application/n3", e.name.toString());
            entryRes.addProperty(Atom.link, next.getResource());
        }

        boolean inverse = false;
        void invert() { inverse = !inverse; }

        void writeAsHtml(Writer out) throws IOException {
            out.write("<table border='1'>");
            super.writeAsHtml(out);
            out.write("<tr><td>entries</td><td colspan='0'><table border='1'>");
            Collections.sort(entries);
            if (inverse) Collections.reverse(entries);
            for (Iterator i = entries.iterator(); i.hasNext();) {
                Entry e = (Entry) i.next();
                e.writeAsHtml(out);
            }
            out.write("</table>");
         }



    }

    class Entry implements Comparable {
        File name;
        Model model = ModelFactory.createDefaultModel();
        Resource entryRes;
        Resource id;

        Entry(File name) {
            this.name = name;
            entryRes = model.createResource(name.toString());
            id = model.createResource("tag:bblfish.net/blog/" + name);
            entryRes.addProperty(RDF.type, Atom.Entry);
            entryRes.addProperty(Atom.id, id);
        }


        /**
         * An entry with a resource different from the file name.
         *
         * @param name     the file name
         * @param resource the name of the entry resource
         */
        Entry(File name, Resource resource) {
            this.name = name;
            entryRes = model.createResource(resource.toString());
            entryRes.addProperty(RDF.type, Atom.Entry);
        }

        void setCreationDate(Date d) {
            entryRes.addProperty(Atom.created, getDateLiteral(d));
        }

        Person author;

        void setAuthor(Person author) {
            this.author = author;
            model.add(author.statements());
            entryRes.addProperty(Atom.author, author.getResource());
        }

        void addContributor(Person contr) {
            model.add(contr.statements());
            entryRes.addProperty(Atom.contributor, contr.getResource());
        }

        void addCopyright() {
            entryRes.addProperty(Atom.copyright, "Creative Commons");
        }


        Resource getResource() {
            return entryRes;
        }

        public int compareTo(Object o) {
            Entry e = (Entry)o;
            return e.creationDate.compareTo(creationDate);
        }

        class Link {
            Resource linkRes;

            Link(String rel, String mime, String href) {
                linkRes = model.createResource(Atom.Link);
                linkRes.addProperty(Atom.rel, rel);
                linkRes.addProperty(Atom.type, mime);
                Resource lnk = model.createResource(href);
                linkRes.addProperty(Atom.href, lnk);
            }

            Resource getResource() {
                return linkRes;
            }
        }


        String title;

        void setTitle(String title) {
            this.title = title;
            Content titleCnt = new Content(title, "text/simple");
            entryRes.addProperty(Atom.title, titleCnt.getResource());
        }

        Date creationDate;

        void setCreated(Date date) {
            this.creationDate = date;
            entryRes.addProperty(Atom.created, getDateLiteral(date));
        }

        Literal getDateLiteral(Date date) {
            String formattedDate = df.format(date);
            Literal d = model.createTypedLiteral(formattedDate,XSDDatatype.XSDdateTime);
            return d;
        }

        String body;

        void setBody(String body) {
            this.body = body;
            Content bodyCnt = new Content(body,"text/html");
            entryRes.addProperty(Atom.content, bodyCnt.getResource());
        }

        void writeAsHtml(Writer out) throws IOException {
            out.write("<tr><td>title</td><td>" + title + "</td>");
            out.write("<tr><td>created</td><td>" + creationDate.toGMTString() + "</td>");
            out.write("<tr><td>author</td><td>" + author.name + "</td>");
            out.write("<tr><td>body</td><td>" + body + "</td>");
            out.write("<tr><td> ---- </td><td> ----</td>");
        }


        class Content {
            Resource content;
            String text;

            Content(String text, String mime) {
                this.text = text;
                content = model.createResource(Atom.Content);
                Literal val = model.createLiteral(text,true);
                content.addProperty(Atom.type, mime);
                content.addProperty(Atom.data, val);
            }

            Resource getResource() {
                return content;
            }

        }


        void publish() throws FileNotFoundException {
            model.setNsPrefix("", Atom.NS);
            model.setNsPrefix("xsd",XSDDatatype.XSD+"#");
            model.setNsPrefix("rdf","http://www.w3.org/1999/02/22-rdf-syntax-ns#");
            model.write(new FileOutputStream(name), "N3-PP",name.toString());

        }

    }

    class Person {
        Statement[] statements = new Statement[4];
        Resource personRes;

        String name;

        Person(String name, String email, String url) {
            this.name = name;
            personRes = worldModel.createResource(Atom.Person);
            Resource webPage = worldModel.createResource(url);
            statements[3] = worldModel.createStatement(personRes, RDF.type, Atom.Person);
            Literal nameLit = worldModel.createTypedLiteral(name);
            statements[0] = worldModel.createStatement(personRes, Atom.name, nameLit);
            Resource emailRes = worldModel.createResource("mailto:"+email);
            statements[1] = worldModel.createStatement(personRes, Atom.email, emailRes);
            statements[2] = worldModel.createStatement(personRes, Atom.url, webPage);
        }

        Resource getResource() {
            return personRes;
        }

        public Statement[] statements() {
            return statements;
        }
    }


    public static void main(String[] args) throws FileNotFoundException {
        Blog2Example b2 = new Blog2Example();

        Person henry = b2.new Person("Henry Story", "hjs@bblfish.net", "http://bblfish.net/");
        Person danny = b2.new Person("Danny Ayers", "danny666@virgilio.it", "http://dannyayers.com/");
        Person asbjorn = b2.new Person("Asbjorn Ulsberg", "asbjorn@tigerstaden.no", "http://asbjorn.no/");
        Person ken = b2.new Person("Ken McLeod", " ken@bitsko.slc.ut.us", "http://bitsko.slc.ut.us/blog");

        Entry entry1 = b2.new Entry(new File("entry.2004-04-22.1010.n3"));
        entry1.addCopyright();
        entry1.setAuthor(henry);
        entry1.setTitle("My third blog entry");
        entry1.setBody("This is the first entry of this example feed. To be precise this is the html representation" +
                " of the entry. The <a href='entry.2004-04-22.1010.n3'>n3 entry itself</a> is editable by WebDav " +
                " like methods. The feed still lacks many features." +
                " It should of course point to this piece of html. Some other things that could be added is an example " +
                " of the entry pointing to its parent (or should" +
                " that be its 'in-reply-to' feed/entry. Or should there be both? <br>You will notice that the feed can " +
                " always be found at <a href='feed.bblfish.n3'>feed.bblfish.n3</a> in <a href='.'>this directory</a>. " +
                " As new entries are added a link object will get" +
                " created in that n3 file pointing to the older links. That will be another n3 file." +
                " You can find the first two blog entries by looking at the feed. "
                );
        entry1.setCreated(new java.util.Date(104, 4, 22, 10, 10, 0));
        entry1.publish();

        Entry entry2 = b2.new Entry(new File("entry.2004-04-23.0912.n3"));
        entry2.addCopyright();
        entry2.setAuthor(henry);
        entry2.setTitle("REST: the thesis");
        entry2.setBody("<a href='http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm'>This thesis</a> by Fielding" +
                " is really worth reading! I think I read it too quickly to do it justice" +
                " and will need to reread it carefully.");
        entry2.setCreated(new java.util.Date(104, 4, 23, 9, 12, 0));
        entry2.publish();

        Entry entry3 = b2.new Entry(new File("entry.2004-04-18.1825.n3"));
        entry3.addCopyright();
        entry3.setAuthor(henry);
        entry3.setTitle("Java and Apple");
        entry3.setBody("Apple is much too secretive about Java. The other day...");
        entry3.setCreated(new java.util.Date(104, 4, 18, 18, 25, 0));
        entry3.publish();

        Entry entry4 = b2.new Entry(new File("entry.2004-04-10.2320.n3"));
        entry4.addCopyright();
        entry4.setAuthor(henry);
        entry4.setTitle("Java and Apple");
        entry4.setBody("Apple is much too secretive about Java. The other day...");
        entry4.setCreated(new java.util.Date(104, 4, 10, 23, 20, 0));
        entry4.publish();

        Feed fentry = b2.new Feed(new File("entry.2004-05-19.2020.n3"));
        fentry.addCopyright();
        fentry.setAuthor(henry);
        fentry.setTitle("FeedisAEntry");
        fentry.setBody("By reconstructing the UML of Danny Ayers Atom in OWL model we can" +
                " show that a feed is an Entry. This chimes with some other very interesting " +
                " consequences of the  REST architecture such as that  " +
                " <a href='http://www.markbaker.ca/2002/08/HowContainersWork/'>described by Mark Baker</a>." +
                " A fuller explanation of my original thinking with graphics can be found " +
                " <a href='http://www.imc.org/atom-syntax/mail-archive/msg04494.html'>on this post</a> of the Atom" +
                " mailing list. " +
                " The OWL model I have used is <a href='atom.owl'>here</a>. I have moved back to something" +
                " a little closer to Danny Ayer's original model, by removing the State object, that I really" +
                " only needed in my database representation. The model can be represent like this in UML" +
                " <img src='Atom-June-25-UML-inherited-simple.jpg'><br> except that I have left out the" +
                " EntryState object and merged it back into Entry as in Danny Ayers' " +
                "<a href='http://semtext.org/atom/index.html'>original Atom OWL</a> model.");
        fentry.setCreated(new java.util.Date(104, 5, 19, 20, 20, 0));

        Entry fentry1 = b2.new Entry(new File("entry.2004-05-20.2320.n3"));
        fentry1.addCopyright();
        fentry1.setAuthor(danny);
        fentry1.setTitle("Interesting idea.");
        fentry1.setBody("That is an interesting idea. " +
                "So you are saying that if I add an entry your post " +
                " it is going to turn into a feed? Pretty neat :-)");
        fentry1.setCreated(new java.util.Date(104, 5, 11, 23, 20, 0));
        fentry1.publish();

        Entry fentry2 = b2.new Entry(new File("entry.2004-05-22.0913.n3"));
        fentry2.addCopyright();
        fentry2.setAuthor(asbjorn);
        fentry2.setTitle("Mapping to XML");
        fentry2.setBody(" Yes! Look at <a href='entry.2004-05-10.2020.n3'>entry.2004-05-10.2020.n3</a> in this directory." +
                " It is a Feed and an Entry at" +
                " the same time.<br> How are you going to map this into Atom XML?");
        fentry2.setCreated(new java.util.Date(104, 5, 22, 9, 13, 0));
        fentry2.publish();

        Entry fentry3 = b2.new Entry(new File("entry.2004-05-22.1055.n3"));
        fentry3.addCopyright();
        fentry3.setAuthor(henry);
        fentry3.setTitle("re: Mapping to XML");
        fentry3.setBody("I am trying to get the semantics right first of all. Since N3 can be mapped into XML in " +
                " a number of ways, showing that this can be done in N3 shows that it can be done in XML. It is a" +
                " further task to then work on the Atom XML syntax to make this work there too.<br>" +
                " Noticed that your entry is <a href='entry.2004-05-21.0913.n3'>entry.2004-05-21.0913.n3</a>. It is not" +
                " a feed. Just a plain entry. Though at any time it could turn into a feed, if someone responded to" +
                " your post. It is interesting how this differs from OO programming like java where Objects cannot" +
                " be cast down the class hierarchy once they have been instantiated. (except for Dynamic Proxies perhaps?)");
        fentry3.setCreated(new java.util.Date(104, 5, 22, 10, 55, 0));
        fentry3.publish();

        Entry fentry4 = b2.new Entry(new File("entry.2004-05-23.0105.n3"));
        fentry4.addCopyright();
        fentry4.setAuthor(ken);
        fentry4.setTitle("Not a good idea");
        fentry4.setBody("After <a href='http://www.ilrt.bris.ac.uk/discovery/chatlogs/atom/2004-06-22.html'>" +
                 "a long conversation</a> on #atom irc channel, between me and henry I think we came to the conclusion that " +
                 " making a Feed a subclass of Entry is a bad idea. The following reasons make me think this" +
                " in fact makes this model unRESTful. The main " +
                 " problem is that by allowing  any entry to be turned into a feed we make it reasonable for every entry on the system" +
                " to be polled, since at any time that entry could turn into a new feed. This massively increases the amount of" +
                " requests going over the web, makes the caching of entries meaningless, and looses perhaps the main reason of a feed's" +
                " existence:" +
                " the reduction in traffic it allows for by funnelling changes into one request. <p>" +
                " So even though this model does seem equivalent to the original one proposed by Danny, and though it looks" +
                " cleaner UML wise, it also potentially fails to preserve the main properties of a feed: <ul>" +
                "<li>entries should not change" +
                "<li>the head document of a feed should be the only thing that does change" +
                "</ul>" +
                "Once a feed has reached a certain size its contents should be moved into a feed archive which should" +
                " have be fixed and unchanging.");
        fentry4.setCreated(new java.util.Date(104, 5, 23, 1, 5, 0));
        fentry4.publish();

        Entry fentry5 = b2.new Entry(new File("entry.2004-05-24.1305.n3"));
        fentry5.addCopyright();
        fentry5.setAuthor(henry);
        fentry5.setTitle("Other improvements");
        fentry5.setBody("I completely agree with bitsko's argument above. <p>" +
                " In any case there are other ways of achieving similar simplifactions of Danny's original model that " +
                " now come to my mind. " +
                " <ul>" +
                " <li> One of them is that the link object feels quite superfluous, especially in N3. In N3 every property" +
                " is a link between two resources, so the idea of modeling a link object between resources feels redundant." +
                " Especially between the feed and its successor (<code>next</code>) and its predecessor (<code>prev</code>)." +
                " In N3 it is clear that that should be simply replaced by a successor and predecessor property." +
                " Furthermore we can allready observe in the current example's <a href='feed.bblfish.archive1.n3'>archived feed</a>" +
                " that the <code>start</code> relation is redundant: An archive feed in N3 is simply a number of triples " +
                " with as object the url of the main feed. " +
                " This removes the relation we had between the Feed and the Link object in our original diagram. "+
                " </li>" +
                " <li> The other simplification we can make to the orignal diagram is move the title and other info from the" +
                " Feed class into the " +
                " Entry class. Ie we can refactor our inheritance diagram with a containement relation between a feed and" +
                " its header entry. A well known refactoring: 'Replace Inheritance with Delegation', p. 352, Refactoring," +
                " by Martin Fowler.</li> This makes the feed into a very specialised Entry container." +
                " </li>" +
                " <li> <code>service.edit</code> type links in RESTish world seem unecessary since that is just the url " +
                " of the entry or feed itself. " +
                " </li>" +
                " <li> <code>service.post</code> type links should just be a specialised relation between a feed and a resource." +
                " What type that resource has in OWL I don't yet know. It just seems a very general type of thing."+
                " </li>" +
                "</ul>" +
                " This leaves us then with the following diagram, which is even simpler that the one using inheritance I" +
                " described previously. <img src='Atom-June-25-UML-inherited-simple-2.jpg'><br>" +
                " So perhaps there is some more work to be done on the content object.<br>" +
                " We do of course loose the threading ability that we see here, but that should be easy to reconsitute by" +
                " adding a in-reply-to relation between entries. Oh heck. Let me add it right now...");
        fentry5.setCreated(new java.util.Date(104, 5, 24, 13, 5, 0));
        fentry5.publish();

        fentry.addFeedEntry(fentry1);
        fentry.addFeedEntry(fentry2);
        fentry.addFeedEntry(fentry3);
        fentry.addFeedEntry(fentry4);
        fentry.addFeedEntry(fentry5);
        fentry.invert();
        fentry.publish();


        Feed feed1 = b2.new Feed(new File("feed.bblfish.n3"));
        feed1.addCopyright();
        feed1.setAuthor(henry);
        feed1.setTitle("A Atom in N3 example feed");
        feed1.setBody("An example of an Atom feed in N3. Conversation have been written by Henry Story. These did not" +
                " take place but could have. (source code available in <a href='.'>directory</a>).");
        feed1.setCreated(new java.util.Date(104, 4, 25, 10, 10, 0));
        feed1.addFeedEntry(fentry);
        feed1.addFeedEntry(entry1);
        feed1.addFeedEntry(entry2);

        Feed feed1a = b2.new Feed(new File("feed.bblfish.archive1.n3"), feed1.getResource());
        feed1a.addCopyright();
        feed1a.setAuthor(henry);
        feed1a.addFeedEntry(entry3);
        feed1a.addFeedEntry(entry4);
        feed1a.publish();

        feed1.addNextLink(feed1a);
        feed1.publish();

        try {
            Writer html = new FileWriter(new File("blogexample.html"));
            html.write("<html>\n" +
                    "<head>\n" +
                    "</head>\n" +
                    "<body>");
            feed1.writeAsHtml(html);
            html.write("</body></html>");
            html.flush();
        } catch (IOException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
}
