#!/usr/bin/python3
#
# Copyright (C) 2010 Chad Hanna
#
# 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 2 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.


from optparse import OptionParser
import sqlite3
import sys

import ligolw
from ligolw import __date__, __version__
import ligolw.dbtables
from ligolw.utils import ligolw_sqlite


__author__ = "Chad Hanna <channa@ligo.caltech.edu>"


def parse_command_line():
	parser = OptionParser(
		version = "Name: %%prog\n%s" % __version__, usage = "%prog (--sql CODE|--sql-file FILENAME) [options] database1.sqlite database2.xml ..."
	)
	parser.add_option("-t", "--tmp-space", metavar = "path", help = "Path to a directory suitable for use as a work area while manipulating the database file.  The database file will be worked on in this directory, and then moved to the final location when complete.  This option is intended to improve performance when running in a networked environment, where there might be a local disk with higher bandwidth than is available to the filesystem on which the final output will reside.")
	parser.add_option("-c", "--sql", metavar = "code", help = "Execute this SQL code.")
	parser.add_option("-s", "--sql-file", metavar = "filename", help = "Execute this SQL file.")
	parser.add_option("-v", "--verbose", action = "store_true", help = "Be verbose.")
	options, filenames = parser.parse_args()
	if bool(options.sql) + bool(options.sql_file) != 1:
		raise ValueError("must set exactly one of --sql or --sql-file")
	return options, (filenames or [])


options, databases = parse_command_line()
if options.verbose:
	ligolw.set_verbose(True, application = "ligolw_run_sqlite")


if options.sql_file:
	# Parse the sql file into something that can be executed in sequence
	sql = [line.strip() for line in open(options.sql_file)]
	# Remove comments and pragmas
	sql = [s for s in sql if not s.startswith("--") and not s.startswith("PRAGMA")]
	sql = "\n".join(sql)
	sql = [statement.strip() for statement in sql.split(";\n")]
elif options.sql:
	sql = [statement.strip() for statement in options.sql.split(";")]
else:
	raise NotImplemented
# remove no-ops
sql = [statement for statement in sql if statement]


#
# how to apply SQL to a database
#


def apply_sql(connection, sql):
	ligolw.logger.info("Executing SQL ...")
	cursor = connection.cursor()
	for statement in sql:
		ligolw.logger.info(statement)
		cursor.execute(statement)
		connection.commit()
	cursor.close()
	ligolw.logger.info("... Done.")


#
# Apply the SQL to all the databases sequentially
#


for n, filename in enumerate(databases, 1):
	#
	# access the document
	#

	ligolw.logger.info("file %d/%d ..." % (n, len(databases)))

	if filename.endswith(".xml") or filename.endswith(".xml.gz"):
		# load XML file into in-ram database for processing
		with sqlite3.connect(":memory:") as connection:
			ligolw.dbtables.ContentHandler.connection = connection
			ligolw_sqlite.insert_from_url(filename, contenthandler = ligolw.dbtables.ContentHandler, preserve_ids = True)

			# apply SQL
			apply_sql(connection, sql)

			# overwrite XML file with in-ram database' contents
			ligolw_sqlite.extract(connection, filename)
	else:
		# assume it's an SQLite file
		with ligolw.dbtables.workingcopy(filename, tmp_path = options.tmp_space) as workingcopy:
			with sqlite3.connect(str(workingcopy)) as connection:
				# disable sync() system calls for the
				# database
				connection.execute("PRAGMA synchronous = OFF;")
				# also use the scratch space for sqlite's
				# temp store
				workingcopy.set_temp_store_directory(connection)

				# apply SQL
				apply_sql(connection, sql)
