/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

// DataBrowserQuery.js

var responseResult = null;

// This function activates Execute button
function enableExecuteBtn(flagEnable) {
  if (flagEnable) {
    $('#btnExecuteQuery').prop("disabled", false);
    if ($('#btnExecuteQuery').hasClass("grey")) {
      $('#btnExecuteQuery').addClass("blue");
      $('#btnExecuteQuery').removeClass("grey");
    }
  } else {
    $('#btnExecuteQuery').prop("disabled", true);
    if ($('#btnExecuteQuery').hasClass("blue")) {
      $('#btnExecuteQuery').addClass("grey");
      $('#btnExecuteQuery').removeClass("blue");
    }
  }
}

// This function is called when query editor content changes
function onQueryTextChange() {
  var queryString = $('#dataBrowserQueryText').val();
  if (!isEmpty(queryString)) {
    enableExecuteBtn(true);
  } else {
    enableExecuteBtn(false);
  }
}

// This function clears query editor 
function clearDBQuery(){
  $('#dataBrowserQueryText').val('');
  onQueryTextChange();
}

// This function executes query written in query editor and displays results
function executeDBQuery(){
  // Disable Execute button
  enableExecuteBtn(false);

  var queryText = $('#dataBrowserQueryText').val();
  var selectedMemberNames = "";
  
  if(isEmpty(queryText)){
    alert("Please enter query to execute..");
    return;
  }
  
  // Determine selected members query to be execute on 
  if($("#membersList").html() !== ""){
    var selectedMembers = $( "input[type=checkbox][name=Member]:checked" );
    for(var i=0; i< selectedMembers.length; i++){
      if(selectedMemberNames === ""){
        selectedMemberNames = selectedMembers[i].value;
      }else{
        selectedMemberNames += ","+selectedMembers[i].value;
      }
    }
  }
  
  // TODO 
  // As of now, member names are creating problem due to ':' character in 
  // member names. hence query based upon member names are not supported.
  // following line of code should be removed when solution to this problem is 
  // in place.
  //selectedMemberNames = "";
  
  requestData = {
        query:queryText,
        members:selectedMemberNames,
        limit:0
      };

  // Display Loading/Busy symbol
  $("#loadingSymbol").show();

  // Delete previous query result, if exists.
  if(!isEmpty($('#memberAccordion').html())){
    deleteExistingResultGrids();
    $('#memberAccordion').html('');
  }

  $.getJSON("dataBrowserQuery", requestData, function(data) {
    
    if(data != undefined && data != null){
      if(data.error != undefined && data.error != null){
        
        // Delete previous query result, if exists.
        /*if(!isEmpty($('#memberAccordion').html())){
          deleteExistingResultGrids();
          $('#memberAccordion').html('');
        }*/
        
        var errorText = data.error;
        errorText = encodeMemberName(errorText);
        
        $('#memberAccordion').append("<span>"+errorText+"</span>");

      }else if(data.message != undefined && data.message != null){

        // Delete previous query result, if exists.
        /*if(!isEmpty($('#memberAccordion').html())){
          deleteExistingResultGrids();
          $('#memberAccordion').html('');
        }*/

        var messageText = data.message;
        messageText = encodeMemberName(messageText);

        $('#memberAccordion').append("<span>"+messageText+"</span>");

      }else{
        //console.log("data.result.length"+data.result.length);
        // Convert received data into expected format
        responseResult = convertRawResponseToExpectedFormat(data);
        
        // Delete previous query result, if exists.
        /*if(!isEmpty($('#memberAccordion').html())){
          deleteExistingResultGrids();
          $('#memberAccordion').html('');
        }*/
        
        // Create html for newly executed query
        createHtmlForQueryResults();
        
        // Apply accordion Behavior 
        accordion();
        accordionNested();
        /*custom scroll bar*/
        $('.ScrollPaneBlock').jScrollPane();
        
        // Populate result grids
        if(!isEmpty($('#memberAccordion').html())){
          populateResultGrids();
        }
        //$('.queryHistoryScroll-pane').jScrollPane();/*Custome scroll*/
        
      }
      // Update queries list in history panel
      updateQueryHistory("view","");
    }
    
    // Hide Loading/Busy symbol
    $("#loadingSymbol").hide();

    // enable Execute button
    enableExecuteBtn(true);

  }).error(resErrHandler);
  

}

// This function displays error if occurred 
function resErrHandler(data){
  // Check for unauthorized access
  if (data.status == 401) {
    // redirect user on Login Page
    window.location.href = "login.html?error=UNAUTH_ACCESS";
  }else{
    console.log(data);
  }
}

// This function creates complete result panel html
function createHtmlForQueryResults(){
  var memberResults = responseResult.result;

  if(memberResults.length > 0){

    if(memberResults[0].member != undefined || memberResults[0].member != null){
      //console.log("member wise results found..");      
      for(var i=0; i<memberResults.length; i++){
        //console.log(memberResults[i].member);
        $('#memberAccordion').append(createHtmlForMember(memberResults[i]));
      }
    }else{
      //console.log("cluster level results found..");
      var accordionContentHtml = "";
      accordionContentHtml = createClusterAccordionContentHtml(memberResults);
      var resultHtml = "<div class=\"accordion-content2\">"+ accordionContentHtml +"</div>";
      $('#memberAccordion').append(resultHtml);
    }
  }else{
    $('#memberAccordion').append("<span> No Results Found...</span>");
  }
}

// This function create/generate single member's html
function createHtmlForMember(memberData){
  
  var accordionContentHtml = "";
  accordionContentHtml = createMemberAccordionContentHtml(memberData);
  // Convert angular brackets ('<', '>') using HTML character codes 
  var memberTitle = memberData.member[1].replace(/\<+/g, '&#60').replace(/\>+/g, '&#62');
  var memberHtml = 
            "<div class=\"listing\">" +
              "<div class=\"heading\">" +
                "<span class=\"title\">"+ memberTitle + "</span>" +
              "</div>" +
              "<div class=\"accordion-content\">"+ accordionContentHtml +
              "</div>" +
            "</div>";
  
  return memberHtml;
}

// This function creates/generates member's accordion content html
function createMemberAccordionContentHtml(memberData){
  
  var memName = encodeMemberName(memberData.member[1]);
  var requiredHeight = determineAccordionContentHeight(memberData.result);
  var memberAccordionContentHtml = "";
  memberAccordionContentHtml += 
                "<div class=\"ScrollPaneBlock\" style=\"height: "+requiredHeight+"px;\">" +
                  "<div id=\"memberDetails_"+memName+"\" class=\"accordionNested\">";
  
  var memberResults = memberData.result;
  for(var i=0; i<memberResults.length; i++){
    var htmlTableId = generateTableIdForObjectsTable(memberData.member[1], memberResults[i].objectType);
    var ScrollPaneBlockHeight = 40;
    var resultsLength = memberResults[i].objectResults.length;
    if(resultsLength > 0 && resultsLength < 6){
      ScrollPaneBlockHeight += resultsLength * 30;
    }else{
      ScrollPaneBlockHeight = 200;
    }
    memberAccordionContentHtml += 
                    "<div class=\"n-listing\">" +
                      "<div class=\"n-heading\">" +
                        "<span class=\"n-title\">"+memberResults[i].objectType+"</span>" +
                      "</div>" +
                      "<div class=\"n-accordion-content\">" +
                        "<div class=\"widthfull-100per\">" +
                            "<table id=\""+ htmlTableId +"\"></table>" +
                        "</div>" +
                      "</div>" +
                    "</div>";
  }
  
  memberAccordionContentHtml += 
                  "</div>" +
                "</div>";
  
  return memberAccordionContentHtml ;
  
}

//This function creates/generates cluster's accordion content html
function createClusterAccordionContentHtml(clusterResults){
  var requiredHeight = determineAccordionContentHeight(clusterResults);
  var memberAccordionContentHtml = "";
  memberAccordionContentHtml += 
             "<div class=\"ScrollPaneBlock\" style=\"height: "+requiredHeight+"px;\">" +
               "<div id=\"clusterDetails\" class=\"accordionNested\">";

  for(var i=0; i<clusterResults.length; i++){
    var htmlTableId = generateTableIdForObjectsTable("", clusterResults[i].objectType);
    var ScrollPaneBlockHeight = 40;
    var resultsLength = clusterResults[i].objectResults.length;
    if(resultsLength > 0 && resultsLength < 6){
      ScrollPaneBlockHeight += resultsLength * 30;
    }else{
      ScrollPaneBlockHeight = 200;
    }
    memberAccordionContentHtml += 
                 "<div class=\"n-listing\">" +
                   "<div class=\"n-heading\">" +
                     "<span class=\"n-title\">"+clusterResults[i].objectType+"</span>" +
                   "</div>" +
                   "<div class=\"n-accordion-content\">" +
                     "<div class=\"widthfull-100per\">" +
                         "<table id=\""+ htmlTableId +"\"></table>" +
                     "</div>" +
                   "</div>" +
                 "</div>";
  }

  memberAccordionContentHtml += 
               "</div>" +
             "</div>";

  return memberAccordionContentHtml ;

}

// Function to find/determine the required height of accordion container
function determineAccordionContentHeight(result){
  var numObjects = result.length;
  // determine height of single grid/table
  var height = 0;
  for(var i=0; i<numObjects; i++){
    var numObjectEntries = result[i].objectResults.length;
    if(numObjectEntries > 0 
        && numObjectEntries < 6){
      if(height < 210){
        height = 60 + numObjectEntries * 30;
      }
    }else{
      height = 210;
    }
  }

  // Add additional height required for accordin headers
  height += numObjects * 30;
  return height;

}

// Function to generates id of html table in which results grid is contained
function generateTableIdForObjectsTable(memberName, objectName){
  // Remove all dots (.) in the string and also replace "[]" by "BxBr"
  var objName = objectName.replace(/\.+/g, '').replace("[]", 'BxBr');
  var memName = encodeMemberName(memberName);
  // form html table id
  var tableId = "table-"+memName+"-"+objName;
  
  return tableId;
}

// Function to generates encoded member name
function encodeMemberName(memberName){
  var memName = memberName.replace(/\.+/g, '');
  memName = memName.replace(/\(+/g, '-');
  memName = memName.replace(/\)+/g, '-');
  memName = memName.replace(/\<+/g, '-');
  memName = memName.replace(/\>+/g, '-');
  memName = memName.replace(/\:+/g, '-');
  
  return memName;
}

// This function populates query results into jqgrid tables 
function populateResultGrids(){
  
  var memberResults = responseResult.result;

  if(memberResults.length == 0){
    return;
  }

  if(memberResults[0].member != undefined || memberResults[0].member != null){
    for(var i=0; i<memberResults.length; i++){
      
      var memberResult = memberResults[i].result;
      var memberName = memberResults[i].member[1];
      for(var j=0; j<memberResult.length; j++){
        var htmlTableId = generateTableIdForObjectsTable(memberName, memberResult[j].objectType);
        
        if($('#'+htmlTableId)){
          createResultGrid(memberName, memberResult[j]);
        }else{
          console.log("Not Found : "+htmlTableId);
        }
      }
    }
  }else{
      
      var memberResult = memberResults;
      var memberName = "";
      for(var j=0; j<memberResult.length; j++){
        var htmlTableId = generateTableIdForObjectsTable(memberName, memberResult[j].objectType);
        
        if($('#'+htmlTableId)){
          createResultGrid(memberName, memberResult[j]);
        }else{
          console.log("Not Found : "+htmlTableId);
        }
      }
  }
}

// Function to delete previous queries results from UI
function deleteExistingResultGrids(){
  if(responseResult == undefined || responseResult == null){
    return;
  }
  
  var memberResults = responseResult.result;

  if(memberResults.length == 0){
    return;
  }

  if(memberResults[0].member != undefined || memberResults[0].member != null){
    for(var i=0; i<memberResults.length; i++){
      
      var memberResult = memberResults[i].result;
      var memberName = memberResults[i].member[1];
      for(var j=0; j<memberResult.length; j++){
        var htmlTableId = generateTableIdForObjectsTable(memberName, memberResult[j].objectType);
        
        if($('#'+htmlTableId)){
          $('#'+htmlTableId).jqGrid('GridDestroy');
        }else{
          console.log("Not Found : "+htmlTableId);
        }
      }
    }
  }else{
    var memberResult = memberResults;
    var memberName = "";
    for(var j=0; j<memberResult.length; j++){
      var htmlTableId = generateTableIdForObjectsTable(memberName, memberResult[j].objectType);
      
      if($('#'+htmlTableId)){
        $('#'+htmlTableId).jqGrid('GridDestroy');
      }else{
        console.log("Not Found : "+htmlTableId);
      }
    }
  }
}

// Function to create single results grid
function createResultGrid(member, memberResultObject){
  var objectType = memberResultObject.objectType;
  // make a copy of memberResultObject.objectResults
  var objectResults = jQuery.extend(true, [], memberResultObject.objectResults);
  var htmlTableId = generateTableIdForObjectsTable(member, objectType);
  
  // TODO Following code section should be removed once to string representation 
  // is finalized in same scenario 
  // If results set is java primitive collection of HashMap then following 
  // custom representation is needed
  /*var customObjectResults = new Array();
  if(objectType.indexOf("java.util.HashMap") != -1){
    // generate customized objectResults
    for(var cnt=0; cnt<objectResults.length; cnt++){
      var uid = objectResults[cnt].uid;
      delete objectResults[cnt].uid;
      var newEntry = {"uid":uid, "result":["java.util.HashMap",objectResults[cnt]]};
      customObjectResults.push(newEntry);
    }
    // replace existing objectResults by new customObjectResults
    objectResults = customObjectResults;
  }*/
  
  // Determine table columns
  var columnName = [];
  var columnModel = [];
  for(var cnt=0; cnt<objectResults.length; cnt++){
    for(key in objectResults[cnt]){
      if(-1 == columnName.indexOf(key)){
        // add column and column model only if not present
        columnName.push(key);
        columnModel.push({name:key, index:key, width:150});
      }
    }
  }
  
  // set custom column widths if less columns
  if(columnModel.length == 2){
    for(var c=0; c<columnModel.length; c++){
      columnModel[c].width = 660;
    }
  }else if(columnModel.length == 3){
    for(var c=0; c<columnModel.length; c++){
      columnModel[c].width = 330;
    }
  }else if(columnModel.length == 4){
    for(var c=0; c<columnModel.length; c++){
      columnModel[c].width = 220;
    }
  }
  
  // Delete jqgrid if already exists
  $("#"+htmlTableId).jqGrid('GridDestroy');
  
  // Determine Grid's Height
  var gridheight = 30;
  if(objectResults.length > 0 && objectResults.length < 6){
    gridheight += objectResults.length * 26;
  }else{
    gridheight = 160;
  }
  
  // Create jqgrid 
  jQuery("#"+htmlTableId).jqGrid({
                  datatype: "local",
                  width : 668,
                  height: gridheight,
                  shrinkToFit : false,
                  colNames:columnName,
                  colModel:columnModel,
                  ondblClickRow:displayPopUpObjectExplorer,
                  gridComplete : function() {
                    $(".jqgrow").css({
                      cursor : 'default'
                    });

                    var gridList = $('#gview_'+htmlTableId);
                    var gridListChild = gridList.children('.ui-jqgrid-bdiv');

                    gridListChild.unbind('jsp-scroll-x');
                    gridListChild.bind('jsp-scroll-x', function(event,
                        scrollPositionX, isAtLeft, isAtRight) {
                      var mRList = $('#gview_'+htmlTableId);
                      var mRLC = mRList.children('.ui-jqgrid-hdiv').children(
                          '.ui-jqgrid-hbox');
                      mRLC.css("position", "relative");
                      mRLC.css('right', scrollPositionX);
                    });
                  },
                  resizeStop : function(width, index) {

                    var gridList = $('#gview_'+htmlTableId);
                    var gridListChild = gridList.children('.ui-jqgrid-bdiv');
                    var api = gridListChild.data('jsp');
                    api.reinitialise();

                    gridList = $('#gview_'+htmlTableId);
                    gridListChild = gridList.children('.ui-jqgrid-bdiv');
                    gridListChild.unbind('jsp-scroll-x');
                    gridListChild.bind('jsp-scroll-x', function(event,
                        scrollPositionX, isAtLeft, isAtRight) {
                      var mRList = $('#gview_'+htmlTableId);
                      var mRLC = mRList.children('.ui-jqgrid-hdiv').children(
                          '.ui-jqgrid-hbox');
                      mRLC.css("position", "relative");
                      mRLC.css('right', scrollPositionX);
                    });
                  }
          });
  
  // Hide "uid" column as it contains program generated unique id of row
  $("#"+htmlTableId).jqGrid('hideCol',columnName[columnName.indexOf("uid")]);
  
  // Remove type details from values
  for(var i=0; i<objectResults.length; i++){
    for(key in objectResults[i]){
      var arrVals = objectResults[i][key];
      if(arrVals != null && arrVals != undefined){
        if($.isArray(arrVals)){
          if(arrVals.length == 2){
            if(typeof (arrVals[1]) == "object"){ // if object, display its type as value
              objectResults[i][key] = arrVals[0];
            }else{ // else display its value
              objectResults[i][key] = arrVals[1];
            }
          }else{ // if ArrayList object, display its type as value
            objectResults[i][key] = arrVals[0];
          }
        }
      } 
    }
  }

  // Clear grid before populating rows into it
  $("#"+htmlTableId).jqGrid('clearGridData');
  $("#"+htmlTableId).jqGrid('addRowData',columnName[columnName.indexOf("uid")],objectResults);
}

// Function that displays object explorer in pop up
function displayPopUpObjectExplorer(rowId, rowIndex, columnIndex, event){
  //console.log("Row clicked: "+rowId+" "+rowIndex+" "+columnIndex+" "+event);
  
  var rowIdSplits = rowId.split('*');
  var selMember = null
  var selMemObject = null;
  var selMemObjectRow = null;
  var selectedRowData = null;
  
  if(rowIdSplits.length == 2){
    selMemObject = rowIdSplits[0];
    selMemObjectRow = rowIdSplits[1];
  }else if(rowIdSplits.length == 3){
    selMember = rowIdSplits[0];
    selMemObject = rowIdSplits[1];
    selMemObjectRow = rowIdSplits[2];
  }else{
    console.log("Incomplete inputs ");
    return;
  }
  
  var resResults = responseResult.result;
  var memberResults = null;
  var memberObjectResults = null;
  
  if(rowIdSplits.length == 2){
    memberResults = resResults;
  }else if(rowIdSplits.length == 3){
    for(var i=0; i< resResults.length; i++){
      if(resResults[i].member[1] == selMember){
        memberResults = resResults[i].result;
        break;
      }
    }
  }
  
  for(var i=0; i< memberResults.length; i++){
    // replace "[]" by "BxBr"
    var objType = memberResults[i].objectType.replace("[]", 'BxBr');
    if(objType == selMemObject){
      memberObjectResults = memberResults[i].objectResults;
      break;
    }
  }
  
  for(var i=0; i< memberObjectResults.length; i++){
    /*if(memberObjectResults[i].ID[1] == selMemObjectRow){
      selectedRowData = memberObjectResults[i];
      break;
    }*/
    if(memberObjectResults[i].uid == rowId){
      selectedRowData = memberObjectResults[i];
      break;
    }
  }
  
  var popUpData = formDataForPopUpGrid(selectedRowData);
  
  // clear pop grid data
  gridElement.jqGrid('clearGridData');
  // add pop grid new data
  gridElement[0].addJSONData({
    total : 1,
    page : 1,
    records : popUpData.length,
    rows : popUpData
  });
  
  // Display pop up grid
  $('#gridPopup').show();
  $('.ui-jqgrid-bdiv').jScrollPane();

  // Attaching handler to each row in object explorer to update vertical
  // scrollbar when user expands/collapse tree grid row
  $('div.tree-plus').click(function() {
    $('.ui-jqgrid-bdiv').jScrollPane();
  });
                  
  // Get the screen height and width
  var maskHeight = $(document).height();
  var maskWidth = $(window).width();

  //Set heigth and width to mask to fill up the whole screen
  $('#mask').css({'width':maskWidth,'height':maskHeight});
  
  //transition effect             
  $('#mask').fadeIn(1000);        
  $('#mask').fadeTo("fast",0.8);  

  //Get the window height and width
  var winH = $(window).height();
  var winW = $(window).width();

  //Set the popup window to center
  $('#gridPopup').css('top',  winH/2-$('#gridPopup').height()/2);
  $('#gridPopup').css('left', winW/2-$('#gridPopup').width()/2);

}

// Creates pop grid
function createPopUpObjectExplorer(){
  gridElement = $("#popUpExplorer");
  gridElement.jqGrid({
      datatype: "local",
      colNames:["Id","Key","Value","Type"],
      colModel:[
          {name:'id', index:'id', width:1, hidden:true, key:true},
          {name:'key', index:'key', width:220},
          {name:'value', index:'value', width:220},
          {name:'type', index:'type', width:230}
      ],
      cmTemplate: {sortable:false},
      height: '220',
      gridview: true,
      rowNum: 10000,
      sortname: 'id',
      treeGrid: true,
      treeGridModel: 'adjacency',
      treedatatype: "local",
      ExpandColumn: 'key',
  });
  
  gridElement.jqGrid('clearGridData');
  
}

// Function to prepare data for pop up object explorer
function formDataForPopUpGrid(data){
  
  var newArray = [];
  var idCounter = 1;

  function traverse(object, level, parentId) {
      for (var prop in object) {
          if (typeof(object[prop]) == "object") {
            if($.isArray(object[prop])){
              
              var arrObj = object[prop];
              if(arrObj.length == 2){
                
                if (typeof(arrObj[1]) == "object") {
                  //newArray.push({'key':prop, 'value':arrObj[1], 'type':arrObj[0]});
                  newArray.push({
                    'id':idCounter++,
                    'key':prop, 
                    'type':arrObj[0],
                    'value':"", // ignore value for composite data
                    'level':level,
                    'parent':parentId,
                    'isLeaf':false,
                    'expanded':false,
                    'loaded':true
                    });
                
                  //going on step down in the object tree!!
                  traverse(arrObj[1], level+1, idCounter-1);
                }else{
                  //newArray.push({'key':prop, 'value':arrObj[1], 'type':arrObj[0]});
                  newArray.push({
                    'id':idCounter++,
                    'key':prop, 
                    'type':arrObj[0],
                    'value':arrObj[1],
                    'level':level,
                    'parent':parentId,
                    'isLeaf':false,
                    'expanded':false,
                    'loaded':true
                    });
                }
              }else{
                //newArray.push({'key':prop, 'value':object[prop], 'type':typeof(object[prop])});
                newArray.push({
                  'id':idCounter++,
                  'key':prop, 
                  'type':typeof(object[prop]),
                  'value':object[prop],
                  'level':level,
                  'parent':parentId,
                  'isLeaf':false,
                  'expanded':false,
                  'loaded':true
                  });
                
                // todo : multiple value traversing in array
              }
            }else{
              newArray.push({
                'id':idCounter++,
                'key':prop, 
                'type':"object",
                'value':"", // ignore value for composite data
                'level':level,
                'parent':parentId,
                'isLeaf':false,
                'expanded':false,
                'loaded':true
                });
              //going on step down in the object tree!!
              traverse(object[prop], level+1, idCounter-1);
            }
              
          }else{
            //console.log(prop + " : "+object[prop]);
            if(prop != "uid"){
              newArray.push({
                  'id':idCounter++,
                  'key':prop, 
                  'type':typeof(object[prop]),
                  'value':object[prop],
                  'level':level,
                  'parent':parentId,
                  'isLeaf':false,
                  'expanded':false,
                  'loaded':true
                  });
            }
          }
      }
  }

  // start traversing into data object 
  traverse(data, 0, 0);
  
  return newArray;
}

//Function for converting raw response into expected format
function convertRawResponseToExpectedFormat(rawResponeData){
  
  if(rawResponeData === null || rawResponeData === undefined){
    return;
  }
  
  var finalResponseData = {};
  var finalResponseResults = [];
  
  if(rawResponeData.result != null || rawResponeData.result !== undefined){
    var rawResponeDataResult = rawResponeData.result;
    
    for(var i=0; i<rawResponeDataResult.length; i++){
      if(rawResponeDataResult[i] != null){
        if(typeof(rawResponeDataResult[i]) == "object"){
          if($.isArray(rawResponeDataResult[i])){
            // Cluster level result set
            finalResponseResults = convertToExpectedObjectsFormat(rawResponeDataResult, "");
            break;
            
          }else if(rawResponeDataResult[i].member != null && rawResponeDataResult[i].member !== undefined){
            
            var responseForMember = {};
            responseForMember.member = rawResponeDataResult[i].member[0];
            responseForMember.result = convertToExpectedObjectsFormat(rawResponeDataResult[i].result, rawResponeDataResult[i].member[0][1]);
            
            // add into final results
            finalResponseResults.push(responseForMember);
          }
        }
      }
    }
  }
  
  finalResponseData.result = finalResponseResults;
  //responseResult = finalResponseData;
  return finalResponseData;
}

// Function for converting raw response into expected object wise results format
function convertToExpectedObjectsFormat(rawResponseResult, prefixForId){

  let entry;
  let objectResults;
  const expResponseResult = [];

  if(rawResponseResult != null ){
    
    for(let i=0; i < rawResponseResult.length; i++){
      if(rawResponseResult[i] != null){
        
        if(expResponseResult.length > 0){
          // search expected object type in expResponseResult
          let flagObjectFound = false;
          for(let j=0 ; j < expResponseResult.length ; j++){
            if(expResponseResult[j].objectType === rawResponseResult[i][0]){
              // required object found
              flagObjectFound = true;
              objectResults = expResponseResult[j].objectResults;
              entry = htmlEncodeEntry(rawResponseResult[i]);

              // add unique id for new entry
              entry.uid = generateEntryUID(prefixForId, expResponseResult[j].objectType, objectResults.length);
              
              objectResults.push(entry);
              break;
            }
          }
          if(!flagObjectFound){  // required object not found in expResponseResult
            expResponseResult.push(addToExpResponseResult(rawResponseResult[i], prefixForId));
          }
        }else{  // expResponseResult is empty
          expResponseResult.push(addToExpResponseResult(rawResponseResult[i], prefixForId));
        }
      }
    }
  }
  
  return expResponseResult;
}

// Add results to the expected responseResults
function addToExpResponseResult(rawResponseResultEntry, prefixForId) {
  let objectResults = [];
  let type = rawResponseResultEntry[0];
  let entry = htmlEncodeEntry(rawResponseResultEntry, prefixForId);

  // add unique id for new entry
  entry.uid = generateEntryUID(prefixForId, type, objectResults.length);

  objectResults.push(entry);

  let newResultObject = {};
  newResultObject.objectType = type;
  newResultObject.objectResults = objectResults;

  return newResultObject;
}

// Ensure that strings are HTML encoded to reduce likelihood of XSS attacks
function htmlEncodeEntry(rawResponseResultEntry, prefixForId) {
  let type = htmlEncodeStringsAndObjects(rawResponseResultEntry[0]);
  let entry = rawResponseResultEntry[1];

  let entryObj = {};

  // if entry is not object then convert it into object
  if(typeof(entry) == "object" ){
    entryObj = htmlEncodeStringsAndObjects(entry);
  } else {
    entryObj[type] = htmlEncodeStringsAndObjects(entry)
  }

  return entryObj;
}

function htmlEncodeStringsAndObjects(raw) {
  switch(typeof(raw)) {
    case "string":
      return $('<pre/>').text(raw).html();
    case "object":
      let objectAsString = JSON.stringify(raw);
      objectAsString = $('<pre/>').text(objectAsString).html();
      return JSON.parse(objectAsString);
    default:
      return raw
  }
}

// Function to generate unique idetifier for entry
function generateEntryUID(prefixForId, type, len) {

  var uid = "";

  if(type.indexOf("[]") != -1){
    type = type.replace("[]","BxBr");
  }

  if (prefixForId != "") {
    uid = prefixForId + "*" + type + "*" + len;
  } else {
    uid = type + "*" + len;
  }

  return uid;
}

// Function that posts the query result response received to backend in order
// export it into file
function exportResult() {
 var queryText = $('#dataBrowserQueryText').val();
  var selectedMemberNames = "";

  if(isEmpty(queryText)){
    alert("Please enter query to execute..");
    return;
  }

  // Determine selected members query to be execute on
  if($("#membersList").html() != ""){
    var selectedMembers = $( "input[type=checkbox][name=Member]:checked" );
    for(var i=0; i< selectedMembers.length; i++){
      if(selectedMemberNames == ""){
        selectedMemberNames = selectedMembers[i].value;
      }else{
        selectedMemberNames += ","+selectedMembers[i].value;
      }
    }
  }

     window.open("dataBrowserExport?query=" + encodeURIComponent(queryText) + "&members=" + encodeURIComponent(selectedMemberNames), "_blank");
}
