var Classify = Class.create({
    initialize: function (args) {
        // console.group ('Classify:initialize()');
        this.container = $('pages');
        // console.log ('Get template for a crosstab item and table');
        this.container.store ('crossrow', new Template (this.serialize (this.container.down ('div.cross tbody > tr').remove ())));
        this.container.store ('crossdiv', new Template (this.serialize (this.container.down ('div.cross').remove ())));
        // console.log ('Get template for a group item and table');
        this.container.store ('grouprow', new Template (this.serialize (this.container.down ('div.group tbody > tr').remove ())));
        this.container.store ('groupdiv', new Template (this.serialize (this.container.down ('div.group').remove ())));
        // console.log ('Get template for a view item and table');
        this.container.store ('viewrow', new Template (this.serialize (this.container.down ('div.view tbody').remove ())));
        this.container.store ('viewdiv', new Template (this.serialize (this.container.down ('div.view').remove ())));
        // console.log ('Add observer for adding tabs');
        this.container.observe ('Classify:add', this.add.bind (this));
        this.container.observe ('Classify:count', this.count.bind (this));
        this.container.observe ('Classify:cross', this.cross.bind (this));
        this.container.observe ('Classify:group', this.group.bind (this));
        this.container.observe ('Classify:search', this.search.bind (this));
        this.container.observe ('Classify:similar', this.similar.bind (this));
        this.container.observe ('Classify:subject', this.subject.bind (this));
        // console.log ('Add handlers tabs actions');
        this.searches = $('tabs');
        this.searches.observe ('click', this.click.bind (this));
        this.searches.observe ('Classify:close', this.close.bind (this));
        this.searches.observe ('Classify:select', this.select.bind (this));
        // console.log ('Add form actions');
        var forms = this.container.down('div.search');
        forms.observe ('submit', this.submit.bind (this));
        forms.fire ('Classify:add', 'Search');
        // console.log ('Capture all keypress actions');
        document.observe ('keypress', this.keypress.bind (this));
        // console.groupEnd ();
    },

    add: function (event) {
        // console.group ('Classify:add()');
        var div = event.findElement ('div');
        // console.log ('Got nearest div %o', div);
        var title = event.memo; 
        // console.log ('Got title %o', title);
        this.searches.insert ('<li><a class="select" href="#">' + title + '</a>    [<a class="close" href="#">X</a>]</li>');
        // console.log ('Inserted new tab');
        var select = this.searches.select ('li').last();
        // console.log ('Found new tab %o', select);
        select.store ('div', div);
        div.store ('li', select);
        select.fire ('Classify:select');
        // console.log ('Stopping propagation');
        event.stop();
        // console.groupEnd ();
    },

    click: function (event) {
        // console.group ('Classify:click()');
        var a = event.findElement ('a');
        if (a) {
            // console.log ('Got element %o', a);
            if (a.match ('a.close')) {
                // console.log ('Got close message');
                a.fire ('Classify:close');
            } else if (a.match ('a.select')) {
                // console.log ('Got select message');
                a.fire ('Classify:select');
            }
            // console.log ('Stopping propagation');
            event.stop();
        }
        // console.groupEnd ();
    },

    count: function (event) {
        // console.group ('count()');
        var parameters = event.memo;
        // console.log ('Constructing query from parameters %o', parameters);
        if (parameters.type == 'sender') {
            // console.log ('Removing localpart from sender');
            parameters.value = parameters.value.replace (/^[^@]*@/, '');
            // console.log ('Reconstructing value');
            parameters.value = 'sender@' + parameters.value;
        }
        var constraints = new Hash;
        constraints.set ('count', 1);
        constraints.set (parameters.type, parameters.value);
        constraints.set ('confidence', parameters.confidence);
        // console.log ('Making request with constraints %s', constraints.toJSON ());
        new Ajax.Request ('/classify/count', {
                              contentType: 'application/json',
                              evalJSON: true,
                              method: 'post',
                              onFailure: this.failure.bind (this),
                              onSuccess: function (response) {
                                  var count = new Number;
                                  $A(response.responseJSON).each (function (i) {
                                                                      count += new Number (i.count);
                                                                  });
                                  parameters.link.replace (count);
                              },
                              postBody: constraints.toJSON()});
        // console.groupEnd ();
    },

    close: function (event) {
        // console.group ('Classify:close()');
        var li = event.findElement ('li');
        // console.log ('Got element %o', li);
        var closer = li.retrieve ('div');
        // console.log ('References %o', closer || 'unknown id');
        if (closer) {
            // console.log ('Removing li');
            li.remove ();
            // console.log ('Removing div');
            var removed = closer.remove ();
            // console.log ('Removed referenced item %o', removed);
            if (removed.match ('.current')) {
                // console.log ('Removed item was current');
                var current = this.searches.select ('li').last();
                // console.log ('Found new current item %o', current);
                if (current) {
                    // console.log ('Selecting new current item');
                    current.fire ('Classify:select');
                }
            }
        }
        // console.log ('Stopping propagation');
        event.stop();
        // console.groupEnd ();
    },

    cross: function (event) {
        // console.group ('Classify:cross()');
        var parameters = event.memo;
        // console.log ('Constructing constraints from parameters %o', parameters);
        // console.log ('Constructing id from parameters %o', parameters);
        var rawid = 'default';
        // console.log ('Starting with default ID');
        if (parameters.type && parameters.value) {
            // console.log ('Constructing real ID');
            rawid = 'cross-' + parameters.type + "-" + parameters.value;
            // console.log ('rawid is %s', rawid);
            if (parameters.type == 'sender') {
                // console.log ('Removing localpart from sender');
                parameters.value = parameters.value.replace (/^[^@]*@/, '');
                // console.log ('Reconstructing rawid');
                rawid = 'cross-' + parameters.type + "-" + parameters.value;
                // console.log ('Reconstructing value');
                parameters.value = 'sender@' + parameters.value;
            }
        }
        var id = rawid.replace (/[@\.\/]/g, '-');
        // console.log ('id is %s', id);
        var constraints = new Hash;
        constraints.set ('id', id);
        constraints.set (parameters.type, parameters.value);
        constraints.set ('confidence', parameters.confidence || 71);
        constraints.set ('title', parameters.title || parameters.value + ' crosstab with confidence < ' + (parameters.confidence || 71));
        // console.log ('Making request with constraints %s', constraints.toJSON ());
        new Ajax.Request ("/classify/cross", {
                              contentType: 'application/json',
                              evalJS: true,
                              method: 'post',
                              onFailure: this.failure.bind (this),
                              postBody: constraints.toJSON()});
        // console.groupEnd ();
    },

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

    group: function (event) {
        // console.group ('Classify:group()');
        var parameters = event.memo;
        // console.log ('Constructing constraints from parameters %o', parameters);
        var constraints = new Hash;
        constraints.set (parameters.type, 1);
        constraints.set ('id', parameters.type);
        constraints.set ('confidence', parameters.confidence || 70);
        // console.log ('Making request with constraints %s', constraints.toJSON ());
        new Ajax.Request ("/classify/group", {
                              contentType: 'application/json',
                              evalJS: true,
                              method: 'post',
                              onFailure: this.failure.bind (this),
                              postBody: constraints.toJSON()});
        // console.groupEnd ();
    },

    keypress: function (event) {
        var element = event.findElement ();
        // console.log ('Checking for an input field');
        if (element.match ('input[type="text"]'))
            return true;
        // console.group ('Classify:keypress()');
        var character = String.fromCharCode(event.keyCode ? event.keyCode : event.which), element = this.container.down ('div.current tbody.current'), memo, signal;
        // console.log ("Got character %o", character);
        if (character == 'c') {
            // console.log ('Finding current tab');
            element = this.searches.down ('.current');
            signal = 'Classify:close';
        } else if (character == 'd') {
            signal = 'ClassifyView:submit';
        } else if (character == 'H') {
            memo = 'f';
            signal = 'ClassifyView:markall';
        } else if (character == 'h') {
            memo = 'f';
            signal = 'ClassifyView:mark';
        } else if (character == 'j') {
            signal = 'ClassifyView:nextitem';
        } else if (character == 'k') {
            signal = 'ClassifyView:previtem';
        } else if (character == 'm') {
            signal = 'ClassifyView:display';
            memo = 'rendered';
        } else if (character == 'p') {
            // console.log ('Got similar class');
            signal = 'ClassifyView:similar';
        } else if (character == 'r') {
            signal = 'ClassifyView:display';
            memo = 'raw';
        } else if (character == 's') {
            memo = 't';
            signal = 'ClassifyView:mark';
        } else if (character == 'S') {
            memo = 't';
            signal = 'ClassifyView:markall';
        } else if (character == 'u') {
            memo = 'u';
            signal = 'ClassifyView:mark';
        } else if (character == 'U') {
            memo = 'u';
            signal = 'ClassifyView:markall';
        }

        // console.log ('Considering whether to signal');
        if (signal) {
            // console.log ('Sending signal %s to element %o with memo %s', signal, element, memo || '[null]');
            element.fire (signal, memo);
            // console.log ('Stopping propagation');
            event.stop();
        }
        // console.groupEnd ();
        return false;
    },

    search: function (event) {
        // console.group ('search()');
        var arguments = event.memo;
        // console.log ('Got arguments %o', arguments);
        var parameters = new Hash;
        // console.log ('Checking for an ID');
        if (!arguments.id) {
            // console.log ('Constructing ID');
            var rawid = 'default';
            // console.log ('Starting with default ID');
            if (arguments.type && arguments.value) {
                // console.log ('Constructing real ID');
                rawid = arguments.type + "-" + arguments.value;
                // console.log ('rawid is %s', rawid);
                if (arguments.type == 'sender') {
                    // console.log ('Removing localpart from sender');
                    arguments.value = arguments.value.replace (/^[^@]*@/, '');
                    // console.log ('Reconstructing rawid');
                    rawid = arguments.type + "-" + arguments.value;
                    // console.log ('Reconstructing value');
                    arguments.value = 'sender@' + arguments.value;
                }
            }

            parameters.set ('id', rawid.replace (/[@\.\/]/g, '-'));
            // console.log ('id is %s', parameters.get ('id'));
        }
        // console.log ('Checking for type/value specification');
        if (arguments.type && arguments.value) {
            if (!arguments.title) {
                // console.log ('Constructing title');
                var title = arguments.type + 's matching ' + arguments.value;
                if (arguments.confidence) {
                    title = title + ' with confidence < ' + arguments.confidence;
                }
                parameters.set ('title', title);
            }
            // console.log ('Setting search bits');
            parameters.set (arguments.type, arguments.value);
            // console.log ('Removing now-superfluous arguments');
            delete arguments['type'];
            delete arguments['value'];
        }
        parameters = parameters.merge (arguments);
        // console.log ('Making request with parameters %s', parameters.toJSON ());
        // console.time ('Loading data');
        new Ajax.Request ('/classify/search', {
                              contentType: 'application/json',
                              evalJS: true,
                              method: 'post',
                              onFailure: this.failure.bind (this),
                              postBody: parameters.toJSON ()});
        // console.groupEnd ();
    },

    select: function (event) {
        // console.group ('Classify:select()');
        var li = event.findElement ('li');
        // console.log ('Got element %o', li);
        if (!li.match ('.current')) {
            // console.log ('Setting element to be current');
            var selected = li.retrieve ('div');
            // console.log ("Selected div is %o", selected);
            if (selected) {
                var divs = this.container.select ('div');
                // console.log ("List of other divs %o", divs);
                // console.log ('Making other divs invisible');
                divs.invoke ('removeClassName', 'current');
                divs.invoke ('hide');
                // console.log ('Other divs are hidden');

                // console.log ('Making other tabs non-current');
                this.searches.select ('li').invoke ('removeClassName', 'current');
                // console.log ('Making new tab current');
                li.addClassName ('current');
                // console.log ('%o is now current', li);
                selected.addClassName ('current');
                selected.show ();

                if (selected.match ('.search')) {
                    // console.log ('Looking for current record in search table');
                    // Handle current row setup
                    var row = selected.down ('tbody.current') || selected.down ('tbody');
                    if (row) {
                        // console.log ('Current record is %o', row);
                        row.addClassName ('current').fire ('ClassifyView:checkpos');
                    }
                }
            }
        }
        // console.groupEnd ();
    },

    serialize: function (node) {
        // console.group ('Classify:serialize()');
        var result = Try.these (function () {return (new XMLSerializer ()).serializeToString (node);},
                                function () {return node.xml;}) || false;
        // console.groupEnd ();
        return result;
    },

    similar: function (event) {
        // console.group ('similar()');
        var arguments = event.memo;
        // console.log ('Got arguments %o', arguments);
        var parameters = new Hash;
        // console.log ('Constructing ID');
        var rawid = 'sha ' + arguments.sha;
        parameters.set ('id', rawid.replace (/[ @\.\/]/g, '-'));
        var title = 'Messages like ' + arguments.sha;
        parameters.set ('title', title);
        parameters = parameters.merge (arguments);
        // console.log ('Making request with parameters %s', parameters.toJSON ());
        // console.time ('Loading data');
        new Ajax.Request ('/classify/similar', {
                              contentType: 'application/json',
                              evalJS: true,
                              method: 'post',
                              onFailure: Classify.failure,
                              postBody: parameters.toJSON ()});
        // console.groupEnd ();
    },

    subject: function (event) {
        // console.group ('search()');
        var arguments = event.memo;
        // console.log ('Got arguments %o', arguments);
        var parameters = new Hash;
        // console.log ('Constructing ID');
        var rawid = 'subject ' + arguments.fti;
        parameters.set ('id', rawid.replace (/[ @\.\/]/g, '-'));
        var title = 'Subjects matching ' + arguments.fti;
        parameters.set ('title', title);
        arguments.fti = $A(arguments.fti.split (/\s+/))
            .map (function (i) {return i + ":A";})
            .join (' & ');
        parameters.set ('raw_fti', 1);
        parameters = parameters.merge (arguments);
        // console.log ('Making request with parameters %s', parameters.toJSON ());
        // console.time ('Loading data');
        new Ajax.Request ('/classify/search', {
                              contentType: 'application/json',
                              evalJS: true,
                              method: 'post',
                              onFailure: this.failure.bind (this),
                              postBody: parameters.toJSON ()});
        // console.groupEnd ();
    },

    submit: function (event) {
        // console.group ('Classify:submit()');
        var form = event.findElement ('form');
        if (form) {
            var parameters = form.serialize ({hash: true, submit: false});
            // console.log ('Got element %o', form);
            if (form.match ('form.cross')) {
                // console.log ('Got cross request');
                form.fire ('Classify:cross', parameters);
            } else if (form.match ('form.group')) {
                // console.log ('Got group request');
                form.fire ('Classify:group', parameters);
            } else if (form.match ('form.recent')) {
                // console.log ('Got view message');
                form.fire ('Classify:search', parameters);
            } else if (form.match ('form.similar')) {
                // console.log ('Got similar message');
                form.fire ('Classify:similar', parameters);
            } else if (form.match ('form.subject')) {
                // console.log ('Got subject message');
                form.fire ('Classify:subject', parameters);
            } else if (form.match ('form.view')) {
                // console.log ('Got view message');
                form.fire ('Classify:search', parameters);
            }
            // console.log ('Stopping propagation');
            event.stop();
        }
        // console.groupEnd ();
    }
});
