<?php
/*******************************************************************************
 *
 * LEIDEN OPEN VARIATION DATABASE (LOVD)
 *
 * Created     : 2007-06-28
 * Modified    : 2010-01-18
 * For LOVD    : 2.0-24
 *
 * Access      : Public
 * Purpose     : Allow overview of all variants in all genes based on a search
 *               on the Patient/Origin/* fields.
 *
 * Copyright   : 2004-2010 Leiden University Medical Center; http://www.LUMC.nl/
 * Programmers : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *               Ir. Gerard C.P. Schaafsma <G.C.P.Schaafsma@LUMC.nl>
 * Last edited : Ing. Ivo F.A.C. Fokkema <I.F.A.C.Fokkema@LUMC.nl>
 *
 *
 * This file is part of LOVD.
 *
 * LOVD is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * LOVD is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LOVD; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *************/

define('ROOT_PATH', './');
require ROOT_PATH . 'inc-init.php';

if (HAS_AUTH) {
    // If authorized, check for updates.
    require ROOT_PATH . 'inc-upgrade.php';
}

// 2010-01-12; 2.0-24 Need a table for hiding columns, just select all variant and selected patient columns
$aTable = array('pathogenic_' => 'Path.');
$sQ = 'SELECT c.colid, c.head_column, c.col_order FROM ' . TABLE_COLS . ' AS c WHERE c.colid LIKE "Variant/%" UNION SELECT pc.colid, c.head_column, pc.col_order FROM ' . TABLE_PATIENTS_COLS . ' AS pc LEFT JOIN ' . TABLE_COLS . ' AS c USING (colid) ' . ($_AUTH && $_AUTH['level'] >= LEVEL_MANAGER? '' : 'WHERE pc.public=1 ') . 'ORDER BY LEFT(colid, 3) DESC, (col_order = 0) ASC, col_order ASC, colid';
$q = mysql_query($sQ);
while ($z = mysql_fetch_assoc($q)) {
    $aTable[$z['colid']] = $z['head_column'];
}

// Additional columns for authorized users.
if ($_AUTH && $_AUTH['level'] >= LEVEL_MANAGER) {
    $aTable['status_'] = 'Status';
}

// 2008-09-17; 2.0-12; Column hiding also introduced in this variant overview.
if (isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols'])) {
    $aHiddenColumns = preg_split('/[;,]+/', $_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']); // Support both , and ; as a separator for backwards compatibility to LOVD 2.0-22 and before.
} else {
    $aHiddenColumns = array();
}

if (!empty($_GET['hide_col'])) {
    // 2009-11-06; 2.0-23; Received strings are now converted into arrays to be processed the same way.
    if (is_string($_GET['hide_col'])) {
        $_GET['hide_col'] = explode(',', $_GET['hide_col']);
    }
    foreach ($_GET['hide_col'] as $sCol) {
        // We don't check the availability of the column here, because some other gene may have it.
        if ($sCol == 'all') {
            foreach ($aTable as $key => $value) {
                if (!in_array($key, $aHiddenColumns)) {
                    $aHiddenColumns[] = $key;
                }
            }
        } elseif (!in_array($sCol, $aHiddenColumns)) {
            $aHiddenColumns[] = $sCol;
        }
    }
}
if (!empty($_GET['show_col'])) {
    // 2009-11-06; 2.0-23; Received strings are now converted into arrays to be prosessed the same way.
    if (is_string($_GET['show_col'])) {
        $_GET['show_col'] = explode(',', $_GET['show_col']);
    }
    foreach ($_GET['show_col'] as $sCol) {
        if ($sCol == 'all') {
            $aHiddenColumns = array();
        } else {
            $nKey = array_search($sCol, $aHiddenColumns);
            if ($nKey !== false) {
                unset($aHiddenColumns[$nKey]);
            }
        }
    }
}

// Now, store the settings in the cookie.
if ((!isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']) && count($aHiddenColumns)) || (isset($_COOKIE[$_SETT['cookie_id'] . '_hidden_cols']) && $_COOKIE[$_SETT['cookie_id'] . '_hidden_cols'] != $aHiddenColumns)) {
    setcookie($_SETT['cookie_id'] . '_hidden_cols', implode(',', $aHiddenColumns), time() + 60*60*24*31);
}

// Then, clean $aHiddenCols so that it will only contain columns that are actually going to be on the screen. This is for counting purposes, to see how much has been hidden.
foreach ($aHiddenColumns as $nKey => $sCol) {
    if (!isset($aTable[$sCol])) {
        unset($aHiddenColumns[$nKey]);
    }
}

require ROOT_PATH . 'inc-top.php';
require ROOT_PATH . 'inc-lib-form.php';
require ROOT_PATH . 'inc-lib-list.php';
require ROOT_PATH . 'class/currdb.php';
lovd_printHeader('variant_search', 'LOVD - Search variants');





// Do we even have the origin fields enabled?
// Sounds stupid, but I need a gene here...
if ($_SESSION['currdb']) {
    $_CURRDB = new CurrDB();
} elseif (!GENE_COUNT) {
    // Standard situation.
    $_CURRDB = new CurrDB(false);
} else {
    // Fetch a gene. Any gene!
    $aGenes = lovd_getGeneList();
    $sSymbol = $aGenes[0];
    $_CURRDB = new CurrDB(false, $sSymbol);
}

// Hide non-public columns for all curators.
// FIXME; Actually, a curator who owns every single gene should be included... Hmm...
$_CURRDB->hideCols('public', ($_AUTH['level'] >= LEVEL_MANAGER));
$aColList = $_CURRDB->getColList();
$aOriginCols = array();
foreach ($aColList as $sCol) {
    if (substr($sCol, 0, 15) == 'Patient/Origin/') {
        $aOriginCols[] = $sCol;
    }
}

$nOriginCols = count($aOriginCols);
if (!$nOriginCols) {
    // No origin columns enabled... goodbye!
    lovd_showInfoTable('There are no Patient/Origin columns enabled. Therefore this overview is non functional.', 'stop');
    require ROOT_PATH . 'inc-bot.php';
    exit;
}





// Show form in all cases.
print('      Fill in at least one of the origin fields to search the gene databases.<BR>' . "\n" .
      '      <BR>' . "\n\n");

// Select current gene, at least.
if (empty($_GET['genes']) || !is_array($_GET['genes'])) {
    $_GET['genes'] = array();
    if ($_SESSION['currdb']) {
        $_GET['genes'][] = $_SESSION['currdb'];
    }
}

// 2008-07-31; 2.0-10; Implement XSS check on search terms.
foreach ($_GET as $key => $val) {
    if (!is_array($val) && $val != strip_tags($val)) {
        $_GET[$key] = '';
    }
}

// Fetch genes.
$qGenes = mysql_query('SELECT symbol, gene FROM ' . TABLE_DBS . ' ORDER BY symbol');
$nGenes = mysql_num_rows($qGenes);

// Table.
// 2008-09-17; 2.0-12; Allow form to store info for hiding/showing columns.
print('      <FORM action="' . $_SERVER['PHP_SELF'] . '" method="get">' . "\n" .
      '        <INPUT type="hidden" name="order" value="' . (!empty($_GET['order'])? $_GET['order'] : '') . '">' . "\n" .
      '        <INPUT type="hidden" name="hide_col" value="">' . "\n" .
      '        <INPUT type="hidden" name="show_col" value="">' . "\n" .
      '        <TABLE border="0" cellpadding="0" cellspacing="1" width="950">' . "\n" .
      '          <TR>' . "\n" .
      '            <TD valign="top" rowspan="' . ($nOriginCols + 3) . '" width="33%" style="padding-right : 10px;">' . "\n" .
      '              <B>Select gene(s) you wish to search in</B><BR>' . "\n" .
      '              <SELECT name="genes[]" size="' . ($nGenes > 5? 5 : $nGenes) . '" multiple>' . "\n");
while ($zGenes = mysql_fetch_assoc($qGenes)) {
    print('              <OPTION value="' . $zGenes['symbol'] . '"' . (in_array($zGenes['symbol'], $_GET['genes'])? ' selected' : '') . '>' . $zGenes['symbol'] . ' (' . lovd_shortenString($zGenes['gene'], 50) . ')</OPTION>' . "\n");
}
print('              </SELECT><BR>' . "\n" .
      '              <A href="#" onclick="var list = document.forms[0][\'genes[]\']; for (i=0;i<list.options.length;i++) { list.options[i].selected = true; }; return false">Select all</A>' . "\n" .
      '            </TD>' . "\n" .
      '            <TH colspan="2">Please fill in the search terms in the field' . ($nOriginCols == 1? '' : 's') . ' below</TH></TR>');

// Rest will be generated automatically.
// Array which will make up the form table.
$aForm = array(
                array('GET', '', '', '33%', '33%'),
              );

foreach ($aOriginCols as $sCol) {
    $aForm = array_merge($aForm, $_CURRDB->buildFormTable('Patient', $sCol));
}

// Modify form so it can be used for searching.
foreach ($aForm as $key => $aField) {
    // 2008-01-28; 2.0-04; Added this check to ensure this does not modify the $aForm[0] element or any comments.
    if ($key && $aField[0] != 'print' && $aField[1] != 'print') {
        $aField[2] = 'search_' . $aField[2];
        $aForm[$key] = $aField;
    }
}
$aForm[] = 'skip';
$aForm[] = array('', 'submit', 'Search sequence variants');
$_MODULES->processForm('VariantsOverviewOriginSearch', $aForm);
lovd_viewForm($aForm);

print('</TABLE></FORM><BR>' . "\n\n");

// That's it? Or do we need to search?
$bSearched = false;
foreach ($aOriginCols as $sCol) {
    if (array_key_exists('search_' . $sCol, $_GET) && $_GET['search_' . $sCol]) {
        $bSearched = true;
        break;
    }
}

if (!$bSearched || !count($_GET['genes'])) {
    // Can't search with this.
    require ROOT_PATH . 'inc-bot.php';
    exit;
}

// 2010-01-13; 2.0-24; Show link at all times. Simply activate only when needed.
$sNavigation = '';
// "Unhide all columns".
if (count($aHiddenColumns)) {
    $sNavigation = '<A href="#" onclick="document.forms[0].show_col.value=\'all\';document.forms[0].submit();">';
} else {
    $sNavigation = '<A style="color : #999999;">';
}
$sNavigation .= 'Unhide all columns</A>';
// 2010-01-13; 2.0-24; Added "Hide specific columns" and "Hide all columns" links.
// "Hide specific columns".
// "Hide all columns".
if (count($aHiddenColumns) < count($aTable)-1) {
    $sNavigation .= ' | <A href="#" onclick="lovd_openWindow(\'views_columns.php' . '?action=full_all&amp;hide=true' . lovd_showSID(true, true) . '\', \'HideColumns\', 300, 500); return false;">Hide Specific Columns</A>' .
                    ' | <A href="#" onclick="document.forms[0].hide_col.value=\'all\';document.forms[0].submit();">Hide all columns</A>';
} else {
    $sNavigation .= ' | <A style="color : #999999;">Hide Specific Columns</A>' .
                    ' | <A style="color : #999999;">Hide all columns</A>';
}
print('      <TABLE border="0" cellpadding="0" cellspacing="0" width="960">' . "\n" .
      '        <TR>' . "\n" .
      '          <TD valign="top">' . "\n");
lovd_viewNavigation($sNavigation, 6);
print('          </TD>' . "\n" .
        '      </TR></TABLE><BR>' . "\n\n");



// Per gene, show the variants connected to the patients found by the search terms.
foreach ($_GET['genes'] as $sSymbol) {
    // Whether or not a user can see non-public data.
    $bCurator = lovd_isCurator($sSymbol);

    $_CURRDB = new CurrDB(true, $sSymbol);
    $_CURRDB->hideCols('public', $bCurator);

    // Standard query, will be extended later on.
    $sQ = 'SELECT v.*, p.*, p2v.allele, p2v.status' . ($bCurator? ', stat.status_text AS status_' : '') . ', path.pathogenic_text AS pathogenic_, count(p2v_2.variantid) AS mutations, s.reference AS subs_reference FROM ' . TABLEPREFIX . '_' . $sSymbol . '_variants AS v LEFT JOIN ' . TABLE_PAT2VAR . ' AS p2v ON (p2v.symbol = "' . $sSymbol . '" AND v.variantid = p2v.variantid) LEFT JOIN ' . TABLE_PATIENTS . ' AS p USING (patientid) LEFT OUTER JOIN ' . TABLE_PAT2VAR . ' AS p2v_2 ON (p.patientid = p2v_2.patientid' . ($bCurator? '' : ' AND p2v_2.status >= ' . STATUS_MARKED) . ')' . ($bCurator? ' LEFT OUTER JOIN ' . TABLE_VAR_STATUS . ' AS stat ON (p2v.status = stat.status)' : '') . ' LEFT OUTER JOIN ' . TABLE_PATHOGENIC . ' AS path ON (p2v.pathogenic = path.pathogenic) LEFT OUTER JOIN ' . TABLE_SUBS . ' AS s ON (p.submitterid = s.submitterid) WHERE ' . ($bCurator? '1=1' : 'p2v.status >= ' . STATUS_MARKED . ' AND p2v_2.status >= ' . STATUS_MARKED);

    // Guess the column used to describe the mutation.
    $sMutationCol = $_CURRDB->getMutationCol();

    // Here starts the actual building of the query.
    // SEARCH: Advanced text search.
    // FIXME; just the origin columns? Simple change to $_CURRDB->buildSearchList() needed, like buildFormTable().
    // 2009-06-26; 2.0-19; Now actually implement a proper selection-list search implementation, like variants_search.php. Separated from this part.
    $aSearchText = array_merge(
        $_CURRDB->buildSearchList('text'),
        $_CURRDB->buildSearchList('textarea'),
        $_CURRDB->buildSearchList('checkbox'));
    foreach ($aSearchText as $key => $val) {
        if (isset($_GET[$key]) && trim($_GET[$key])) {
            $a = explode(' ', trim($_GET[$key]));
            foreach ($a as $sTerm) {
                if ($sTerm) {
                    if (substr_count($sTerm, '|') && preg_match('/^[^|]+(\|[^|]+)+$/', $sTerm)) {
                        // OR.
                        $aOR = explode('|', $sTerm);
                        $sQ .= ' AND (';
                        foreach ($aOR as $nTerm => $sTerm) {
                            // 2009-03-03; 2.0-17; Advanced searching did not allow to combine NOT and OR searches.
                            if (substr($sTerm, 0, 1) == '!') {
                                // NOT.
                                $sQ .= ($nTerm? ' OR ' : '') . $val . ' NOT LIKE "%' . lovd_escapeSearchTerm(substr($sTerm, 1)) . '%"';
                            } else {
                                // Common search term.
                                $sQ .= ($nTerm? ' OR ' : '') . $val . ' LIKE "%' . lovd_escapeSearchTerm($sTerm) . '%"';
                            }
                        }
                        $sQ .= ')';
                    } elseif (substr($sTerm, 0, 1) == '!') {
                        // NOT.
                        $sQ .= ' AND ' . $val . ' NOT LIKE "%' . lovd_escapeSearchTerm(substr($sTerm, 1)) . '%"';
                    } else {
                        // Common search term.
                        $sQ .= ' AND ' . $val . ' LIKE "%' . lovd_escapeSearchTerm($sTerm) . '%"';
                    }
                }
            }
        }
    }

    // 2009-04-21; 2.0-18; Aargh, we need to implement a select search here, because we build the form elements on this page.
    // 2009-06-26; 2.0-19; Now actually implement a proper selection-list search implementation, like variants_search.php.
    // SEARCH: List search.
    $aSearchSelect = $_CURRDB->buildSearchList('select');
    foreach ($aSearchSelect as $key => $val) {
        if (!empty($_GET[$key])) {
            if (!is_array($_GET[$key])) {
                // Single select.
                $sQ .= ' AND CONCAT(";", ' . $val . ', ";") LIKE "%;' . $_GET[$key] . ';%"';
            } else {
                // Multiple select.
                $sQ .= ' AND CONCAT(";", ' . $val . ', ";") LIKE "%;' . implode('" AND CONCAT(";", ' . $val . ', ";") LIKE "%', $_GET[$key]) . ';%"';
            }
        }
    }

    // Add p2v.allele here to show homozygous mutations twice in the list.
    $sQ .= ' GROUP BY v.variantid, p.patientid';

    $nResults = mysql_num_rows(mysql_query($sQ));

    // SORT: Current settings.
    if (isset($_GET['order']) && $_GET['order']) {
        $aOrder = explode(',', $_GET['order']);
    } else {
        $aOrder = array('', '');
    }

    // SORT: Column data.
    $aOrderList = array_merge(
             array('pathogenic_' => array('path.pathogenic', 'ASC')),
             array('sort' => array('v.sort', 'ASC')),
             $_CURRDB->buildOrderList());
    if ($bCurator) {
        // Add status.
        $aOrderList['status_'] = array('p2v.status', 'ASC');
    }
    if (!array_key_exists($aOrder[0], $aOrderList)) {
        // 2007-10-19; 2.0-01; Changed default sorting column from Exon to Variant/DNA.
        $aOrder[0] = $sMutationCol;
    }
    if ($aOrder[1] != 'ASC' && $aOrder[1] != 'DESC') {
        $aOrder[1] = $aOrderList[$aOrder[0]][1];
    }

    // 2007-10-19; 2.0-01; Fixed bug #18 - Variants do not sort correctly when selecting DNA column to sort on.
    // 2009-06-12; 2.0-19; Changed again, since the sort column now also contains the Exon information.
    // SORT: Additional sort columns (faking a perfect sort).
    $sOrderPre = '';
    $sOrderPost = '';
    if ($aOrder[0] == 'Variant/DNA') {
        // 1) In case of the mutation column, sort FIRST on sort.
        $sOrderPre .= 'v.sort ' . $aOrder[1] . ', ';
    } else {
        // 2) In case of anything else, sort on sort AFTER.
        $sOrderPost .= ', v.sort ' . $aOrder[1];
    }

    $sQ .= ' ORDER BY ' . $sOrderPre . $aOrderList[$aOrder[0]][0] . ' ' . $aOrder[1] . $sOrderPost . ', p.patientid';



    if (!$nResults) {
        // Searched, but no results.
        // We actually didn't check, if you're not a curator for all genes, if variants were public.
        print('      No variants found in the ' . $sSymbol . ' gene that match your search terms.<BR><BR>' . "\n\n");
        continue;
    }

    $q = mysql_query($sQ);
    if (!$q) {
        lovd_dbFout('VariantsOverviewOrigin', $sQ, mysql_error());
    }

    $n = (isset($nResults)? $nResults : $nTotal);
    print('      <SPAN class="S13"><B>' . $n . ($bCurator? '' : ' public') . ' entr' . ($n == 1? 'y' : 'ies') . ' in ' . $sSymbol . '</B></SPAN><BR>' . "\n");

    // Array which will make up the table (header and data).
    $aTable = array('pathogenic_' => array('Path.', 50));
    $aTable = array_merge($aTable, $_CURRDB->buildTable('list'));

    // Additional columns for authorized users.
    if ($bCurator) {
        $aTable['status_'] = array('Status', 75);
    }

    // Stretch it up, cause we're adding data to it...
    if ($aTable[$sMutationCol][1] < 200) {
        $aTable[$sMutationCol][1] = 200;
    }

    // Total width.
    $nTotalWidth = 2;
    $nHiddenColWidth = 5;
    $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
    foreach ($aTable as $sCol => $aCol) {
        // 2008-02-15; 2.0-04; Hidden columns are smaller!
        if (in_array($sCol, $aHiddenColumns) && $sCol != $sMutationCol) {
            // We don't allow the MutationCol to be hidden!
            // 2009-12-17; 2.0-24; We now group hidden columns into one.
            if (!$bPrevHidden) {
                $nTotalWidth += $nHiddenColWidth + 8;
                $bPrevHidden = true;
            }
        } else {
            $nTotalWidth += $aCol[1] + 8;
            $bPrevHidden = false;
        }
    }



    // Stretch table up, to respect column widths.
    print('      <IMG src="' . ROOT_PATH . 'gfx/trans.png" alt="" width="' . $nTotalWidth . '" height="1"><BR>' . "\n\n");

    // Table.
    print('      <TABLE border="0" cellpadding="0" cellspacing="1" class="data">' . "\n" .
          '        <TR>');

    $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
    // Print the column headers.
    foreach ($aTable as $sField => $aCol) {
        if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
            // We don't allow the MutationCol to be hidden!
            // 2009-12-17; 2.0-24; We now group hidden columns into one.
            if (!$bPrevHidden) {
                print("\n" . '          <TH width="' . $nHiddenColWidth . '"><A href="#" onclick="lovd_openWindow(\'views_columns.php?action=full_all' . lovd_showSID(true, true) . '\', \'ShowColumns\', 300, 500);"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_show.png" alt="Unhide specific columns" title="Unhide specific columns" width="5" height="13" border="0"></A></TH>');
                $bPrevHidden = true;
            }
            continue;
        } else {
            $bPrevHidden = false;
        }

        // Location onclick changed because of the search fields within the same <TH>. Clicking on the field triggers a sort.
        print("\n" . '          <TH valign="top"' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '"' : '') . '>' .
              (array_key_exists($sField, $aOrderList)? "\n" .
                                                           '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                           '              <TR' . (array_key_exists($sField, $aOrderList)? ' onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' . "\n" .
                                                           '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                           '                <TD align="right" width="13">' . ($sField == $sMutationCol? '&nbsp;' : '<IMG src="' . ROOT_PATH . 'gfx/order_arrow_hide.png" alt="Hide ' . $aCol[0] . ' column" title="Hide ' . $aCol[0] . ' column" width="11" height="11" onclick="document.forms[0].hide_col.value=\'' . $sField . '\';document.forms[0].submit();" style="margin : 1px;">') . '</TD>' . "\n" .
                                                           '                <TD align="right" width="13"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="' . ROOT_PATH . 'gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR>' .
                                                           '</TABLE>' : $aCol[0]) . '</TH>');
    }
    print('</TR>');

    $i = 0;
    $nRepeat = 25;
    while ($zData = mysql_fetch_assoc($q)) {
        // Print header repeatedly.
        $i ++;
        if (!($i%($nRepeat+1))) {
            print("\n" .
                  '        <TR>');
            $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
            // Print the repeated column headers.
            foreach ($aTable as $sField => $aCol) {
                if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
                    // We don't allow the MutationCol to be hidden!
                    // 2009-12-17; 2.0-24; We now group hidden columns into one.
                    if (!$bPrevHidden) {
                        print("\n" . '          <TH width="' . $nHiddenColWidth . '"><A href="#" onclick="lovd_openWindow(\'views_columns.php?action=' . $_GET['action'] . lovd_showSID(true, true) . '\', \'ShowColumns\', 300, 500);"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_show.png" alt="Unhide specific columns" title="Unhide specific columns" width="5" height="13" border="0"></A></TH>');
                        $bPrevHidden = true;
                    }
                    continue;
                } else {
                   $bPrevHidden = false;
                }

                print("\n" . '          <TH valign="top"' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . (array_key_exists($sField, $aOrderList)? ' class="order' . ($aOrder[0] == $sField? 'ed' : '') . '"' : '') . '>' .
                      (array_key_exists($sField, $aOrderList)? "\n" .
                                                                   '            <TABLE border="0" cellpadding="0" cellspacing="0" width="100%" class="S11">' . "\n" .
                                                                   '              <TR' . (array_key_exists($sField, $aOrderList)? ' onclick="document.forms[0].order.value=\'' . $sField . ',' . ($aOrder[0] == $sField? ($aOrder[1] == 'ASC'? 'DESC' : 'ASC') : $aOrderList[$sField][1]) . '\';document.forms[0].submit();"' : '') . '>' . "\n" .
                                                                   '                <TH>' . str_replace(' ', '&nbsp;', $aCol[0]) . '</TH>' . "\n" .
                                                                   '                <TD align="right" width="13">' . ($sField == $sMutationCol? '&nbsp;' : '<IMG src="' . ROOT_PATH . 'gfx/order_arrow_hide.png" alt="Hide ' . $aCol[0] . ' column" title="Hide ' . $aCol[0] . ' column" width="11" height="11" onclick="document.forms[0].hide_col.value=\'' . $sField . '\';document.forms[0].submit();" style="margin : 1px;">') . '</TD>' . "\n" .
                                                                   '                <TD align="right" width="13"><IMG src="' . ROOT_PATH . 'gfx/order_arrow_desc' . ($aOrder[0] == $sField && $aOrder[1] == 'DESC'? '_sel' : '') . '.png" alt="Descending" title="Descending" width="13" height="6"><BR><IMG src="' . ROOT_PATH . 'gfx/order_arrow_asc' . ($aOrder[0] == $sField && $aOrder[1] == 'ASC'? '_sel' : '') . '.png" alt="Ascending" title="Ascending" width="13" height="6"></TD></TR>' .
                                                                   '</TABLE>' : $aCol[0]) . '</TH>');
            }
            print('</TR>');
            $i = 1;
        }

        print("\n" .
              '        <TR valign="top"' . ($bCurator && $zData['status'] < STATUS_OK? ' class="' . ($zData['status'] < STATUS_MARKED? 'del' : 'marked') . '"' : '') . ' style="cursor : pointer; cursor : hand;" onmouseover="this.className = \'' . ($bCurator && $zData['status'] < STATUS_OK? ($zData['status'] < STATUS_MARKED? 'del_' : 'marked_') : '') . 'hover\';" onmouseout="this.className = \'' . ($bCurator && $zData['status'] < STATUS_OK? ($zData['status'] < STATUS_MARKED? 'del' : 'marked') : '') . '\';" onclick="window.location.href = \'' . lovd_cleanDirName(dirname($_SERVER['PHP_SELF']) . '/' . ROOT_PATH) . 'variants.php?select_db=' . $sSymbol . '&amp;action=view&amp;view=' . $zData['patientid'] . '%2C' . $zData['variantid'] . '%2C' . $zData['allele'] . lovd_showSID(true, true) . '\';">');

        // Create normal anchor link as well.
        $sLink = '<A href="' . ROOT_PATH . 'variants.php?select_db=' . $sSymbol . '&amp;action=view&amp;view=' . $zData['patientid'] . '%2C' . $zData['variantid'] . '%2C' . $zData['allele'] . lovd_showSID(true, true) . '" class="data"' . ($zData['status'] < STATUS_MARKED? ' style="color : #AAAAAA;"' : '') . '>';
        if ($sMutationCol) {
            $zData[$sMutationCol] = $sLink . $zData[$sMutationCol] . '</A>';
        }

        // Very unlikely not to function; only if none of the HGVS standard DNA, RNA or Protein fields are enabled, this will fail.
        if ($zData['mutations'] > 1) {
            // 2008-03-27; 2.0-05; Show only a maximum number of other mutations associated with this patient. Otherwise, hide them all.
            if ($zData['mutations'] > 10) {
                // Too many to fetch; wouldn't look good.
                // FIXME; make a setting out of this.
                $zData[$sMutationCol] .= '<BR>&nbsp;&nbsp;&nbsp;&nbsp;+ ' . ($zData['mutations'] - 1) . ' others';
            } else {
                // Fetch info on other mutations.
                // FIXME; this may be quite a performance lag with a high $_GET['limit'] value and lots of associated mutations.
                // Backwards compatible with MySQL 4.0 and earlier; if we rely on MySQL 4.1, we could do a GROUP_CONCAT in top mutation fetch query that save us these queries.
                $sQOther = 'SELECT p2v.*, v.`' . $sMutationCol . '` FROM ' . TABLE_PAT2VAR . ' AS p2v LEFT OUTER JOIN ' . TABLEPREFIX . '_' . $sSymbol . '_variants AS v ON (p2v.symbol = "' . $sSymbol . '" AND v.variantid = p2v.variantid) WHERE ' . ($bCurator? '' : 'p2v.status >= ' . STATUS_MARKED . ' AND ') . 'p2v.patientid = "' . $zData['patientid'] . '" AND !(p2v.symbol = "' . $sSymbol . '" AND v.variantid = "' . $zData['variantid'] . '" AND p2v.allele = "' . $zData['allele'] . '") ORDER BY v.sort ASC';

                $qOther = mysql_query($sQOther);
                $sOther = '';
                $aOther = array();

                // Loop other variants found in the same patient.
                while ($zOther = mysql_fetch_assoc($qOther)) {
                    if ($zOther['symbol'] == $sSymbol) {
                        // Other variant in same gene.
                        $sOther .= ($sOther? ', ' : '') . $zOther[$sMutationCol];
                    } else {
                        // Other variant in different gene.
                        if (!isset($aOther[$zOther['symbol']])) {
                            $aOther[$zOther['symbol']] = 0;
                        }
                        $aOther[$zOther['symbol']] ++;
                    }
                }

                if (count($aOther)) {
                    foreach ($aOther as $sGene => $nOther) {
                        $sOther .= ($sOther? ', ' : '') . $sGene . ' (' . $nOther . ')';
                    }
                }

                $zData[$sMutationCol] .= '<BR>&nbsp;&nbsp;&nbsp;&nbsp;+ ' . $sOther;
            }
        }

        // Parse and build Custom Links.
        lovd_buildLinks($zData);

        // Transform select lists' values.
        $_CURRDB->transformSelectValues($zData);

        // FIXME;
        // Shorten strings for TEXT that surely doesn't contain HTML tags... HOW?

        // Submitter reference...
        if ($_CURRDB->colExists('Patient/Reference') && $zData['subs_reference']) {
            // 2008-03-28; 2.0-05; Show full submitter information directly from this link.
            $zData['Patient/Reference'] .= (!empty($zData['Patient/Reference'])? ', ' : '') . '<A href="#" onclick="lovd_openWindow(\'' . ROOT_PATH . 'submitters.php?action=view&amp;view=' . $zData['submitterid'] . lovd_showSID(true, true) . '\', \'SubmitterView\', 400, 175, event.screenX - 50, event.screenY - 50); return false;">' . $zData['subs_reference'] . '</A>';
        }

        $bPrevHidden = false; // 2009-12-17; 2.0-24; To remember if the previous column was hidden. We now group hidden columns into one.
        // Print the data.
        foreach ($aTable as $sField => $aCol) {
            // 2008-02-15; 2.0-04; Hidden columns are smaller!
            if (in_array($sField, $aHiddenColumns) && $sField != $sMutationCol) {
                // We don't allow the MutationCol to be hidden!
                // 2009-12-17; 2.0-24; We now group hidden columns into one.
                if (!$bPrevHidden) {
                    print("\n" . '          <TD width="' . $nHiddenColWidth . '">&nbsp;</TD>');
                    $bPrevHidden = true;
                }
                continue;
            } else {
               $bPrevHidden = false;
            }
            print("\n" . '          <TD' . (!empty($aCol[2])? ' ' . $aCol[2] : '') . (is_numeric($aCol[1])? ' width="' . $aCol[1] . '"' : '') . ($aOrder[0] == $sField? ' class="ordered"' : '') . '>' . (!$zData[$sField]? '-' : $zData[$sField]) . '</TD>');
        }
        print('</TR>');
    }
    print('</TABLE><BR>' . "\n\n");
}

require ROOT_PATH . 'inc-bot.php';
exit;
?>