


function SelectPanel(rdbAdmin, databaseManager, sqlPanel)
/* object to handle creation and renaming of views */
{
  this.panelId = "select-panel";
  var tableId = 'select-panel-result-table',
      pagerId = 'sel-table_pager',
      // tableIdentifier is the ascii/unicode table name, optionally with schema
      tableIdentifier = undefined,
      // metaTable contains the table metadata (column list, schema, name, etc)
      metaTable = undefined,
      // data will be the json blob received from the server
      data = undefined,
      // keyFieldIndexes will be a list of 2-tuples [[fld-idx,TableColumn],..]
      keyFieldIndexes = undefined,
      totalRowsCount = 0,
      limit = 30,
      page = 1,
      isView = undefined,
      that = this;

  // function to handle error result of query submit
  function errback(err, msg) {
    rdbAdmin.showErrorMessage('<pre>' + err.toString() + ': ' + msg + '</pre>');
  }

  this.init_handlers = function(app) {
    // bind handlers to create button, and to form submit buttons
    var $form = $('#' + this.panelId);
    // add click handlers
    $('#select-panel-new-item').click( function(ev) {
      app.setLocation('#/browser/insert/'+encodeURIComponent(tableIdentifier));
      ev.stopPropagation();
    });
    $('#sel-table-select-btn').click( function(ev) { // [update] button
      that.select();
      ev.stopPropagation();
    });
    $('#sel-panel-delete-selected').click( function(ev) {
      deleteSelected();
      ev.stopPropagation();
    });
    $('span.sel-table-tr-edit',$form).live('click', function (ev) {
      var keyVals = createPrimKeyValueList(this);
      var kV = JSON.stringify(keyVals);
      app.setLocation('#/browser/editrec/'+encodeURIComponent(tableIdentifier)+'/'+encodeURIComponent(kV));
      ev.stopPropagation();
    });
    $('.colhdr',$form).live('click', function (ev) {
      sortByColumn(this);
      ev.stopPropagation();
    });
    // select/deselect all
    $('#sel-table-selitem',$form).live('click', function(ev) {
      if ($(this).attr('checked')) {
        $('input.sel-table-selitem',$form).attr('checked', 'checked');
      }
      else {
        $('input.sel-table-selitem',$form).attr('checked', '');
      }
      ev.stopPropagation();
    });
    // pagination link handlers
    $('span[id^="pager-"]',$form).live('click', function(ev) {
      var pg = $(this).attr('id').substr(6, 15);
      page = parseInt(pg, 10);
      buildTable();
      ev.stopPropagation();
    });
    $('#select-panel-select-div .sel-panel-functions, #select-panel-select-div .sel-table-fields',$form)
        .live('change', function (ev) {
      add_row(this);
      ev.stopPropagation();
    });
    $('#select-panel-fwhere-div .sel-table-fwhere, #select-panel-fwhere-div input',$form)
        .live('change', function (ev) {
      add_row(this);
      ev.stopPropagation();
    });
    $('#select-panel-fsort-div .sel-table-fsort, #select-panel-fsort-div :checkbox',$form)
        .live('change', function (ev) {
      add_row(this);
      ev.stopPropagation();
    });
    $('#select-panel-limit-div input',$form).live('change', function (ev) {
      add_row(this);
      ev.stopPropagation();
    });
    // toggle control fieldset boxes hidden/nothidden
    $('fieldset legend',$form).click(function (ev) {
      $(this).closest('fieldset').children('*:not(legend)').toggle();
      ev.stopPropagation();
    });

    // toggle control fieldset boxes hidden/nothidden
    $(':input',$form).change(function (ev) {
      updateSQL();
    });
  };

  function updateSQL() {
    var queryRes = buildQuery(false); // not count
    var $panel = $('#' + that.panelId);
    $('#sel-panel_jush-sql',$panel).html(queryRes.query);
  }

  function createPrimKeyValueList(domel) {
    var rI = $(domel).parents('tr:first').get(0).rowIndex,
        row = data.rows[parseInt(rI,10)-1],
        keyVals = [];
    for (var k in keyFieldIndexes) {
      keyVals.push(keyFieldIndexes[k][1].columnName);
      keyVals.push(row[keyFieldIndexes[k][0]]);
    }
    return keyVals;
  }

  function reveal() {
    var $panel = $('#' + that.panelId);
    rdbAdmin.onStopQueryExecution();
    //$panel.slideDown('fast','linear');
    $panel.show();
  }
  function errReveal(err,msg) {
    errback(err,msg);
    reveal();
  }

  this.show = function(tableName, isAView) {
    var $panel = $('#' + this.panelId);
    $('.tablehide',$panel).show();
    $panel.hide();
    rdbAdmin.onStartQueryExecution();
    rdbAdmin.setHeading("Select: " + tableName);
    if (tableIdentifier !== tableName) {
      tableIdentifier = tableName;
      metaTable = new TableMeta(tableIdentifier);
      page = 1;      
    }
    data = undefined;
    isView = isAView;

    // get fields type and other meta-info
    function callback(dtls) {
      metaTable = dtls; //.fields;
      getPKConstraint();
    }
    databaseManager.getTableDetails(metaTable.schemaName, metaTable.tableName, callback, errReveal);
  };

  var okTypes = [ 'int','bigint','smallint','integer','serial','bigserial',
                  'real','float','double','decimal','double','decimal',
                  'number','numeric','text','character','char','varchar',
                  'character varying','timestamp','timestamp with time zone',
                  'timestamp without time zone','date','time',
                  'time with time zone','time without time zone','bytea' ];
  function getPKConstraint() {
    // get constraints data, especially primary key info
    function callbackCon(constraints) {
      keyFieldIndexes = [];
      for (var k=0; k<constraints.length; k+=1) {
			  var constraint = constraints[k],
            typ = constraint[1],
            columns = constraint[7];
        if (typ === 'p') {
          for (var c in columns) {
              var colIdx = parseInt(columns[c],10)-1;
              var column = metaTable.fields[colIdx];
              if ($.inArray(column.dataType, okTypes) >-1 ) {
                keyFieldIndexes.push([colIdx,column]);
              } 
              else {
                keyFieldIndexes = [];
                break;
              }
          }
          break;
        }
      }
      that.select();
    }
    databaseManager.getConstraints(metaTable.schemaName, metaTable.tableName, callbackCon, errReveal);
  }

  this.select = function() {
    rdbAdmin.resetMessages();
    return buildTable();
  };

  function clearSelectControls() {
    // init some interface controls
    var $form = $('#' + that.panelId);
    $('div#select-panel-select-div span:gt(0)',$form).remove();
    $('.sel-panel-functions', $form).find('option:first').attr("selected","selected");
    $('.sel-table-fields', $form).empty().append('<option></option>');

    // where line
    $('div#select-panel-fwhere-div span:gt(0)',$form).remove();
    $('.sel-table-fwhere', $form).empty().append('<option></option>');
    $('.sel-statement', $form).find('option:first').attr("selected","selected");
    $('div#select-panel-fwhere-div input',$form).val('');

    // sort line
    $('div#select-panel-fsort-div span:gt(0)',$form).remove();
    $('.sel-table-fsort', $form).empty().append('<option></option>');
    $('div#select-panel-fsort-div :checkbox', $form).attr('checked', false);

    // limit line
    $('#sel-panel-limit').val('30');
  }

  function fillSelectControls(insertAllowed, deleteAllowed) {
    var i,
        $form = $('#'+that.panelId);
    if ( data && data.header ) {
      if ($('.sel-table-fields',$form).children().length <= 1) {
        for (i in data.header) {
          $('.sel-table-fields',$form).append('<option>' + data.header[i][1] + '</option>');
        }
      }
      if ($('.sel-table-fsort',$form).children().length <= 1) {
        for (i in data.header) {
          $('.sel-table-fsort',$form).append('<option>' + data.header[i][1] + '</option>');
        }
      }
      if ($('.sel-table-fwhere').children().length <= 1) {
        for (i in data.header) {
          $('.sel-table-fwhere',$form).append('<option>' + data.header[i][1] + '</option>');
        }
      }
      if ( deleteAllowed ) {
        $('#sel-panel-del-btn').show();
      } else {
        $('#sel-panel-del-btn').hide();
      }
      if ( insertAllowed ) {
        $('#select-panel-new-item').show();
      } else {
        $('#select-panel-new-item').hide();
      }
    }
    // hide select and search control boxes unless in use
    if ( $('.sel-panel-functions:first').val() === '' ) {
      $('div#select-panel-select-div').hide();
    }
    if ( $('.sel-table-fwhere:first').val() === '' ) {
      $('div#select-panel-fwhere-div').hide();
    }
  }

  function add_row(field) {
    // check if this row is the last - if isn't - return false
    var $span = $(field).closest('span');
    if ($span.nextAll('span:first').length !== 0) {
      return false;
    }
    var $newspan = $span.clone();
    // clean selects in cloned row
    var selects = $newspan.find('select');
    for (var i = 0; i < selects.length; i += 1) {
      selects[i].name = selects[i].name.replace(/[a-z]\[[0-9]+/, '$&1');
      selects[i].selectedIndex = 0;
    }
    var inputs = $newspan.find('input');
    if (inputs.length) {
      inputs[0].name = inputs[0].name.replace(/[a-z]\[[0-9]+/, '$&1');
      inputs[0].value = '';
    }
    $span.parent().append('<br/>').append($newspan);
    return true;
  }

  function sortByColumn(colm) {
    var col = $(colm).html();
    // set up sorting inputs
    if ($('div#sel-panel-fsort-div select[value="' + col + '"]').length === 0) {
      // add row with this col name
      add_row($('div#sel-panel-fsort-div:last').find('select: first').get(0));
      // set col name
      $('div#sel-panel-fsort-div:last').find('select:first').val(col);
    }
    var select = $('div#sel-panel-fsort-div select[value="' + col + '"]:first');
    var checkbox = $(select.parent().find('input:checkbox').get(0));
    if (checkbox.attr('checked')) {
      checkbox.attr('checked', false);
    }
    else {
      checkbox.attr('checked', true);
    }
    that.select();
  }

  function buildTable() {
    var $table = $('#' + tableId),
        $panel = $('#'+that.panelId);
    if (data === undefined) {
      clearSelectControls();
    }
    // build query
    var queryObj = buildQuery(false);
    $('#sel-panel_jush-sql').html('<code>' + queryObj.query + '</code>');
    // add click handler to edit query
    $('#sel-panel-sql-edit').click(function(ev) {
      ev.stopPropagation();
      sqlPanel.showQueryLater(queryObj);
      rdbAdmin.loadNewPage('#/sqlcommand');
    });
    var insertAllowed = !isView && queryObj.isFullRecord,
        listIsEditable = insertAllowed && keyFieldIndexes.length > 0,
        deleteAllowed;
    // querying
    function successcb(json) {
      rdbAdmin.showWorkingMessage(json.row_count[1]);
      data = json.records;
      // empty the table
      $table.empty();
      if (data && data.header) {
        // add select option
        deleteAllowed = listIsEditable && data.rows && data.rows.length;
        fillSelectControls(insertAllowed,deleteAllowed);
        // show notes for no-edit cases
        $('.no-primary-key-note',$panel)[(listIsEditable || isView) ? 'hide' : 'show']();
        $('.only-view-note',$panel)[isView ? 'show' : 'hide']();
        // create header of html table
        if ( listIsEditable || data.header.length===0 ) {
          $('<tr id="sel-table_th"><td>' +
              '<input type="checkbox" id="sel-table-selitem" />all' +
              '</td></tr>').appendTo($table);
        }
        else {
          $('<tr id="sel-table_th"></tr>').appendTo($table);
        }
        for (var i in data.header) {
          // add html table header
          $('#sel-table_th').append('<th><span class="btn colhdr">' +
              data.header[i][1] + '</span></th>');
        }
        // render data
        if ( data && data.rows && data.rows.length ) {
          for ( i in data.rows ) {
            var row = data.rows[i];
            if ( listIsEditable ) {
              $('<tr id="sel-table_tr_' + i + '"><td>' +
                '<input type="checkbox" class="sel-table-selitem" />' +
                '<span class="btn sel-table-tr-edit">edit</span>' + '</td></tr>').appendTo($table);
            }
            else {
              $('<tr id="sel-table_tr_' + i + '"></tr>').appendTo($table);
              //'"><td> </td></tr>').appendTo(table);
            }
            var tabRow, cell;
            for (var j in row) {
              tabRow = $('#sel-table_tr_' + i).get(0);
              cell = document.createElement("td");
              cell.appendChild(document.createTextNode(row[j]));
              tabRow.appendChild(cell);
            }
          }
        }
      }
      reveal();
    }
    // send query to server
    databaseManager.sqlEngine.query({'q' : queryObj.query,
                                     'args' : queryObj.args,
                                     'callback' : successcb,
                                     'errback' : errReveal });
    // build pager
    buildPager();
  }

  function deleteSelected() {
    var query = [], q = [], arglist = [], dTypes = [], primKeyList, col,
        fldVal, fldName, fldIdx, fldType, colDetails;
    // find selected rows
    $('input.sel-table-selitem:checked').each( function() {
      // get selected items from data.rows
      primKeyList = createPrimKeyValueList(this);
      q = [];
      colDetails = keyFieldIndexes.slice(0);
      while (colDetails.length) {
        col = colDetails.shift();
        fldIdx = col[0];
        fldType = col[1].dataType;
        fldName = primKeyList.shift();
        fldVal = primKeyList.shift();
        if (fldVal === null) {
          q.push(quoteIdentifier(fldName) + " IS NULL");
        }
        else {
          q.push(quoteIdentifier(fldName) + ' = %s ');
          arglist.push(fldVal);
          dTypes.push(fldType);
        }
      }
      query.push("DELETE FROM " + metaTable.qualTableName() +
                 " WHERE " + q.join(' AND ') + ";");
    });
    query = query.join("\n");
    function successcb() {
      buildTable(); // refresh page
    }
    // send query to server
    databaseManager.sqlEngine.query({ 'q' : query,
                                      'args' : arglist,
                                      'argtypes' : dTypes,
                                      'callback' : successcb,
                                      'errback' : errback       });
  }

  function buildPager() {
    // fetch total rows count
    var qResult = buildQuery(true); // records count
    // build is called for both success and failure
    function build() {
      var btn, i;
      $('#' + pagerId).empty();
      $('#sel-table_total-rows_count').empty().append(parseInt(totalRowsCount, 10));
      // if our item count or per-page total is zero there is no need to continue
      if (totalRowsCount === 0) {
        totalRowsCount = 1;
      }
      // calculate page count
      var page_count = Math.ceil(parseFloat(parseInt(totalRowsCount, 10) / parseInt(limit, 10)));
      // current page - page
      // Calculate the start and end numbers. These determine
      // which number to start and end the digit links with
      var num_links = 2; // Number of "digit" links to show before/after the currently viewed page
      var start = ((page - num_links) > 0) ? page - (num_links - 1) : 1;
      var end = ((parseInt(page, 10) + parseInt(num_links, 10)) < parseInt(page_count, 10)) ?
          parseInt(page, 10) + parseInt(num_links, 10) : page_count;
      // Render the "First" link
      if (page > (num_links + 1)) {
        btn = '<span class="txtbtn" id="pager-~p~" >&lsaquo; First</span>';
        btn = btn.replace('~p~', 1);
        $('#' + pagerId).append(btn);
      }
      // Render the "previous" link
      if (page !== 1) {
        i = page - limit;
        if (i === 0) {
          i = '';
        }
        btn = '<span class="txtbtn" id="pager-~p~">&lt;</span>';
        btn = btn.replace('~p~', parseInt(page, 10) - 1);
        $('#' + pagerId).append(btn);
      }
      // Write the digit links
      for (var loop = start - 1; loop <= end; loop += 1) {
        i = (loop * limit) - limit;
        if (i >= 0) {
          if (page === loop) {
            btn = '<span id="pager-~p~">~p~</span>';
            btn = btn.replace(/~p~/g, loop);
            $('#' + pagerId).append(btn); // Current page
          } else {
            //var n = (i === 0) ? '' : i;
            btn = '<span class="txtbtn" id="pager-~p~">~p~</span>';
            btn = btn.replace(/~p~/g, loop);
            $('#' + pagerId).append(btn);
          }
        }
      }
      // Render the "next" link
      if (page < page_count) {
        btn = '<span class="txtbtn" id="pager-~p~">&gt;</span>';
        btn = btn.replace('~p~', parseInt(page, 10) + 1);
        $('#' + pagerId).append(btn);
      }
      // Render the "Last" link
      if ((parseInt(page, 10) + parseInt(num_links, 10)) < parseInt(page_count, 10)) {
        btn = '<span class="txtbtn" id="pager-~p~">Last &rsaquo;</span>';
        btn = btn.replace('~p~', parseInt(page_count, 10));
        $('#' + pagerId).append(btn);
      }
    }

    function successcb(json) {
      totalRowsCount = json.records.rows[0];
      build();
    }

    function errorcb() {
      totalRowsCount = 0; // data.rows.length;
      build();
    }
    // send query to server
    databaseManager.sqlEngine.query({'q' : qResult.query,
                                     'args' : qResult.args,
                                     'callback' : successcb,
                                     'errback' : errorcb });
  }

  function buildQuery(isCount) {
    var query = '',
        limitClause = '',
        limitArgs = [];

    limit = $('#sel-panel-limit').val();
    // if we have limit
    if (limit !== '') {
      var offsetLim = [(page - 1) * limit, limit];
      limitClause = ' OFFSET ' + offsetLim[0] + ' LIMIT ' + offsetLim[1];
    }
    else {
      limitClause = '';
    }

    // build what-to-select statement
    var what = [], i, whatArgs = [], isFullRecord = true,
        field, func, $selSpan,
        $selSpans = $('div#select-panel-select-div span');
    for (i = 0; i < $selSpans.length; i += 1) {
      $selSpan = $($selSpans[i]);
      func = $selSpan.find('select.sel-panel-functions:first').val();
      field = $selSpan.find('select.sel-table-fields:first').val();
      if (field !== '') {
        isFullRecord = false;
      }
      if ((func !== '') && (field !== '')) {
        if (func === 'unix_timestamp') {
          what.push('extract(epoch FROM ' + field + ')');
        }
        else {
          what.push(func + '(' + field + ')');
        }
      }
      else if (field !== '') {
        what.push(field);
      }
    }
    if (what.length === 0) {
      what = '*';
    }
    else {
      what = what.join(', ');
    }

    // 'searching' - where statement
    var where = [], whereArgs = [], val, statement;
    $selSpans = $('div#select-panel-fwhere-div span');
    for (i = 0; i < $selSpans.length; i += 1) {
      $selSpan = $($selSpans[i]);
      field = $selSpan.find('select.sel-table-fwhere:first').val();
      statement = $selSpan.find('select.sel-statement').val();
      val = $selSpan.find('input[name="where"]').val();

      if (field !== '') {
        // should we quote a value?
        var ftype = getFieldTypeByName(field);
        switch (ftype) {
          case 'int':
          case 'bigint':
          case 'integer':
          case 'number':
          case 'serial':
          case 'bigserial':
          case 'float':
          case 'double':
          case 'decimal':
          case 'numeric':
          case 'smallint':
          case 'real':
          case 'boolean':
            where.push(quoteIdentifier(field) + ' ' + statement + ' %s');
            val = val.replace(/%/g, '%%');
            whereArgs.push(val);
            break;
          default:
            where.push(quoteIdentifier(field) + ' ' + statement + ' %s');
            val = val.replace(/%/g, '%%');
            whereArgs.push(val);
            break;
        }
      }
    }
    if (where.length === 0) {
      where = '';
    }
    else {
      where = " WHERE " + where.join(' AND ') + " ";
    }

    // ordering and sorting
    var orderBy = [],
        sels = $('div#select-panel-fsort-div');
    for (i = 0; i < sels.length; i += 1) {
      field = $($(sels[i]).find('select')[0]).val();
      val = $($(sels[i]).find('input')).attr('checked') ? 'DESC' : 'ASC';
      if (field !== '') {
        orderBy.push(quoteIdentifier(field) + ' ' + val);
      }
    }
    if (orderBy.length === 0) {
      orderBy = '';
    }
    else {
      orderBy = " ORDER BY " + orderBy.join(', ') + " ";
    }
    var args;
    if (isCount) {
      query = "SELECT COUNT(*) FROM " + metaTable.quotedTableName() + where;
      args = whereArgs;
    } else {
      query = "SELECT " + what + " FROM " + metaTable.quotedTableName() +
          where + orderBy + limitClause;
      args = whatArgs.concat(whereArgs, limitArgs);
    }
    return { 'status' : 'ok',
             'query' : query,
             'args' : args,
             'isFullRecord' : isFullRecord };
  }

  function getFieldTypeByName(name) {
    var fieldDetails = metaTable.fields;
    for (var i in fieldDetails) {
      if (fieldDetails[i].columnName === name) {
        return fieldDetails[i].dataType;
      }
    }
    return false;
  }
  function truncate() {
    // **** reform truncate
    if (!confirm('Are you sure?')) {
      return;
    }
    function callback(json) {
      rdbAdmin.showWorkingMessage(json.status[1]);
      that.show(tableIdentifier);
    }
    databaseManager.truncate(tableIdentifier, callback, errback);
  }

}
