// This class encapsulates our sideline display behavior so that we
// can simply instantiate on the given page and it will implement all
// necessary behavior
//
// Michael Alan Dorman <mdorman@ironicdesign.com>

var Sideline = Class.create({

    // Get the number of messages we have, calculate the number of
    // pages total, make sure we're not beyond the last page

    adjust: function () {
        // console.group ("adjust()");

        var sideline = $('sideline');

        var count = sideline.retrieve ('data').size();
        $('sideline_count').update(count);

        var total = new Number ($('sideline_total').innerHTML);
        $('sideline_total').update(Math.max (total, count));

        var page_length = sideline.retrieve ('page_length', 300);
        var page_total = (count / page_length) | 0;
        // console.log ("page_total is " + page_total);
        $$('div.formblock span.count').invoke ('update', page_total + 1);

        sideline.store('page_total', page_total);

        var page = Math.min (sideline.retrieve ('page'), page_total);
        // console.log ("current page is " + page);
        $$('div.formblock input[name="current"]').each (function (el) {
                                                            el.value = page + 1;
                                                        });
        sideline.store('page', page);

        // console.log ("Checking backward buttons");
        if (page === 0) {
            $$('form.pageForm input[name="first"]', 'form.pageForm input[name="previous"]').invoke ('writeAttribute', 'disabled', true);
        } else {
            $$('form.pageForm input[name="first"]', 'form.pageForm input[name="previous"]').invoke ('writeAttribute', 'disabled', false);
        }

        // console.log ("Checking forward buttons");
        if (page === page_total) {
            $$('form.pageForm input[name="next"]', 'form.pageForm input[name="last"]').invoke ('writeAttribute', 'disabled', true);
        } else {
            $$('form.pageForm input[name="next"]', 'form.pageForm input[name="last"]').invoke ('writeAttribute', 'disabled', false);
        }

        // console.groupEnd();
    },

    // This generates a request to retrieve the count for the current
    // filter.

    count: function () {
        // console.group ("count()");

        // Hide the data that no longer reflects the query
        $('sideline').hide();

        // Generate the params we're passing, and submit the request
        var param = $H($('filterForm').serialize ({hash: true}));

        new Ajax.Request ("sideline/count",
                          {method: 'post',
                           contentType: 'application/json',
                           postBody: Object.toJSON(param),
                           evalJSON: true,
                           sanitizeJSON: true,
                           onFailure: function () {
                               alert ("Failed to retrieve count");
                           },
                           onSuccess: function (response) {
                               // console.group ("count() callback");
                               $('sideline_count').update (response.responseJSON.count || 0);
                               // console.groupEnd();
                           }});
        // console.groupEnd();
    },

    // This generates a request to view a message, or removes it if it's showing

    display: function (event) {
        // console.group ("display()");

        var sideline = $('sideline');

        event.memo.rows.each(function (row) {
                                 var param = new Hash();
                                 param.set (row.param.recipient, [row.param.sha]);

                                 // console.log ("Checking for existing copy of the message");

                                 var body = $(row.id + '_body');

                                 if (body) {
                                     var sign = $(row.id).down ("input[name='display']");
                                     if (body.visible()) {
                                         body.hide();
                                         sign.value="+";
                                         sign.src="/static/images/show.png";
                                         sign.title="Show this message";
                                     } else {
                                         body.show();
                                         sign.value="-";
                                         sign.src="/static/images/hide.png";
                                         sign.title="Hide this message";
                                     }
                                 } else {
                                     // console.log ("Retrieving copy of the message");
                                     new Ajax.Request ('sideline/message', {
                                                           method: 'post',
                                                           contentType: 'application/json',
                                                           postBody: Object.toJSON(param),
                                                           evalJSON: true,
                                                           sanitizeJSON: true,
                                                           onFailure : function () {
                                                               alert ("Couldn't display entry");
                                                           },
                                                           onSuccess : function (response) {
                                                               var template = sideline.retrieve ('message');
                                                               var r = response.responseJSON.first();
                                                               r.id = row.id;
                                                               if (r.sender)
                                                                   r.sender = r.sender.escapeHTML();
                                                               if (r.recipient)
                                                                   r.recipient = r.recipient.escapeHTML();
                                                               if (r.created)
                                                                   r.created = r.created.escapeHTML();
                                                               if (r.subject)
                                                                   r.subject = r.subject.escapeHTML();
                                                               if (r.reason)
                                                                   r.reason = r.reason.escapeHTML();
                                                               if (r.content)
                                                                   r.content = r.content.escapeHTML();
                                                               // console.log ("row id is %s and record values are %o", row.id, r);
                                                               var display = template.evaluate (r);
                                                               $(row.id).insert ( {after : display} );
                                                               var sign = $(row.id).down ("input[name='display']");
                                                               sign.value="-";
                                                               sign.src="/static/images/hide.png";
                                                               sign.title="Hide this message";
                                                           }
                                                       });
                                 }
                                 // console.log ("Done with pass");
                             });

        // console.groupEnd();
    },

    // This generates a request to forward, delete, passlist, or view
    // a message

    dispose: function (event) {
        // console.group ("dispose()");

        var action = event.memo.action;
        var rows = event.memo.rows;

        if (action && rows.size() > 0) {

            var param = new Hash();
            var shas = new Hash();

            rows.each (function (row) {
                           // Collect our parameters
                           var value = param.get (row.param.recipient);
                           if (value) {
                               value.push (row.param.sha);
                               param.set (row.param.recipient, value);
                           } else {
                               param.set (row.param.recipient, [row.param.sha]);
                           }

                           // Create a lookup table for removing records
                           var value = shas.get (row.param.sha);
                           shas.set (row.param.sha, [value, row.param.recipient]);
                       });

            // console.log ("Making request");
            new Ajax.Request ('sideline/' + action,
                              {method: 'post',
                               contentType: 'application/json',
                               postBody: Object.toJSON(param),
                               evalJSON: true,
                               sanitizeJSON: true,
                               onFailure : function () {
                                   alert ("Couldn't " + action + " entry");
                               },
                               onSuccess : function () {
                                   if ($('seen') && $('seen').checked) {
                                       rows.each (function (row) {
                                                      $(row.id).addClassName ('seen');
                                                      if ($(row.id + '_body'))
                                                          $(row.id + '_body').addClassName ('seen');
                                                  });
                                   } else {
                                       // Remove the rows from the table
                                       rows.each (function (row) {
                                                      $(row.id).remove();
                                                      if ($(row.id + '_body'))
                                                          $(row.id + '_body').remove();
                                                  });
                                     
                                       // Update the total
                                       $('sideline_total').update(new Number ($('sideline_total').innerHTML) - rows.size());
                                     
                                       // Remove the data from the system
                                       var sideline = $('sideline');
                                       var data = sideline.retrieve ('data');
                                       var newdata = data.select (function (i) {
                                                                      var recipients = shas.get (i.sha);
                                     
                                                                      // sha isn't present
                                                                      if (!recipients)
                                                                          return true;
                                     
                                                                      // Recipient isn't present
                                                                      if (!recipients.grep (i.recipient))
                                                                          return true;
                                     
                                                                      return false;
                                                                  });
                                     
                                       sideline.store ('data', newdata);
                                     
                                       // If there's more than one page and we're not on the last one, render again; otherwise, adjust.
                                       var page = sideline.retrieve ('page');
                                       var page_total = sideline.retrieve ('page_total');
                                       sideline.fire ('sideline:render');
                                   }
                               }});
        }

        // console.groupEnd();
    },

    // This extracts some content from the page to be used as a
    // template, attaches bits of behavior to several items, then
    // kicks off an async request to load the data.

    initialize: function (advanced, asbackup) {
        // console.group ("initialize()");

        // Speed access
        var sideline = $('sideline');

        // console.log ("Setting some internal parameters");
        sideline.store('advanced', advanced);
        sideline.store('asbackup', asbackup);
        sideline.store('page', 0);
        sideline.store('page_length', 300);
        sideline.store('sort_column', 'created');

        // console.log ("Instantiating our templates and clearing out the source");
        sideline.store('listing', new Template ($('sideline_body').innerHTML));
        $('sideline_body').update('');
        sideline.store('message', new Template ($('message_body').innerHTML));
        $('message_body').remove();

        // console.log ("Setting observers");

        // We want to be able to invoke the count adjustment stage
        sideline.observe ('sideline:adjust', this.adjust);

        // We want to be able to invoke the display stage
        sideline.observe ('sideline:display', this.display);

        // We want to be able to invoke the dispose stage
        sideline.observe ('sideline:delete', this.dispose);
        sideline.observe ('sideline:forward', this.dispose);

        // We want to be able to invoke the passlist quick-add stage
        sideline.observe ('sideline:passlist', this.passlist);

        // We want to be able to invoke the sort stage
        sideline.observe ('sideline:sort', this.sort);

        // We want to be able to invoke the render stage
        sideline.observe ('sideline:render', this.render);

        // We want to see incoming clicks
        $('sideline_body').observe ('click', this.single);

        // We want the count button to retrieve our counts
        $('count').observe ('click', this.count);

        // We want the view button to retrieve our data
        $('view').observe ('click', this.retrieve);

        // We want the checkboxes to toggle everything
        $$('input.toggle').invoke ('observe', 'click', this.toggle);

        // We want the iterators to operate en masse
        $$('input.iterate').invoke ('observe', 'click', this.multiple);

        // In case the user hits enter in the text box
        $$('form.pageForm').invoke ('observe', 'submit', function (event) {event.stop();});

        // Let the user page back and forth (we really want a scrollbar)
        $$('form.pageForm input[type="button"]').invoke ('observe', 'click', this.move);

        // Let the user choose a specific page (we really want a scrollbar)
        $$('form.pageForm input[type="text"]').invoke ('observe', 'change', this.page);

        // We want the header and footer to pay attention to clicks
        $$('thead', 'tfoot').invoke ('observe', 'click', this.sort);

        // console.log ("Retrieve the counts (total and today)");
        this.total();
        this.count();

        // console.groupEnd();
    },

    move: function (event) {
        // console.group ("move()");
        var sideline = $('sideline');

        var page = sideline.retrieve ('page');
        var page_total = sideline.retrieve ('page_total');

        var el = event.element ();
        var action = el.readAttribute ('name');

        var newpage;

        switch (action) {
        case 'first':
            newpage = 0;
            break;
        case 'previous':
            newpage = Math.max (page - 1, 0);
            break;
        case 'next':
            newpage = Math.min (page + 1, page_total);
            break;
        case 'last':
            newpage = page_total;
            break;
        }

        // console.log ("new page is " + newpage);

        if (newpage != page) {
            // console.log ("Re-rendering with new page()");
            sideline.store ('page', newpage);
            sideline.fire ('sideline:render');
        }
        // console.groupEnd();
    },

    multiple: function (event) {
        // console.group ("multiple()");
        event.stop();
        var el = event.element ();
        var action = el.readAttribute ('name');

        if (window.confirm('Are you sure you want to ' + action + ' all of the selected emails?') ) {

            // Get all the <tr>s for checked boxes.
            var rows = $$('tbody input[type="checkbox"]').findAll (function (el) {
                                                            return el.checked
                                                        }).collect (function (el) {
                                                                        var id = el.up ('tr').identify();
                                                                        var f = el.form;
                                                                        var values = Form.serializeElements (f.getInputs ('hidden'), true);
                                                                        return {
                                                                            id: id,
                                                                            param: values
                                                                        };
                                                                    });

            $('sideline').fire ("sideline:" + action, {action: action, rows: rows});

            $$('input.toggle').each (function (el) {
                                         el.checked = false;
                                     });
            if ($('seen') && $('seen').checked) {
                $$('tbody input[type="checkbox"]').each (function (el) {
                                                             el.checked = false;
                                                         });
            }
        }

        // console.groupEnd();
    },

    page: function (event) {
        // console.group ("page()");

        var sideline = $('sideline');

        var page = sideline.retrieve ('page');
        var page_total = sideline.retrieve ('page_total');

        var el = event.element ();

        var newpage = Math.min (Math.max (el.value - 1, 0), page_total);

        // Guard against trash input
        if (isNaN(newpage))
            newpage = page;

        // console.log ("new page is " + newpage);

        if (newpage != page) {
            // console.log ("Re-rendering with new page()");
            sideline.store ('page', newpage);
            sideline.fire ('sideline:render');
        } else if (newpage != el.value) {
            el.value = newpage + 1;
        }

        // console.groupEnd();
    },

    passlist: function (event) {
        // console.group ("passlist()");

        var sideline = $('sideline');

        event.memo.rows.each(function (row) {
                                 var id = row.id;
                                 var param = row.param;

                                 var promptText = sideline.retrieve ('advanced') ? "Edit the sender, or enter all or part of the email subject, then click OK." : "Edit the sender, if desired, then click OK.";
                                 var entry = window.prompt (promptText, param.sender);
                                 // console.log ("Entry is %s", entry);

                                 if (entry) {
                                     new Ajax.Request ("list/pass/",
                                     {method: 'post',
                                     onFailure: function () {
                                         alert ("Failed to retrieve list");
                                     },
                                     onSuccess: function () {
                                         $(id).down ("input[name='passlist']").remove();
                                     },
                                     parameters: {"add": entry}});
                                 }
                             });

        // console.groupEnd();
    },

    render: function () {
        // console.group ("render()");

        // console.log ("Adjusting header stuff");
        $('sideline').fire ('sideline:adjust');

        var sideline = $('sideline');

        var start = sideline.retrieve ('page') * sideline.retrieve ('page_length');

        // console.log ("First record is " + start);

        // console.time ("Grabbing a slice");
        var displayable = sideline.retrieve ('data').slice (start, start + sideline.retrieve ('page_length'));
        // console.timeEnd ("Grabbing a slice");

        // Iterate over our list to produce html of the table
        if (displayable.size() > 0) {
            var template = sideline.retrieve ('listing');
            // console.time ("Display");
            var cached = 0;
            var html = [];
            // console.time ("Constructing array of rows");
            displayable.inject (html, function (array, r) {
                                    if (!r.html) {
                                        if (r.sender)
                                            r.sender = r.sender.escapeHTML();
                                        if (r.subject)
                                            r.subject = r.subject.escapeHTML();
                                        if (r.recipient)
                                            r.recipient = r.recipient.escapeHTML();
                                        if (r.reason)
                                            r.reason = r.reason.escapeHTML();
                                        r.html = template.evaluate (r);
                                    } else {
                                        cached++;
                                    }
                                    array.push (r.html);
                                    return array;
                                });
            // console.timeEnd ("Constructing array of rows");
            // console.log ("Cached HTML used: " + cached);
            // Update the HTML for the table
            // console.time ("Creating string");
            var output = html.join ('');
            // console.timeEnd ("Creating string");
            // console.time ("Display");
            $('sideline_body').update (output);
            // console.timeEnd ("Display");
            sideline.show ();
        } else {
            sideline.hide ();
        }

        $$('div.formblock').invoke ('show');

        // console.groupEnd();
    },

    retrieve: function (e) {
        // console.group ("retrieve()");

        // Generate the params we're passing, and submit the request
        var param = $H($('filterForm').serialize ({hash: true}));
        new Ajax.Request ("sideline/list",
                          {method: 'post',
                           contentType: 'application/json',
                           postBody: Object.toJSON(param),
                           evalJSON: true,
                           sanitizeJSON: true,
                           onFailure: function () {
                               alert ("Failed to retrieve list");
                           },
                           onSuccess: function (response) {
                               // console.group ("retrieve() callback");
                               var sideline = $('sideline');
                               sideline.store ('data', $A(response.responseJSON));
                               var column = sideline.retrieve ('sort_column');
                               var direction = 'descending';
                               if ($$('th.' + column).first().hasClassName ('ascending')) direction = 'ascending';
                               $$('th.' + column).first().fire ('sideline:sort', {direction: direction});
                               // console.groupEnd();
                           },
                           parameters: $('filterForm').serialize(false)});
        // console.groupEnd();
    },

    single: function (event) {
        // console.group ("single()");
        var el = event.element ();
        var action = el.readAttribute ('name');
        if (action != 'selected') {
            event.stop();
            var f = el.form;
            if (f) {
                var id = el.up ('tr').identify();
                var values = Form.serializeElements (f.getInputs ('hidden'), true);
                // console.log ("Using id %s and values %o", id, values);
                $('sideline').fire ("sideline:" + action, {action: action, rows: new Array ({id: id, param: values})});
            } else {
                var checkbox = el.up ('tr').down ("input[name='selected']");
                checkbox.checked = !checkbox.checked;
            }
        }

        // console.groupEnd();
    },

    sort: function (event) {
        // console.group ("sort()");

        var el = event.element ();

        // Not a sort spec, nothing to do
        if (!el.hasClassName ('sort')) {
            // console.groupEnd();
            return;
        }

        // Get the element's sort column spec (by eliminating sort and/or direction
        var column = $A($w(el.className)).without ('sort', 'ascending', 'descending').first();

        // console.log ("column is " + column);

        var direction = 'descending';

        // Compute the appropriate direction
        if (el.hasClassName ('ascending')) {
            el.removeClassName ('ascending');
        } else if (el.hasClassName ('descending')) {
            el.removeClassName ('descending');
            direction = 'ascending';
        } else {
            // Remove the direction attribute from the old column
            $$('th.ascending', 'th.descending').each (function (i) {
                                                          i.removeClassName ('ascending');
                                                          i.removeClassName ('descending');
                                                      });
        }

        if (event.memo)
            direction = event.memo.direction;

        // console.log ("direction is " + direction);

        // Write the new class on both header and footer
        $$('th.' + column).invoke ('addClassName', direction);

        // console.log ("Choosing sort function");
        var f;

        if (direction == "ascending") {
            // console.log ("Sort is ascending");
            switch (column) {
            case 'domain':
                f = function (a, b) {
                    return a['domain'] < b['domain'] ? -1 : a['domain'] === b['domain'] ? a['sender'] < b['sender'] ? -1 : a['sender'] === b['sender'] ? 0 : 1 : 1;
                };
                break;
            case 'reason':
                f = function (a, b) {
                    return a['score'] - b['score'];
                };
                break;
            case 'size':
                f = function (a, b) {
                    return a['size'] - b['size'];
                };
                break;
            default:
                f = function (a, b) {
                    return a[column] < b[column] ? -1 : a[column] === b[column] ? 0 : 1;
                };
                break;
            }
        } else {
            // console.log ("Sort is descending");
            switch (column) {
            case 'domain':
                f = function (a, b) {
                    return b['domain'] < a['domain'] ? -1 : b['domain'] === a['domain'] ? b['sender'] < a['sender'] ? -1 : b['sender'] === a['sender'] ? 0 : 1 : 1;
                };
                break;
            case 'reason':
                f = function (a, b) {
                    return b['score'] - a['score'];
                };
                break;
            case 'size':
                f = function (a, b) {
                    return b['size'] - a['size'];
                };
                break;
            default:
                f = function (a, b) {
                    return b[column] < a[column] ? -1 : b[column] === a[column] ? 0 : 1;
                };
                break;
            }
        }

        var sideline = $('sideline');
        sideline.store('sort_column', column);

        // console.time ("Sorting data");
        sideline.retrieve ('data').sort (f);
        // console.timeEnd ("Sorting data");

        // Now that we've sorted, render
        sideline.fire ('sideline:render');

        // console.groupEnd();
    },

    toggle: function (event) {
        // console.group('toggle()');
        var el = event.element ();
        var c = el.checked;
        $$('table input[type="checkbox"]').each (function (i) {
                                                     i.checked = c;
                                                 });
        // console.groupEnd();
    },

    // This generates a request to retrieve sideline totals from the server.
    // We could concievably do this periodically.  Dunno if we want the extra load.

    total: function () {
        // console.group ("total()");

        // One or the other of these must be set
        var element = $('domain') || $('recipient');

        // Create a hash to hold the parameter
        var param = new Hash();
        param.set (element.id, $F(element));
        if ($('seen')) param.set ('seen', 't');

        // Generate the params we're passing, and submit the request
        new Ajax.Request ("sideline/count",
                          {method: 'post',
                           contentType: 'application/json',
                           postBody: Object.toJSON(param),
                           evalJSON: true,
                           sanitizeJSON: true,
                           onFailure: function () {
                               alert ("Failed to retrieve total");
                           },
                           onSuccess: function (response) {
                               // console.group ("total() callback");
                               $('sideline_total').update (response.responseJSON.count || 0);
                               // console.groupEnd();
                           }});
        // console.groupEnd();
    }
});

