var ClassifyView = Class.create({
    initialize: function (constraints, input) {
        // console.group ('data()');
        // console.log ('constraints are %o', constraints);
        this.data = $A(input);
        // console.timeEnd ('Loading data');
        this.constraints = $H(constraints);
        // console.time ('Fixing up data');
        $R(0, this.data.size () - 1).each (function (i) {
                                          this.data[i].idx = i;
                                          if (this.data[i].confidence >= 70) {
                                              if (this.data[i].isspam == 1) {
                                                  this.data[i].spam = 't';
                                                  this.data[i].css = 'spam';
                                              } else {
                                                  this.data[i].spam = 'f';
                                                  this.data[i].css = 'ham';
                                              }
                                          }
                                          if (this.data[i].sender)
                                              this.data[i].sender = this.data[i].sender.escapeHTML();
                                          if (this.data[i].tests)
                                              this.data[i].tests = this.data[i].tests.replace (/,/g, ', ');
                                          if (this.data[i].subject)
                                              this.data[i].subject = this.data[i].subject.escapeHTML();
                                      }, this);
        // console.timeEnd ('Fixing up data');
        // console.log ('Getting #pages');
        this.container = $('pages');
        // console.log ('Getting viewdiv template');
        var viewdiv = this.container.retrieve ('viewdiv');
        // console.log ('Evaluating viewdivtemplate');
        var divhtml = viewdiv.evaluate (this.constraints);
        // console.log ('Insert viewdiv template %s', divhtml);
        this.container.insert (divhtml);
        // console.log ('Retrieving new viewdiv');
        this.div = $(this.constraints.get ('id'));
        // console.log ('page is %o', this.div);
        this.table = this.div.down ('table.result');
        // console.log ('table is %o', this.table);
        this.div.observe ('change', this.change.bind (this));
        this.div.observe ('click', this.click.bind (this));
        this.div.observe ('ClassifyView:adjustpage', this.adjustpage.bind (this));
        this.div.observe ('ClassifyView:submit', this.submit.bind (this));
        this.div.observe ('ClassifyView:checkpos', this.checkpos.bind (this));
        this.div.observe ('ClassifyView:count', this.count.bind (this));
        this.div.observe ('ClassifyView:cross', this.cross.bind (this));
        this.div.observe ('ClassifyView:display', this.display.bind (this));
        this.div.observe ('ClassifyView:host', this.host.bind (this));
        this.div.observe ('ClassifyView:firstpage', this.firstpage.bind (this));
        this.div.observe ('ClassifyView:lastpage', this.lastpage.bind (this));
        this.div.observe ('ClassifyView:mark', this.mark.bind (this));
        this.div.observe ('ClassifyView:markall', this.markall.bind (this));
        this.div.observe ('ClassifyView:move', this.move.bind (this));
        this.div.observe ('ClassifyView:nextitem', this.nextitem.bind (this));
        this.div.observe ('ClassifyView:nextpage', this.nextpage.bind (this));
        this.div.observe ('ClassifyView:previtem', this.previtem.bind (this));
        this.div.observe ('ClassifyView:prevpage', this.prevpage.bind (this));
        this.div.observe ('ClassifyView:render', this.render.bind (this));
        this.div.observe ('ClassifyView:search', this.search.bind (this));
        this.div.observe ('ClassifyView:similar', this.similar.bind (this));
        // console.log ('Storing some parameters and data');
        this.page = 0;
        this.ipp = 300;
        this.title = this.constraints.get ('title');
        // console.log ('Checking whether to setup paging');
        this.items = this.data.size ();
        // console.log ('We have %s items', this.items);
        if (this.items > 300) {
            this.pages = (this.items / 300) | 0;
            // console.log ('We have %s pages', this.pages);
            // console.log ('Constructing options HTML');
            var options = $R(0, this.pages).map (function (i) {
                                                // console.log ('Page %s', i);
                                                return '<option value="' + i + '">' + (i+1) + '</option>';
                                            }).join ('');
            // console.log ('Loading options HTML');
            $$('form.paging').each (function (el) {
                                        el.down ('select').innerHTML = options;
                                        el.show ();
                                    });
        }
        this.div.fire ('ClassifyView:adjustpage');
        // console.groupEnd ();
    },

    adjustpage: function (event) {
        // console.group ('adjustpage()');
        if (this.page == 0) {
            // console.log ('On first page');
            $$('input.first', 'input.prev').invoke ('writeAttribute', 'disabled', true);
            // console.log ('Disabled first and prev');
            if (this.pages > 0) {
                // console.log ('Multiple pages');
                $$('input.last', 'input.next').invoke ('writeAttribute', 'disabled', false);
                // console.log ('Enabled last and next');
            }
        } else if (this.page == this.pages) {
            // console.log ('On last page');
            $$('input.last', 'input.next').invoke ('writeAttribute', 'disabled', true);
                // console.log ('Disabled last and next');
            if (this.pages > 0) {
                // console.log ('Multiple pages');
                $$('input.first', 'input.prev').invoke ('writeAttribute', 'disabled', false);
                // console.log ('Enabled first and prev');
            }
        } else {
            // console.log ('Neither first nor last page');
            if (this.pages > 0) {
                // console.log ('Multiple pages');
                $$('input.first', 'input.last', 'input.next', 'input.prev').invoke ('writeAttribute', 'disabled', false);
                // console.log ('Enabled all buttons');
            }
            
        }

        // console.log ('Deselecting all pages');
        $$('select.page option').invoke ('writeAttribute', 'selected', false);
        // console.log ('Selecting current page');
        var select = 'select.page option[value="' + this.page + '"]';
        // console.log ('Looking for %s', select);
        $$(select).invoke ('writeAttribute', 'selected', true);
        // console.log ('Calling render');
        this.div.fire ('ClassifyView:render');
        // console.log ('Done with render');
        // console.groupEnd ();
    },

    checkpos: function (event) {
        // console.group ('checkpos()');
        var el = event.findElement ('tbody');
        // console.log ('Checking element %o', el);
        if (el) {
            // console.log ('Getting dimensions and offsets');
            var viewport = document.viewport.getDimensions ();
            var tbody = el.getDimensions();
            var offset = el.viewportOffset ();
            if (offset.left < 0 || offset.top < 0 || offset.top + tbody.height > viewport.height || offset.left > viewport.width) {
                // console.log ('Scrolling to element');
                el.scrollTo ();
            }
        }
        // console.groupEnd ();
    },

    change: function (event) {
        // console.group ('change()');
        var el = event.findElement ();
        // console.log ('Checking element %o', el);
        if (el.match ('select.page')) {
            el.fire ('ClassifyView:topage');
        }
        // console.log ('Stopping propagation');
        event.stop();
        // console.groupEnd ();
    },

    click: function (event) {
        // console.group ('click()');
        var el = event.findElement ();
        if (el.match ('a.count')) {
            el.fire ('ClassifyView:count');
        } else if (el.match ('a.cross')) {
            el.fire ('ClassifyView:cross');
        } else if (el.match ('a.display')) {
            el.fire ('ClassifyView:display', 'rendered');
        } else if (el.match ('a.host')) {
            el.fire ('ClassifyView:host');
        } else if (el.match ('a.similar')) {
            el.fire ('ClassifyView:similar');
        } else if (el.match ('a.raw')) {
            el.fire ('ClassifyView:display', 'raw');
        } else if (el.match ('a.search')) {
            el.fire ('ClassifyView:search');
        } else if (el.match ('input.first')) {
            el.fire ('ClassifyView:firstpage');
        } else if (el.match ('input.last')) {
            el.fire ('ClassifyView:lastpage');
        } else if (el.match ('input.markclear')) {
            el.fire ('ClassifyView:markall');
        } else if (el.match ('input.markham')) {
            el.fire ('ClassifyView:markall', 'f');
        } else if (el.match ('input.markspam')) {
            el.fire ('ClassifyView:markall', 't');
        } else if (el.match ('input.next')) {
            el.fire ('ClassifyView:nextpage');
        } else if (el.match ('input.prev')) {
            el.fire ('ClassifyView:prevpage');
        } else if (el.match ('input.submit')) {
            el.fire ('ClassifyView:submit');
        } else {
            el.fire ('ClassifyView:move');
        }
        // console.log ('Stopping propagation');
        event.stop();
        // console.groupEnd ();
    },

    count: function (event) {
        // console.group ('count()');
        this.container.fire ('Classify:count', {link: event.findElement ('a'), type: event.findElement ('td').className, value: event.findElement ('td').down ('a').innerHTML, confidence: this.constraints.get ('confidence')});
        // console.groupEnd ();
    },

    cross: function (event) {
        // console.group ('cross()');
        var parameters = {type: event.findElement ('td').className, value: event.findElement ('td').down ('a').innerHTML, confidence: this.constraints.get ('confidence')};
        
        this.container.fire ('Classify:cross', {type: event.findElement ('td').className, value: event.findElement ('td').down ('a').innerHTML, confidence: this.constraints.get ('confidence')});
        // console.groupEnd ();
    },

    display: function (event) {
        // console.group ('display()');
        var tbody = event.findElement ('tbody');
        // console.log ('Getting tbody index');
        var idx = tbody.readAttribute ('idx');
        var content = event.memo;
        if (this.data[idx].message) {
            var header = this.data[idx].message.header;
            // console.log ('Got header %o', header);
            var body = this.data[idx].message[content];
            // console.log ('Got body %o', body);
            // console.log ('Instantiating window');
            var viewport = document.viewport.getDimensions ();
            Modalbox.show (header + body, {autoFocusing: false, height: viewport.height, width: viewport.width, title: 'Message ' + this.data[idx].sha});
            // console.log ('Done showing');
        } else {
            var sha = tbody.identify ();
            // console.log ('Retrieving message with sha %s', sha);
            var constraints = new Hash;
            constraints.set ('sha', sha);
            // console.log ('Making request with constraints %s', constraints.toJSON ());
            new Ajax.Request ('/classify/message', {
                                  contentType: 'application/json',
                                  evalJSON: true,
                                  method: 'post',
                                  onFailure: this.failure.bind (this),
                                  onSuccess: function (response) {
                                      // console.log ('Got response');
                                      var data = $H(response.responseJSON).get (sha);
                                      // console.log ('Got data %o', data);
                                      this.data[idx].message = [];
                                      this.data[idx].message.header = data.header;
                                      this.data[idx].message.raw = "<pre>" + data.raw.escapeHTML() + "</pre>";
                                      this.data[idx].message.rendered = data.rendered;
                                      // console.log ('Re-sending message');
                                      tbody.fire ('ClassifyView:display', content);
                                  }.bind (this),
                                  postBody: Object.toJSON(constraints)
                              });
            
        }
        // console.groupEnd ();
    },

    failure: function () {
        alert ('Failed to retrieve list');
    },

    firstpage: function (event) {
        // console.group ('nextpage()');
        var el = event.findElement ();
        if (this.page > 0) {
            this.page = 0;
            el.fire ('ClassifyView:adjustpage');
        }
        // console.groupEnd ();
    },

    host: function (event) {
        // console.group ('host()');
        var el = event.element ();
        var ip = el.parentNode.firstChild.innerHTML;
        new Ajax.Request ('/classify/reverse/' + ip, {
                              onFailure: this.failure.bind (this),
                              onSuccess: function (response) {
                                  el.replace (response.responseText);
                              }
                          });
        // console.groupEnd ();
    },

    lastpage: function (event) {
        // console.group ('nextpage()');
        var el = event.findElement ();
        if (this.page < this.pages) {
            this.page = this.pages;
            el.fire ('ClassifyView:adjustpage');
        }
        // console.groupEnd ();
    },

    mark: function (event) {
        // console.group ('mark()');
        var row = event.findElement ('tbody');
        var spam = event.memo;
        var idx = row.readAttribute ('idx');
        this.data[idx].spam = spam;
        var classes = new Array;
        if (row.hasClassName ('current')) {
            classes.push ('current');
        }
        
        if (spam == 't') {
            classes.push ('spam');
        } else if (spam == 'f') {
            classes.push ('ham');
        }

        row.className = classes.join (' ');
        row.fire ('ClassifyView:nextitem');
        // console.groupEnd ();
    },

    markall: function (event) {
        // console.group ('markall()');
        var div = event.findElement ('div');
        var spam = event.memo;

        // console.log ('Marking all rows as %s', spam);
        this.data.each (function (r) {
                            if (r.spam != spam) {
                                r.spam = spam;
                                r.html = '';
                                r['class'] = r.spam == 't' ? 'spam' : 'ham';
                            }
                        });

        // console.log ('Marking all visible elements elements as %s', spam);
        div.select ('tbody').each (function (e) {
                                       var classes = new Array;
                                       if (e.hasClassName ('current')) {
                                           classes.push ('current');
                                       }
        
                                       if (spam == 't') {
                                           classes.push ('spam');
                                       } else if (spam == 'f') {
                                           classes.push ('ham');
                                       }

                                       e.className = classes.join (' ');
                                   });
        // console.groupEnd ();
    },

    move: function (event) {
        // console.group ('move()');
        var tbody = event.findElement ('tbody');
        if (tbody) {
            // console.log ('Working with tbody %o', tbody);
            // console.log ('Working with table %o', this.table);
            if (!this.table.match ('.current')) {
                // console.log ('Removing current from other tables');
                $$('table.current').invoke ('removeClassName', 'current');
                // console.log ('Setting current on this table');
                this.table.addClassName ('current');
            }
            // If the tbody's not already current, make sure it's the only one marked so
            if (!tbody.match ('.current')) {
                // console.log ('Removing current from other tbodies');
                this.table.select ('.current').invoke ('removeClassName', 'current');
                // console.log ('Adding current to this tbody');
                tbody.addClassName ('current');
            }
            // console.log ('Repositioning');
            tbody.fire ('ClassifyView:checkpos');
        }
        // console.groupEnd ();
    },

    nextitem: function (event) {
        // console.group ('nextitem()');
        var current = event.element ('tbody');
        var next = current.next ('tbody');
        if (next) {
            next.addClassName ('current');
            current.removeClassName ('current');
            next.fire ('ClassifyView:checkpos');
        }
        // console.groupEnd ();
    },

    nextpage: function (event) {
        // console.group ('nextpage()');
        var el = event.findElement ();
        var newpage = this.page + 1;
        if (newpage <= this.pages) {
            this.page = newpage;
            el.fire ('ClassifyView:adjustpage');
        }
        // console.groupEnd ();
    },

    previtem: function (event) {
        // console.group ('previtem()');
        var current = event.findElement ();
        var previous = current.previous ('tbody');
        if (previous) {
            previous.addClassName ('current');
            current.removeClassName ('current');
            previous.fire ('ClassifyView:checkpos');
        }
        // console.groupEnd ();
    },

    prevpage: function (event) {
        // console.group ('prevpage()');
        var el = event.findElement ();
        var newpage = this.page - 1;
        if (newpage >= 0) {
            this.page = newpage;
            el.fire ('ClassifyView:adjustpage');
        }
        // console.groupEnd ();
    },

    render: function (event) {
        // console.group ('render()');
        // console.log ('Calculating start');
        var start = this.page * this.ipp;
        // console.log ('Start is %s', start);
        var end = Math.min (this.items - 1, start + this.ipp - 1);
        // console.log ('End is %s', end);
        // console.log ('Retrieving row template');
        var template = this.container.retrieve ('viewrow');
        // console.time ('Generating html');
        var html = $R(start, end).map (function (i) {
                                           if (!this.data[i].html) {
                                               this.data[i].html = template.evaluate (this.data[i]);
                                           }
                                           return this.data[i].html;
                                       }, this).join ('');
        // console.timeEnd ('Generating html');
        // console.log ('Table element is %o', this.table);
        // console.time ('Clearing out existing rows');
        this.table.select ('tbody').invoke ('remove');
        // console.timeEnd ('Clearing out existing rows');
        // console.time ('Inserting html');
        this.table.insert (html);
        // console.timeEnd ('Inserting html');
        // console.log ('Considering whether to create a tab');
        if (!this.tabbed) {
            // console.log ('Adding tab for %o', this.div);
            this.div.fire ('Classify:add', this.title);
            this.tabbed = 1;
        }
        // console.log ('Move to first item');
        this.table.down ('tbody').fire ('ClassifyView:move');
        // console.groupEnd ();
    },

    search: function (event) {
        // console.group ('search()');
        this.container.fire ('Classify:search', {type: event.findElement ('td').className, value: event.findElement ('a').innerHTML, confidence: this.constraints.get ('confidence')});
        // console.groupEnd ();
    },

    similar: function (event) {
        // console.group ('similar()');
        this.container.fire ('Classify:similar', {sha: event.findElement ('tbody').identify()});
        // console.groupEnd ();
    },

    submit: function (event) {
        // console.group ('submit()');
        var div = event.findElement ('div');
        // console.log ('Got div %o', div);
        var constraints = $H({f: $A([]), t: $A([]), u: $A([])});
        // console.log ('Initial constraints %o', constraints);
        this.data.each (function (r) {
                            var key = r.spam;
                            // console.log ('key for %s is %s', r.sha, key);
                            if (key)
                                constraints.get (key).push (r.sha);
                        });
        // console.log ('Making request with constraints %s', constraints.toJSON ());
        new Ajax.Request ('/classify/reclassify',{
                              method: 'post',
                              contentType: 'application/json',
                              postBody: Object.toJSON(constraints),
                              evalJSON: true,
                              sanitizeJSON: true,
                              onFailure: this.failure.bind (this),
                              onSuccess: function (response) {
                                  div.retrieve ('li').fire ('ClassifyView:close');
                              }});
        // console.groupEnd ();
    },

    topage: function (event) {
        // console.group ('topage()');
        var el = event.findElement ();
        var option = el.down ('option [selected="selected"]');
        if (option) {
            if (option.value != this.page)
            this.page = option.value;
            el.fire ('ClassifyView:adjustpage');
        }
        // console.groupEnd ();
    }
});

