#!/usr/bin/env python

# Copyright (C) 2011 Ian W. Harry
#
# This program 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 3 of the License, or (at your
# option) any later version.
#
# This program 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 this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

# =============================================================================
# Preamble
# =============================================================================

from __future__ import division

import os,sys,datetime,re,glob,shutil,ConfigParser
from optparse import OptionParser
from glue import markup
from pylal import grbsummary,date,rate,antenna,git_version
from pylal.xlal.datatypes.ligotimegps import LIGOTimeGPS
import os.path
#from pylal.coh_PTF_pyutils import *

__author__  = "Ian Harry <ian.harry@astro.cf.ac.uk>"
__version__ = "git id %s" % git_version.id
__date__    = git_version.date

def make_external_call(\
  command, show_stdout=False, \
  show_command=False, show_error=True):
  """
  Run a command line argument and print informative messages on failure.
  It returns two outputs: the stdout of the command, and its status.  

    >>> make_external_call('cp * /tmp', False, False, True)

  @param command: the command to try
  @type command: string
  @param show_stdout: show the stdout 
  @type show_stdout: boolean
  @param show_command: show the command
  @type show_command: boolean
  @param show_error: show the error if any
  @type show_error: boolean
  @return: the stdout and a status  


  """
  if show_command and opts.verbose is True:
    print "--- Trying this command :" + command

  stdin, out, err = os.popen3(command)
  pid, status = os.wait()
  this_output = out.read()
  if show_error & status != 0:
    print >>sys.stderr, "External call failed."
    print >>sys.stderr, "  status: %d" % status
    print >>sys.stderr, "  stdout: %s" % this_output
    print >>sys.stderr, "  stderr: %s" % err.read()
    print >>sys.stderr, "  command: %s" % command
    sys.exit(status)
  if show_stdout:
    if this_output[0:1]=='\n':
      print  this_output[1:]  #first character is \n
    else:
      print this_output

  stdin.close()
  out.close()
  err.close()
  return this_output, status


def initialize_page( title, style, script, header=None ):

  """
    A function that returns a markup.py page object with the required html
    header.
  """

  page         = markup.page(mode="strict_html")
  page._escape = False
  page.init( title=title, css=style, script=script, header=header )

  return page

def write_banner( title, text='&nbsp;' ):

  """
    Write html <title> tag into markup.page object
  """

  page = markup.page( mode="strict_html" )
  page._escape = False

  page.div( id="header" )
  page.h1()
  page.add( title )
  page.h1.close()
  page.h3()
  page.add( text )
  page.h3.close()

  page.hr( class_="short" )
  page.hr( class_="long" )

  page.div.close()

  page.div( id="container" )

  return page

def write_table( page, headers, data, cl='' ):

  """
    Write table in html
  """

  page.table( class_=cl )

  # list
  if cl=='list':
    for i in range( len( headers ) ):

      page.tr()
      page.th()
      page.add( '%s' % headers[i] )
      page.th.close()
      page.td()
      page.add( '%s' % data[i] )
      page.td.close()
      page.tr.close()

  else:
    page.tr()
    for n in headers:
      page.th()
      page.add( '%s' % n )
      page.th.close()
    page.tr.close()

    if data and not re.search( 'list',str( type( data[0] ) ) ):
      data = [data]

    for row in data:
      page.tr()
      for item in row:
        page.td()
        page.add( '%s' % item )
        page.td.close()
      page.tr.close()

  page.table.close()

  return page

def write_summary( page, ExtTrigger, ifos, skyError=None, ipn=False, ipnError=False ):

  """
    Write summary of information to markup.page object page
  """

  gps = ExtTrigger.start_time
  grbdate = datetime.datetime( *date.XLALGPSToUTC( LIGOTimeGPS(gps) )[:6] )\
                .strftime( "%B %d %Y, %H:%M:%S %ZUTC" )
  page.h3()
  page.add( 'Basic information' )
  page.h3.close()

  if ipn:
    ra = []
    dec = []
    td1 = []
    td2 = []
    td3 = []
    timedelay = {}
    deltat = []
    search_file = '../../../S5IPN_GRB%s_search_180deg.txt' % ExtTrigger.event_number_grb
    for line in open(search_file):
      ra.append(line.split()[0])
      dec.append(line.split()[1])
    th1 = [ 'GPS', 'Date', 'Error Box (sq.deg.)', 'IFOs' ]
    td1 = [ gps, grbdate, ipnError, ifos ]
    th2 = [ 'RA', 'DEC' ]
    th3 = ['Timedelays (ms)', '', '' ]
    for ra_i,dec_i in zip(ra,dec):
      td_i = [ ra_i, dec_i ]
      td2.append(td_i)
    ifo_list = [ ifos[i*2:(i*2)+2] for i in range(int(len(ifos)/2)) ]
    for j in td2:
      for p in range(0, len(ifo_list)):
        for q in range(0, len(ifo_list)):
          pairs = [ifo_list[p], ifo_list[q]]
          ifo_pairs = "".join(pairs)
          timedelay[ifo_pairs]   = antenna.timeDelay(int(gps),float(j[0]),float(j[1]), 'degree',ifo_list[p],ifo_list[q])
          timedelay[ifo_pairs]="%.4f" % timedelay[ifo_pairs]
      if ifos == 'H1H2L1':
        td3.append(['H1L1: %f' % float(timedelay['H1L1'])])
      if ifos == 'H1H2L1V1':
        td3.append(['H1L1: %f' % float(timedelay['H1L1']), 'H1V1: %f' % float(timedelay['H1V1']), 'L1V1: %f' % float(timedelay['L1V1'])])
      if ifos == 'L1V1':
        td3.append(['L1V1: %f' % float(timedelay['L1V1'])])
    page = write_table( page, th1, td1 )
    page = write_table( page, th2, td2 )
    page = write_table( page, th3, td3 )

  else:
    ra,dec = ExtTrigger.event_ra, ExtTrigger.event_dec
    if skyError:
      th = [ 'GPS', 'Date', 'RA', 'DEC', 'Sky Error', 'IFOs' ]
      td = [ gps, grbdate, ra, dec, skyError, ifos ]
    else:
      th = [ 'GPS', 'Date', 'RA', 'DEC', 'IFOs' ]
      td = [ gps, grbdate, ra, dec, ifos ]

    page = write_table( page, th, td )

  return page

def write_antenna( page, ExtTrigger, ifos, grid=False, ipn=False ):

  """
    Write antenna factors to merkup.page object page and generate John's
    detector response plot.
  """

  page.h3()
  page.add( 'Antenna factors and sky locations' )
  page.h3.close()

  th = []
  td = []
  th2 = []
  td2 = []

  if ipn:
    antenna_ifo = {}
    ra = []
    dec = []
    # FIXME: Remove hardcoding here and show this in all cases
    search_file = open('../../../S5IPN_GRB%s_search_180deg.txt' % ExtTrigger.event_number_grb)
    for line in search_file:
      ra.append(line.split()[0])
      dec.append(line.split()[1])
    for ifo in ifos:
      antenna_ifo[ifo] = []
      for k, l in zip(ra, dec):
        _, _, _, f_q = antenna.response( ExtTrigger.start_time,
                                         float(k), float(l),\
                                         0.0, 0.0, 'degree',ifo )
        antenna_ifo[ifo].append(round(f_q,3))
    dectKeys = antenna_ifo.keys()
    newList=[]

    for elements in range(len(antenna_ifo.values()[0])):
        newDict={}
        for detectors in range(len(antenna_ifo.keys())):
            newDict[dectKeys[detectors]] = antenna_ifo[ dectKeys[detectors] ][elements]
        for key in newDict.keys():
          th.append(key)
        td.append(newDict.values())        
    page = write_table( page, list(set(th)), td )
  for ifo in ifos:
    _, _, _, f_q = antenna.response( ExtTrigger.start_time,
                                     ExtTrigger.event_ra, ExtTrigger.event_dec,\
                                     0.0, 0.0, 'degree',ifo )
    th.append( ifo )
    td.append( round( f_q, 3 ) )

  cmmnd = 'projectedDetectorTensor --gps-sec %d --ra-deg %f --dec-deg %f' \
      % (ExtTrigger.start_time,ExtTrigger.event_ra, ExtTrigger.event_dec)
  for ifo in ifos:
    if ifo == 'H1':
      cmmnd += ' --display-lho'
    elif ifo == 'L1':
      cmmnd += ' --display-llo'
    elif ifo == 'V1':
      cmmnd += ' --display-virgo'
  output,status = make_external_call(cmmnd)
  page = write_table( page, th, td )

  plot = markup.page()
  p = "projtens.png"
  plot.a( href=p, title="Detector response and polarization" )
  plot.img( src=p )
  plot.a.close()
  th2 = ['Response Diagram']
  td2 = [plot() ]

    # FIXME: Add these in!!
#    plot = markup.page()
#    p = "ALL_TIMES/plots_clustered/GRB%s_search.png"\
#        % ExtTrigger.event_number_grb
#    plot.a(href=p, title="Error Box Search")
#    plot.img(src=p)
#    plot.a.close()
#    th2.append('Error Box Search')
#    td2.append(plot())

#    plot = markup.page()
#    p = "ALL_TIMES/plots_clustered/GRB%s_simulations.png"\
#        % ExtTrigger.event_number_grb
#    plot.a(href=p, title="Error Box Simulations")
#    plot.img(src=p)
#    plot.a.close()
#    th2.append('Error Box Simulations')
#    td2.append(plot())

  plot = markup.page()
  p = "ALL_TIMES/plots_clustered/GRB%s_sky_grid.png"\
      % ExtTrigger.event_number_grb
  plot.a(href=p, title="Sky Grid")
  plot.img(src=p)
  plot.a.close()
  th2.append('Sky Grid')
  td2.append(plot())

  plot = markup.page()
  p = "GRB%s_inspiral_horizon_distance.png"\
      % ExtTrigger.event_number_grb
  plot.a(href=p, title="Inspiral Horizon Distance")
  plot.img(src=p)
  plot.a.close()
  th2.append('Inspiral Horizon Distance')
  td2.append(plot())

  page = write_table( page, th2, td2 )

  return page

def write_offsource( page, ifos, grbtag, onsource=False ):

  """
    Write offsource SNR versus time plots to markup.page object page
  """

  th = ['BestNR 8', 'Coherent SNR']

  if onsource:
    dir = 'ALL_TIMES'
  else:
    dir = 'OFFSOURCE'

  plot = markup.page()
  p = "%s/plots_clustered/GRB%s_bestnr_vs_time_noinj.png" % ( dir, grbtag )
  plot.a( href=p, title="Coherent SNR versus time" )
  plot.img( src=p )
  plot.a.close()
  td = [ plot() ]

  plot = markup.page()
  p = "%s/plots/GRB%s_triggers_vs_time_noinj.png" % ( dir, grbtag )
  plot.a( href=p, title="Coherent SNR versus time" )
  plot.img( src=p )
  plot.a.close()
  td.append( plot() )

  for ifo in ifos:
    th.append('%s SNR' % ifo )
    plot = markup.page()
    p = "%s/plots/GRB%s_%s_triggers_vs_time_noinj.png" % ( dir, grbtag, ifo )
    plot.a( href=p, title="%s SNR versus time" % ifo )
    plot.img( src=p )
    plot.a.close()
    td.append( plot() )

  page = write_table( page, th, td )

  return page

def write_chisq( page, injList, grbtag ):

  """
    Write injection chisq plots to markup.page object page
  """

  if injList:
    th = ['']+injList + ['OFFSOURCE']
  else:
    th= ['','OFFSOURCE']
    injList = ['OFFSOURCE']
  td = []

  plots = ['bank_veto','auto_veto','chi_square', 'mchirp']

  for test in plots:
    pTag = test.replace('_',' ').title()
    d = [pTag]
    for inj in injList + ['OFFSOURCE']:
      plot = markup.page()
      p = "%s/plots_clustered/GRB%s_%s_vs_snr_zoom.png" % ( inj, grbtag, test )
      plot.a( href=p, title="%s %s versus SNR" % ( inj, pTag ) )
      plot.img( src=p )
      plot.a.close()
      d.append( plot() )
 
    td.append(d)

  page = write_table( page, th, td )

  return page

def write_inj_snrs( page, ifos, injList, grbtag ):

  """
    Write injection chisq plots to markup.page object page
  """

  if injList:
    th = ['']+injList + ['OFFSOURCE']
  else:
    th= ['','OFFSOURCE']
    injList = ['OFFSOURCE']
  td = []

  plots = ['null_stat2']+['%s_snr' % ifo for ifo in ifos]

  for row in plots:
    pTag = row.replace('_',' ').title()
    d = [pTag]
    for inj in injList + ['OFFSOURCE']:
      plot = markup.page()
      p = "%s/plots_clustered/GRB%s_%s_vs_snr_zoom.png" % ( inj, grbtag, row )
      plot.a( href=p, title="%s %s versus SNR" % ( inj, pTag ) )
      plot.img( src=p )
      plot.a.close()
      d.append( plot() )
    td.append(d)

  page = write_table( page, th, td )

  return page

def write_found_missed( page, ifos, injList ):

  """
    Write injection found/missed plots to markup.page object page
  """

  th = ['']+injList
  td = []

  d = ['Number of injections']
  for inj in injList:
    cmmnd = 'lwtprint ../*' + inj + '*MISSED*xml -t sim_inspiral | wc -l'
    output,status = make_external_call(cmmnd)
    numInjs = int(output)
    cmmnd = 'lwtprint ../*' + inj + '*FOUND*xml -t sim_inspiral | wc -l'
    output,status = make_external_call(cmmnd)
    numInjs += int(output)
    d.append(str(numInjs))
  td.append(d)

  plots = []
  text  = {}
  for ifo in ifos:
    plots.extend(['effdist_%s' % ifo[0].lower(),\
                  'effdist_time_%s' % ifo[0].lower()])
    text['effdist_%s' % ifo[0].lower()]      = 'Eff. dist. %s vs Mchirp' % ifo
    text['effdist_time_%s' % ifo[0].lower()] = 'Eff. dist %s vs Time' % ifo

  for row in plots:
    pTag = text[row]
    d = [pTag]
    for inj in injList:
      plot = markup.page()
      p = "%s/efficiency_OFFTRIAL_1/found_missed_injections_%s.png" % ( inj, row )
      plot.a( href=p, title=pTag )
      plot.img( src=p )
      plot.a.close()
      d.append( plot() )
    td.append(d)

  td.append(['Close injections without FAP = 0']+\
            ['<a href="%s/efficiency_OFFTRIAL_1/quiet_found_triggers.html">here</a>' % inj\
            for inj in injList])

  page = write_table( page, th, td )

  return page

def write_recovery( page, injList ):

  """
    Write injection recovery plots to markup.page object page
  """

  th = ['']+injList
  td = []

  plots = ['sky_error_time','sky_error_mchirp','sky_error_distance']
  text = { 'sky_error_time':'Sky error vs time',\
           'sky_error_mchirp':'Sky error vs mchirp',\
           'sky_error_distance':'Sky error vs distance' }

  for row in plots:
    pTag = text[row]
    d = [pTag]
    for inj in injList:
      plot = markup.page()
      plot = markup.page()
      p = "%s/efficiency_OFFTRIAL_1/found_%s.png" % ( inj, row )
      plot.a( href=p, title=pTag )
      plot.img( src=p )
      plot.a.close()
      d.append( plot() )
    td.append(d)

  page = write_table( page, th, td )

  return page

def write_loudest_events( page, bins, onsource=False ):

  """
    Write injection chisq plots to markup.page object page
  """

  th = ['']+['Mchirp %s - %s' % tuple(bin) for bin in bins]
  td = []

  plots = ['BestNR','SNR']

  if onsource:
    trial = 'ONSOURCE'
  else:
    trial = 'OFFTRIAL_1'

  for pTag in plots:
    row = pTag.lower()
    d = [pTag]
    for bin in bins:
      b = '%s_%s' % tuple(bin)
      plot = markup.page()
      p = "%s/efficiency/%s_vs_fap_%s.png" % ( trial, row, b )
      plot.a( href=p, title="FAP versus %s" % pTag )
      plot.img( src=p )
      plot.a.close()
      d.append( plot() )
    td.append(d)

  row = 'snruncut'
  d = ['SNR after cuts <br> have been applied']
  for bin in bins:
    b = '%s_%s' % tuple(bin)
    plot = markup.page()
    p = "%s/efficiency/%s_vs_fap_%s.png" % ( trial, row, b )
    plot.a( href=p, title="FAP versus %s" % pTag )
    plot.img( src=p )
    plot.a.close()
    d.append( plot() )
  td.append(d)

  page = write_table( page, th, td )

  page.add( 'For more details on the loudest offsource events see')
  page.a( href='%s/efficiency/loudest_offsource_trigs.html' % (trial))
  page.add('here.')
  page.a.close()


  return page

def write_exclusion_distances(page , trial, injList, massbins, reduced=False, onsource=False):
  file = open( '%s/efficiency/loud_numbers.txt' % (trial), 'r' )
  FAPS = []
  for line in file:
    line = line.replace('\n','')
    if float(line) == -2:
      FAPS.append('No event')
    else:
      FAPS.append(float(line))

  file.close()

  th = ['']+['Mchirp %s - %s' % tuple(bin) for bin in massbins]
  td = ['FAP']+FAPS
  page = write_table( page, th, td )
  page.add( 'For more details on the loudest onsource events see')
  page.a( href='%s/efficiency/loudest_events.html' % (trial) )
  page.add('here.')
  page.a.close()

  if reduced or not injList:
    return page  

  page.h3()
  page.add('Detection efficiency plots - injections louder than loudest background trigger')
  page.h3.close()

  th = injList
  td = []
  d = []
  for inj in injList:
    plot = markup.page()
    p = "%s/efficiency_%s/BestNR_max_efficiency.png" % ( inj,trial )
    plot.a( href=p, title="Detection efficiency" )
    plot.img( src=p )
    plot.a.close()
    d.append( plot() )
  td.append(d)

  page = write_table( page, th, td )

  page.h3()
  page.add('Exclusion distance plots - injections louder than loudest foreground trigger')
  page.h3.close()

  th = injList
  td = []
  d = []
  for inj in injList:
    plot = markup.page()
    p = "%s/efficiency_%s/BestNR_on_efficiency.png" % ( inj,trial )
    plot.a( href=p, title="Exclusion efficiency" )
    plot.img( src=p )
    plot.a.close()
    d.append( plot() )
  td.append(d)

  page = write_table( page, th, td )

  page.h3()
  page.add('90% confidence exclusion distances (Mpc)')
  th = injList
  td = []
  d = []
  for inj in injList:
    file = open( '%s/efficiency_%s/exclusion_distance.txt' % (inj,trial), 'r' )
    for line in file:
      line = line.replace('\n','')
      excl_dist = float(line)
    d.append(excl_dist)
    file.close()
  td.append(d)

  page = write_table( page, th, td )

  page.h3.close()

  return page
  



# =============================================================================
# Parse command line
# =============================================================================

def parse_command_line():

  usage = """usage: %prog [options] 
  
coh_PTF_injfinder will find and record found and missed injections for the given injection run

--grb-name
--ifo-tag
--num-files
--inj-name
--time-window
"""

  parser = OptionParser( usage, version=__version__)

  parser.add_option( "-g", "--grb-xml", action="store", type="string",\
                     default=None, help="location of the GRB xml file" )

  parser.add_option( "-f", "--config-file", action="store", type="string",\
                     default=None, help="location of the run ini file" )

  parser.add_option( "-i", "--ifo-tag", action="store", type="string",\
                     default=None,\
                     help="The ifo tag, H1L1 or H1L1V1 for instance" )

  parser.add_option( "-o", "--output-path", action="store", type="string",\
                     default=os.getcwd(), help="output directory, "+\
                                               "default: %default" )
  parser.add_option( "-D", "--exclusion-injections", action="store",\
                     type="string", default=None,\
                     help="A comma seperated list of the detection injection"+\
                          "run names." )

  parser.add_option( "-T", "--tuning-injections", action="store",\
                     type="string",default=None,\
                     help="A comma seperated list of the tuning injection"+\
                          "run names.")

  parser.add_option( "-O", "--open-box", action="store_true", default=False,
                     dest="open_box", help ="Use to show onsource results" )

  parser.add_option( "-G", "--sky-grid", action="store_true", default=False,\
                     help="Include sky grid information, default: %default" )

  parser.add_option("-M", "--mass-bins", action="store", type="string",\
                     default="0-3.48,3.48-6,6-20",\
                     help="comma separated list of dash-separated pairs "\
                          "of m_low-m_high mass bins, default: %default")

  parser.add_option( "", "--ipn", action="store_true", default=False,\
                     help="Include ipn information on extra to Fermi case, default: %default" )
  parser.add_option( "", "--ipn-error", action="store", type="float",\
                     default=None, help="size of the IPN error box" )

  (opts,args) = parser.parse_args()

  if not opts.grb_xml:
    parser.error( "must provide --grb-xml" )

  if not opts.config_file:
    parser.error( "must provide --config-file" )

  if not opts.ifo_tag:
    parser.error( "must provide --ifo-tag" )

  (opts,args) = parser.parse_args()

  return opts, args

# =============================================================================
# Main function
# =============================================================================

def main( grbxml, inifile, outdir, ifoTag, detectionInjList=[],\
          tuningInjList=[], open_box=False, grid=False, ipn=False,\
          massBins = [('0','8.0')] ):

  # get entry information  
  ifos     = [ ifoTag[i*2:(i*2)+2] for i in range(int(len(ifoTag)/2)) ]
  extTrigs = grbsummary.load_external_triggers( grbxml )

  if len( extTrigs )>1:
    raise ValueError, 'Found more than one GRB in %s.' % grbfile

  grb = extTrigs[0]
  GRBnum = extTrigs[0].event_number_grb
  os.chdir( outdir )

  # get sections
#  injList = glob.glob('injections*')
  
  trials  = sorted(glob.glob('OFFTRIAL_*'))

  # initialise page
  title   = 'Coherent PTF Inspiral search of GRB%s' % GRBnum
  if open_box:
    title += '- OPEN BOX PAGE'
  else:
    title += '- CLOSED BOX PAGE'
  banner  = write_banner( title )
  js = 'coh_PTF_html_summary.js'
  script = {js:'javascript'}
  css = 'coh_PTF_html_summary.css'
  webpage = initialize_page( title, css, script, header=banner() )
#  if open_box:
#    webpage.add('<img src="https://www.lsc-group.phys.uwm.edu/ligovirgo/cbc/public/segments/S5/OpenBox.jpg">')
#  else:
#    webpage.add('<img src="https://www.lsc-group.phys.uwm.edu/ligovirgo/cbc/public/segments/S5/thomasLegacy.jpg">')
  
  # set divs
  webpage.div( id="maintab" )
  webpage.div( id="content" )

  # write summary section
  secname = 'Summary information'
  i = 0
  webpage.input( id="input_%s" % i, type="button", class_="h2",\
                 onclick="toggleVisible('%s');" % i, value=secname )
  webpage.div( id="div_%s" % i, style="display: none;" )

  # get sky error from config
  if grid:
    cp = ConfigParser.ConfigParser()
    cp.read(inifile)
    err = cp.get('coh_PTF_inspiral', 'sky-error') 
  else:
    err = None
  if opts.ipn:
    ipnerr = opts.ipn_error
    webpage = write_summary( webpage, grb, ifoTag, None, opts.ipn, ipnerr )
    webpage = write_antenna( webpage, grb, ifos, None, opts.ipn )
  else:
    webpage = write_summary( webpage, grb, ifoTag, err, None, None )
    webpage = write_antenna( webpage, grb, ifos, grid, None )

  # add links
  # ownership of the domain changed, grbsumlink is not provided
  # grbsumlink = 'http://grblog.org/grblog.php?view=burst&GRB=%s' % GRBnum
  # plot_segments_grb%s.png changed to segment_plot_%s.png 
  #seglink    = 'plot_segments_grb%s.png' % GRBnum
  seglink    = 'segment_plot_%s.png' % GRBnum
  inilink    = os.path.basename(inifile)
  #linktext = { grbsumlink:'Summary of this GRB',\
  #             seglink:   'Segments availability',\
  #             inilink:   'ini-file used'}
  linktext = { seglink:   'Segments availability',\
               inilink:   'ini-file used'}
  #for link in [ grbsumlink, seglink, inilink ]:
  for link in [ seglink, inilink ]:
    webpage.add( linktext[link] )
    webpage.a( href=link )
    webpage.add( 'here' )
    webpage.a.close()
    webpage.br()

  webpage.div.close()

  # copy over these files
  ## plot segments is generated by dataquery
  segplot = '%s/%s/%s'\
            % ( os.path.split(grbxml)[0], os.path.pardir, seglink )
  if os.path.isfile(segplot):
    shutil.copy( segplot, outdir ) 
  else:
    print >>sys.stderr, 'WARNING: Cannot find segments availability plot.'
  ## inifile is simply copied
  if not os.path.isfile( '%s/%s' % (outdir, os.path.basename(inifile)) ) or\
     not os.path.samefile( inifile, '%s/%s'\
                                    % ( outdir, os.path.basename(inifile) ) ):
    shutil.copy( inifile, outdir )
  

  # add off source trig plots
  secname = 'Offsource triggers versus time'
  i += 1
  webpage.input( id="input_%s" % i, type="button", class_="h2",\
                 onclick="toggleVisible('%s');" % i, value=secname )
  webpage.div( id="div_%s" % i, style="display: none;" )

  webpage = write_offsource( webpage, ifos, GRBnum )

  webpage.div.close()

  # add signal consistency plots
  secname = 'Signal consistency plots'
  i += 1
  webpage.input( id="input_%s" % i, type="button", class_="h2",\
                 onclick="toggleVisible('%s');" % i, value=secname )
  webpage.div( id="div_%s" % i, style="display: none;" )

  webpage.h3()
  webpage.add('Chi squared tests')
  webpage.h3.close()
  webpage.h4()
  webpage.add('Plot key')
  webpage.h4.close()
  webpage.add('Blue crosses: Background triggers')
  webpage.br()
  webpage.add('Red crosses: Injections triggers')
  webpage.br()
  webpage.add('Black line: Veto line, shaded region indicates vetoed area')
  webpage.br()
  webpage.add('Yellow lines: Contours of new SNR')
  webpage = write_chisq( webpage, tuningInjList, GRBnum )
  webpage.h3()
  webpage.add('Individual detector and null SNRs')
  webpage.h3.close()
  webpage.h4()
  webpage.add('Plot key')
  webpage.h4.close()
  webpage.add('Blue crosses: Background triggers')
  webpage.br()
  webpage.add('Red crosses: Injections triggers')
  webpage.br()
  webpage.add('Black line: Veto line, shaded region indicates vetoed area')
  webpage.h5()
  webpage.add('For the null stat plot')
  webpage.h5.close()
  webpage.add('Green line: Above this triggers have reduced detection statistic')
  webpage.br()
  webpage.add('Magenta line: On this line the statistic is reduced by a factor of two')
  webpage.h5()
  webpage.add('For the single detector plots')
  webpage.h5.close()
  webpage.add('The cut is only applied to the two most sensitive detectors, this can vary with mass and sky location, which is shown on the plots')
  webpage.br()
  webpage.add('Green line: This indicates the expected SNR for optimally oriented injections')
  webpage.br()
  webpage.add('Magenta and cyan lines: Show 1 and 2 sigma errors on the green line')
  webpage = write_inj_snrs( webpage, ifos, tuningInjList, GRBnum )

  webpage.div.close()

  # add found/missed plots
  if detectionInjList:
    secname = 'Found/missed injections'
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%d');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )

    webpage.h3()
    webpage.add('A key for these plots')
    webpage.h3.close()
    webpage.add('Black cross indicates no trigger was found coincident with '+\
              'the injection')
    webpage.br()
    webpage.add('Red cross indicates a trigger was found coincident with the '+\
              'injection but it was vetoed')
    webpage.br()
    webpage.add('Green cross indicates that a trigger was found coincident '+\
              'with the injection and it was louder than all events in the '+\
              'offsource')
    webpage.br()
    webpage.add('Coloured circle indicates that a '+\
              'trigger was found coincident '+\
              'with the injection but it was not louder than all offsource '+\
              'events. The colour bar gives the FAP of the trigger.')

    webpage = write_found_missed( webpage, ifos, detectionInjList )

    webpage.div.close()

  if tuningInjList and grid:
    # add injection recovery plots
    secname = 'Injection recovery'
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%d');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )
    webpage = write_recovery( webpage, detectionInjList )
    webpage.div.close()

  if tuningInjList and ipn:
    # add injection recovery plots
    secname = 'Injection recovery'
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%d');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )
    webpage = write_recovery( webpage, detectionInjList )
    webpage.div.close()

  # add loudest off source events
  secname = 'Loudest offsource events'
  i += 1
  webpage.input( id="input_%s" % i, type="button", class_="h2",\
                 onclick="toggleVisible('%s');" % i, value=secname )
  webpage.div( id="div_%s" % i, style="display: none;" )

  webpage = write_loudest_events( webpage, massBins )

  webpage.div.close()

  # write trial results
  for ii,trial in zip(range(len(trials)),trials):
    reduced = False
    if ii > 1:
      reduced = True
    secname = 'Results for %s' % trial.replace('_',' ').title()
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%s');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )
    webpage = write_exclusion_distances(webpage,trial,detectionInjList,massBins,reduced=reduced,onsource=False)
    webpage.div.close()

  if open_box:

    trial = 'ONSOURCE'

    webpage.hr( class_="long", style="margin-top: 20px;" )

    # add on source trig plots
    secname = 'Full data triggers versus time'
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%s');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )
  
    webpage = write_offsource( webpage, ifos, GRBnum, onsource=True )
    webpage.div.close()
  
    # write on source events
    trial = 'ONSOURCE'
    secname = 'Results for %s' % trial.replace('_',' ').title()
    i += 1
    webpage.input( id="input_%s" % i, type="button", class_="h2",\
                   onclick="toggleVisible('%s');" % i, value=secname )
    webpage.div( id="div_%s" % i, style="display: none;" )
    webpage = write_exclusion_distances(webpage,trial,detectionInjList,\
              massBins,reduced=False,onsource=False)
    webpage.div.close()

  webpage.div.close()
  webpage.div.close()
  webpage.div.close()
  webpage.body.close()
  webpage.html.close()

  file = open( '%s/summary.html' % outdir, 'w' )
  file.write( webpage() )
  file.close()
  
if __name__=='__main__':

  opts, args = parse_command_line()

  grbxml     = os.path.abspath( opts.grb_xml )
  inifile    = os.path.abspath( opts.config_file )
  outdir     = os.path.abspath( opts.output_path )
  ifoTag     = opts.ifo_tag
  grid       = opts.sky_grid
  ipn        = opts.ipn
  massBins = map(lambda p: map(float, p.split('-')), opts.mass_bins.split(','))
  detInjsList = []
  tunInjsList = []
  if opts.exclusion_injections:
    detInjsList = opts.exclusion_injections.split(",")
  if opts.tuning_injections:
    tunInjsList = opts.tuning_injections.split(",")
  
  
  main( grbxml, inifile, outdir, ifoTag, detectionInjList=detInjsList,\
        tuningInjList=tunInjsList, open_box=opts.open_box, grid=grid, ipn=ipn,\
        massBins = massBins )

