#!/usr/bin/env python

"""EIS timeline web page generator turned into a PyQt4/5 gui.

(2015-Apr-15): JTM intitial test version into git.

Requires:

(1) An up-to-date SSW Hinode installation to access the sqlite
    database version of the EIS timeline, or a copy of the file
    timeline_db.sqlite.

(2) A reasonably complete python 2.7 or 3.4 installation, such as
    Anaconda. In particular, both sqlite3 and PyQt4 or PyQt5 must
    be installed as part of the python installaion.

Has been tested on OS X and Linux with Anaconda python. Windows
should work as well.

"""

from __future__ import absolute_import, print_function, unicode_literals
import sys
import os
import time
import re
import sqlite3
import datetime
import pkgutil

if pkgutil.find_loader('PyQt4'):
    from PyQt4 import QtCore
    from PyQt4 import QtGui as QtWidgets
elif pkgutil.find_loader('PyQt5'):
    from PyQt5 import QtCore, QtWidgets
else:
    print('Either PyQt4 or PyQt5 must be installed.')
    sys.exit()


class Top(QtWidgets.QWidget):

    def __init__(self, dbfile, parent=None):
        super(Top, self).__init__(parent)

        self.dbfile = dbfile

        self.init_ui()
        
    def init_ui(self):
        """Manage everyting."""
        self.grid = QtWidgets.QGridLayout(self)

        # Info at the top
        self.top()

        # Left top panel -- select by date
        self.left_panel()

        # Center top panel -- select by study id
        self.center_panel()

        # Right top panel -- select by study acronym
        self.right_panel()

        # Middle row -- grab last 30 entries
        self.middle_row()

        # Bottom row -- where it all comes out
        self.bottom_row()

        # And away we go
        self.setLayout(self.grid)
        
        self.setGeometry(200, 200, 1000, 800)
        self.setWindowTitle('EIS Timeline Information')
        self.show()

    def top(self):
        """Some info at the top."""
        title = QtWidgets.QLabel(self)
        if os.path.isfile(self.dbfile):
            title.setText('Using file: ' + os.path.abspath(self.dbfile))
            self.dblines = DB(self.dbfile)
        else:
            title.setText('Unable to locate file: ' +
                          self.dbfile)
        title.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeft)
#        title2 = QtGui.QLabel(self)
#        title2.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignLeft)
#        text = 'Or select a different SQLITE file.'
#        title2.setText(text)
#        sub_file = QtGui.QPushButton('OK', self)
#        sub_file.setFixedWidth(50)
#        sub_file.clicked.connect(self.event_file)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addWidget(title)
#        hbox.addWidget(title2)
#        hbox.addWidget(sub_file)
        hbox.addStretch(1)
        self.grid.addLayout(hbox, 0, 0, 1, 8)

    def event_file(self):
        """Select a different SQLITE file."""
        self.dbfile = QtWidgets.QFileDialog.getOpenFileName(self,
                                                        'Select SQLITE file',
                                                        './')
        self.dbfile = str(self.dbfile)
        self.dblines = DB(self.dbfile)

    def left_panel(self):
        """Select entries by date."""
        
        title = QtWidgets.QLabel(self)
        title.setText('Select by Date')
        title.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        year_t = QtWidgets.QLabel(self)
        year_t.setText('Year (yyyy)')
        mon_t = QtWidgets.QLabel(self)
        mon_t.setText('Month (mm)')
        day_t = QtWidgets.QLabel(self)
        day_t.setText('Day (dd)')
        sub_date = QtWidgets.QPushButton('Submit', self)
        sub_date.setFixedWidth(100)
        sub_date.clicked.connect(self.event_dateb)

        self.y = QtWidgets.QLineEdit(self)
        self.y.setFixedWidth(100)
        self.m = QtWidgets.QLineEdit(self)
        self.m.setFixedWidth(100)
        self.d = QtWidgets.QLineEdit(self)
        self.d.setFixedWidth(100)

        self.grid.addWidget(title, 1, 0, 1, 2)
        self.grid.addWidget(year_t, 2, 0)
        self.grid.addWidget(mon_t, 3, 0)
        self.grid.addWidget(day_t, 4, 0)
        self.grid.addWidget(sub_date, 5, 0, 1, 2)
        self.grid.addWidget(self.y, 2, 1)
        self.grid.addWidget(self.m, 3, 1)
        self.grid.addWidget(self.d, 4, 1)

    def event_dateb(self):
        """Process date selection."""
        self.year = self.y.text()
        self.month = self.m.text()
        self.day = self.d.text()
        if self.year == '' or self.month == '' or self.day == '':
            print('* * * Invalid date values entered * * *')
        else:
            info = self.dblines.get_by_date(int(self.year),
                                       int(self.month), int(self.day))
            info.sort(key=lambda r: r[0])
            self.mk_table(info)

    def center_panel(self):
        """Select by study id."""
        title = QtWidgets.QLabel(self)
        title.setText('Select by Study ID')
        title.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        study_t = QtWidgets.QLabel(self)
        study_t.setText('Study ID')
        sub_id = QtWidgets.QPushButton('Submit', self)
        sub_id.setFixedWidth(100)
        sub_id.clicked.connect(self.event_studyb)
        self.study = QtWidgets.QLineEdit(self)
        self.study.setFixedWidth(100)
        self.study.returnPressed.connect(self.event_study)
                
        self.grid.addWidget(title, 1, 3, 1, 2)
        self.grid.addWidget(study_t, 2, 3)
        self.grid.addWidget(sub_id, 3, 3, 1, 2)
        self.grid.addWidget(self.study, 2, 4)

    def event_study(self):
        """Process study id selection enter."""
        study = self.study.text()
        if study == '':
            print('* * * No study ID entered * * *')
        else:
            info = self.dblines.get_by_study_id(int(study))
            info.sort(key=lambda r: r[0])
            self.mk_table(info)

    def event_studyb(self):
        """Process study id selection."""
        study = self.study.text()
        if study == '':
            print('* * * No study ID entered * * *')
        else:
            info = self.dblines.get_by_study_id(int(study))
            info.sort(key=lambda r: r[0])
            self.mk_table(info)
            
    def right_panel(self):
        """Select by study acronym."""
        title = QtWidgets.QLabel(self)
        title.setText('Select by Acronym (first few characters will do)')
        title.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        acro_t = QtWidgets.QLabel(self)
        acro_t.setText('Study Acronym')
        sub_acro = QtWidgets.QPushButton('Submit', self)
        sub_acro.setFixedWidth(100)
        sub_acro.clicked.connect(self.event_acrob)
        self.acronym = QtWidgets.QLineEdit(self)
        self.acronym.returnPressed.connect(self.event_acro)
                
        self.grid.addWidget(title, 1, 6, 1, 2)
        self.grid.addWidget(acro_t, 2, 6)
        self.grid.addWidget(sub_acro, 3, 6, 1, 2)
        self.grid.addWidget(self.acronym, 2, 7)
        
    def event_acro(self):
        """Process study acronym selection enter."""
        acro = self.acronym.text()
        info = self.dblines.get_by_acronym(str(acro))
        info.sort(key=lambda r: r[0])
        self.mk_table(info)
        
    def event_acrob(self):
        """Process study acronym selection."""
        acro = self.acronym.text()
        info = self.dblines.get_by_acronym(str(acro))
        info.sort(key=lambda r: r[0])
        self.mk_table(info)

    def middle_row(self):
        """Select last 30 or user entered quantity entries."""
        title = QtWidgets.QLabel(self)
        title.setText('or')
        title.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        last_t = QtWidgets.QLabel(self)
        last_t.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        text = 'I want to know what EIS is doing now. '
        text += 'Just give me that last'
        last_t.setText(text)
        self.num_entries = QtWidgets.QLineEdit(self)
        self.num_entries.setFixedWidth(50)
        self.num_entries.returnPressed.connect(self.event_last)
        self.num_entries.setText('30')
        last_t_end = QtWidgets.QLabel(self)
        last_t_end.setAlignment(QtCore.Qt.AlignBottom|QtCore.Qt.AlignCenter)
        last_t_end.setText('entries:')
        
        sub_last = QtWidgets.QPushButton('OK', self)
        sub_last.setFixedWidth(50)
        sub_last.clicked.connect(self.event_last)

        hbox = QtWidgets.QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(last_t)
        hbox.addWidget(self.num_entries)
        hbox.addWidget(last_t_end)
        hbox.addWidget(sub_last)
        hbox.addStretch(1)

        self.grid.addWidget(title, 6, 0, 1, 8)
        self.grid.addLayout(hbox, 7, 0, 1, 8)

    def event_last(self):
        """Process last entries."""
        nentries = self.num_entries.text()
        info = self.dblines.tail(int(nentries))
        info.sort(key=lambda r: r[0])
        self.mk_table(info)

    def bottom_row(self):
        """Display results as table."""
        headers = ['Sequence', 'Date', 'Start Time', 'Align', 'Xcen',
                   'Ycen', 'Study ID', 'Study Acronym',
                   'Brief Description']
        widths = [60, 100, 70, 40, 70, 70, 50, 180, 350]

        self.table = QtWidgets.QTableWidget(self)
        self.table.setRowCount(1)
        self.table.setColumnCount(len(headers))
        self.table.setItem(0, 0, QtWidgets.QTableWidgetItem(' '))
        self.table.setHorizontalHeaderLabels(headers)
        for i in range(len(headers)):
            self.table.setColumnWidth(i, widths[i])
        #self.table.verticalHeader().setVisible(False)
        self.grid.addWidget(self.table, 8, 0, 1, 8)

    def mk_table(self, info):
        """Add entries to the results table."""
        len_info = len(info)
        num_cols = len(info[0])
        self.table.clearContents()
        self.table.setRowCount(len_info)
        for row in range(len_info):
            # Sequence
            item = QtWidgets.QTableWidgetItem(str(info[row][1]))
            self.table.setItem(row, 0, item)
            # Date and start time
            date = time.strftime("%Y-%b-%d", info[row][0])
            item = QtWidgets.QTableWidgetItem(date)
            self.table.setItem(row, 1, item)
            hms = time.strftime("%H:%M:%S", info[row][0])
            item = QtWidgets.QTableWidgetItem(hms)
            self.table.setItem(row, 2, item)
            # Alignment info
            item = QtWidgets.QTableWidgetItem(str(info[row][5]))
            self.table.setItem(row, 3, item)
            # Xcen and Ycen
            fstring = '{:0.1f}'.format(info[row][2])
            item = QtWidgets.QTableWidgetItem(fstring)
            self.table.setItem(row, 4, item)
            fstring = '{:0.1f}'.format(info[row][3])
            item = QtWidgets.QTableWidgetItem(fstring)
            self.table.setItem(row, 5, item)
            # Study ID
            item = QtWidgets.QTableWidgetItem(str(info[row][4]))            
            self.table.setItem(row, 6, item)
            # Acronym
            item = QtWidgets.QTableWidgetItem(info[row][6])            
            self.table.setItem(row, 7, item)
            # Description
            item = QtWidgets.QTableWidgetItem(info[row][7])            
            self.table.setItem(row, 8, item)


def regexp(expr, item):
    """
    Return 1 if `item` matches the regular expression `expr`, 
    0 otherwise. Needed for matching study names.
    """
    r = re.compile(expr, re.IGNORECASE)
    return r.match(item) is not None


class DB(object):
    """Connect to the timeline sqlite database file provide
    information in respose to requests from the gui.
    """
    
    def __init__(self, dbfile):
        self.intail = False
        self.conn = sqlite3.connect(dbfile)
        self.conn.create_function("regexp", 2, regexp) # so we can use REGEX
        self.cur = self.conn.cursor()

    def mk_list(self):
        r = self.cur.fetchall()
        info = []
        for i in range(len(r)):
            date = datetime.datetime(r[i][1], r[i][2],
                                     r[i][3], r[i][4],
                                     r[i][5], r[i][6])
            info.append([date.timetuple(),
                         r[i][0], r[i][7], r[i][8],
                         r[i][9], r[i][10], r[i][11], r[i][12]])

        if self.intail:
            info = info[len(r)-self.nlines:]
        self.intail = False
        return info

    def tail(self, nlines):
        """Increase nlines to account for random placement of studies
        by the planner, then reduce back to requested number.
        """
        self.intail = True
        self.nlines = nlines
        self.cur.execute("""select * from (select * from timeline
            order by rowid desc limit ?) order by year, month, day,
            hour, minute""", (nlines+50,))
        return self.mk_list()

    def get_by_date(self, year, month, day):
        self.cur.execute("""select * from timeline
            where year is ? and month is ? and day is ?
            order by year, month, day, hour, minute""",
                    (year, month, day))
        return self.mk_list()

    def get_by_study_id(self, study_id):
        self.cur.execute("""select * from timeline
            where study_id is ? order by year, month, day,
            hour, minute""", (study_id,))
        return self.mk_list()

    def get_by_acronym(self, study_acronym):
        self.cur.execute("""select * from timeline
            where study regexp ? order by year, month, day, hour,
            minute""", (study_acronym.strip(),))
        return self.mk_list()

        
# # # MAIN # # #

if len(sys.argv) == 2:
    dbfile = sys.argv[1]
else:
    dbfile = os.path.join(os.environ['SSW'], 'hinode', 'eis',
                                     'data', 'timeline_sqlite',
                                     'timeline_db.sqlite')

#dbfile = 'timeline_db.sqlite'

#dblines = DB(dbfile)

app = QtWidgets.QApplication([])
top = Top(dbfile)

sys.exit(app.exec_())
