from PySide2 import QtWidgets as qtw
from PySide2 import QtCore as qtc
from PySide2 import QtGui as qtg
from tendo.singleton import SingleInstanceException
from about_minimisation import AboutMinimisationDialog
from config import Config

from db import Database
from enrol_form import EnrolForm
from factor_levels import FactorLevels
from freq_table import FreqTable
from mnd import *
from ui_main_window import Ui_MainWindow
from minim import Minim
from model import Model
from my_random import Random
from run_test import RunTest
from trial import *
import sys
from tendo import singleton

# noinspection PyTypeChecker
class MainWindow(qtw.QMainWindow):
    def __init__(self, settings):
        super(MainWindow, self).__init__()
        self.prev_tab_index = None
        self.helpdict = {
                    ('tialsTableWidget',): self.tr("""Each trial has a uniqe tile and also a code usually in the form of an abreviation of the title.\nLast column show if the trial is the active one. Only one trial can be active at any time. Other pages of the application relate to this active trial. To make a treial active check the box in last column.\nClick and then type over the trial title or code to edit it"""),
                    ('trialSetting',): self.tr('''In this page you can view and edit different settings for the current trial. Click on blue lable for each setting item to view detailed help information for that setting'''),
                    ('trialCreatedLabel','trialCreatedValueLabel'): self.tr('''Creation date of this trial\n'''),
                    ('trialModifiedLabel','trialModifiedValueLabel'): self.tr('''Modification date of this trial\n'''),
                    ('trialTitleLabel','trialTitleLineEdit'): self.tr('''Title of this trial. Title must be unique\n'''),
                    ('trialCodeLabel','trialCodeLineEdit'): self.tr('''Code of the trial usually title abbreviation\n'''),
                    ('trialProbmethodLabel','trialProbMethodComboBox'): self.tr('''The method used for calculating of the assignment probabilities. There are two choices. Biased coin minimization which include the effect of allocation ratios into the computation, and naive method which do not include the effect of allocation ratios. Biased coin is the preffered method. These options only is meaningfull when we have treatments with unequal allocation ratios. Biased Coin method is based on the different probabilities of assignment for subjects to trial groups. The subject is allocated to the preferred treatment with a higher probability and to other groups with lower probabilities. Therefore if treatments with higher allocations ratios are assigned more probabilities this will be considered as biased-coin minimization. In this method a base probability is used for the group with the lowest allocation ratio when that group is selected as the preferred treatment. Probabilities for other groups (PHi) when they selected as preferred treatments are calculated as a function of base probability and their allocation ratios. In Naive method the subject is allocated to the preferred treatment with a higher probability and to other groups with lower probabilities. Here the probabilities are not affected by allocation ratios and usually the same high probability are used for all treatment groups when they are selected as preferred treatments (base probability). This is denoted by Naive Minimization. In this method, probabilities for non-preferred treatments are distributed equally.\n'''),
                    ('trialBaseProbabilityLabel','trialBaseProbabilitySlider','baseProbLabel'): self.tr('''The probability given to the treatment with the lowest allocation ratio in biased-coin minimization. In naive minimization it is the probability used for all treatment groups when they are selected as preferred treatments. Use the spin button to specify the value.\n'''),
                    ('trialDistanceMethodLabel','trialDistanceMethodComboBox'): self.tr('''Distance measure is used to calculate the imbalance score. There are four options here. Marginal balance computes the cumulative difference between every possible pairs of level counts. This is the default and the one which is recommended. It tends to minimize more accurately when treatment groups are not equal in size. For each factor level marginal balance is calculated as a function of adjusted number of patients present in a factor level for each treatment group. Range calculates the difference between the maximum and the mimimum values in level counts. Standard deviation calculates the standard deviation of level counts and variance calculate their variances. All of these measures uses adjusted level counts. Adjustment performs relative to the values of allocation ratios.\n'''),
                    ('trialIdentifierTypeLabel', 'trialIdentifierTypeComboBox'): self.tr('''Type of subject identifier. It can be Numeric, Alpha or Alphanumeric.\nOnce a subject is enrolled, you can not change identifier type'''),
                    ('trialIdentifierOrderLabel','trialIentifierOrderComboBox'): self.tr('''The order of identifier sequence. It can be Sequential or Random.\n'''),
                    ('trialIdentifierLengthLabel','max_sample_size_label'): self.tr('''Length  of subjects identifier in character. The longer the length the higher  will be the possible sample size\n'''),
                    ('trialRecycleIdsLabel','trialRecycleIdsCheckBox'): self.tr('''Check to reuse IDs of a deleted subject. The default is to discard\n'''),
                    ('trialNewSubjectRandomLabel','trialNewSubjectRandomCheckBox'): self.tr('''If unchecked (the default), enrol form will show with the level for each factor unselected. If checked, for each factor a random level will be selected. This is only for test purpose to rapidly and randomly enrol a subject\n'''),
                    ('trialArmsWeightLabel','trialArmsWeightDoubleSlider', 'armWeightLabel'): self.tr(''' This is equalizing factor for arms of trial. The higher the arms weight the higher will be the possibility of having equal group sizes at the end of study\n'''),
                    ('treatmentTableWidget','Treatments'): self.tr('''For each treatment a name (unique for the trial) and an integer value for allocation ratio are needed. Allocation ratios are integer numeric values which denote the ratios of the treatment counts. For example if we have three groups a, b and c with their allocation ratios 1, 2 and 3 respectively, this mean that in the final sample nearly 1/6 of subjects will be from treatment a, 1/3 from b and 1/2 from c\nYou have to define at lease two groups to use minimisation on them.\nClick on treatment title and type over it to edit the title of the treatment, and use the spin control to change the allocation ratios'''),
                    ('Factors','factorTableWidget'): self.tr('''Factors or prognostic factors, are the subject factors which are selected to match subjects based on them. At lease one factor is mandatory for minimisation. Each factor has a name (unique for the trial) and a weight. Weight indicates the relative importance of the factor for minimization. For each factor you must define at least two levels. As an example factor may be gender and its levels may be Male and Female.\nFactor title can be edited by clicking and typing over the title of the factor\n\nClick on the levels button on tool bar or double click on levels in table to manage (add, edit, delete) levels of the selected factor'''),
                    ('factor_levels',): self.tr('''Manage (add, edit, delete) levels of the selected factor'''),
                    ('Frequencies','freqGridLayout'): self.tr('''This table shows frequencies of subjects enrolled in the trial or entered via preload table depending on the active checkboxes. When you have already enrolled a number of subjects (using other tools or software) and you want to continue with this minimisation program for the remaining cases, you can enter data of already enrolled subject as counts. You enter counts of different treatments accross all levels of each subject into the preload table. Then the counts from this table will be added to counts of enrolled subject by this application and total counts will be used in minimisation model.\nIt is as if you have entered the list of subject into your existing list. The effect is exactly the same. When defining preload please note that sum of subjects across different levels of a factor must be equal to thats of other factors, otherwise an error will be generated.\nIf you have a greal number of subjects and have no need to keep their list, you may convert them into preload table. Select frequencies check box and unselect preload checkbox, then click 'convert to preload' to clear all subjects and add them to preload table.'''),
                    ('Subjects', 'subjectTableWidget'): self.tr('''Subjects enrolled in the trial will be listed in subjects table. Each row belongs to one subject and shows subject ID, treatment, levels of factors, dates of enrollment and modification (if any). Subject can be added, edited or deleted from this table, although editing or deleting subject may compromise the minimisation model and therefore are not recommended.To enter a new subject, click plus sign in the tool bar. To edit double click and to delete click on Delete button on toolbar. A progressbar above the subject table show the enrollment progress as the number of subject enrolled compared to the remaining counts of identifiers'''),
                    ('Balance','balanceTreeWidget'): self.tr('''In this table you can see four different scale related to overall trial balance (marginal balance, range, variance, SD) and randomness. Also the balances for each factor or for each level within each factor are displayed. The last two rows of the table show the mean and max values for each scale. The lower the numeric values of these balance, the more balance we have in our trial. Also a scale of randomness for enrolling subjects is calculated based on run test.'''),
                    ('start', 'startWidget'): self.tr('''A quick list of steps to start a new minimization.''')
        }

        self.settings = settings


        engine = self.settings.value('random_engine', 'random', type=str)
        self.random = Random(engine)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)
        lang = settings.value('language', 'en_US', type=str)
        filename = 'start_{}.html'.format(lang)
        start_str = open(filename, encoding='utf8').read()
        self.ui.textEdit.setHtml(start_str)
        self.ui.actionNew.setIcon(qtg.QIcon('images/add.png'))
        self.ui.actionDelete.setIcon(qtg.QIcon('images/delete.png'))
        self.ui.actionLevels.setIcon(qtg.QIcon('images/levels.png'))
        self.ui.actionSave.setIcon(qtg.QIcon('images/save.png'))
        self.ui.actionHelp.setIcon(qtg.QIcon('images/help.png'))
        self.ui.actionQuit.setIcon(qtg.QIcon('images/exit.png'))
        self.ui.actionConfig.setIcon(qtg.QIcon('images/config.png'))
        self.ui.actionAbout_MinimPy2.setIcon(qtg.QIcon('images/about.png'))
        self.database = Database.get_instance(self)
        self.trial = None

        self.ui.tabWidget.currentChanged.connect(self.page_changed)

        self.ui.actionNew.triggered.connect(self.action_new)
        self.ui.actionDelete.triggered.connect(self.action_delete)
        self.ui.actionSave.triggered.connect(self.action_save)
        self.ui.actionLevels.triggered.connect(self.factor_levels)
        self.ui.actionQuit.triggered.connect(self.close)
        self.ui.actionImport.triggered.connect(self.import_mnd_file)
        self.ui.actionExport.triggered.connect(self.export_mnd_file)
        self.ui.actionHelp.triggered.connect(self.action_help)
        self.ui.actionConfig.triggered.connect(self.on_config)
        self.ui.actionAbout_Minimisation.triggered.connect(self.on_about_minimisation)
        self.ui.actionAbout_MinimPy2.triggered.connect(self.on_about_minimpy)
        self.ui.actionAbout_Qt.triggered.connect(lambda : qtw.QMessageBox.aboutQt(self, ''))

        lst = [self.ui.trialCreatedLabel, self.ui.trialCreatedValueLabel,
               self.ui.trialModifiedLabel, self.ui.trialModifiedValueLabel,
               self.ui.trialTitleLabel, self.ui.trialCodeLabel,
               self.ui.trialProbmethodLabel, self.ui.trialBaseProbabilityLabel,
               self.ui.baseProbLabel, self.ui.trialDistanceMethodLabel,
               self.ui.trialIdentifierTypeLabel, self.ui.trialIdentifierOrderLabel,
               self.ui.trialIdentifierLengthLabel, self.ui.max_sample_size_label,
               self.ui.trialRecycleIdsLabel, self.ui.trialNewSubjectRandomLabel,
               self.ui.trialArmsWeightLabel, self.ui.armWeightLabel]
        for w in lst:
            self.set_help_handler(w)

        self.ui.tialsTableWidget.cellChanged.connect(self.trial_cell_changed)
        self.ui.treatmentTableWidget.itemChanged.connect(self.treatment_item_changed)
        self.ui.factorTableWidget.itemChanged.connect(self.factor_item_changed)
        self.ui.factorTableWidget.cellClicked.connect(self.set_help_handler(self.ui.factorTableWidget))

        self.ui.tialsTableWidget.setSelectionBehavior(qtw.QAbstractItemView.SelectRows)
        self.ui.tialsTableWidget.setSelectionMode(qtw.QAbstractItemView.SingleSelection)

        self.ui.treatmentTableWidget.setSelectionBehavior(qtw.QAbstractItemView.SelectRows)
        self.ui.treatmentTableWidget.setSelectionMode(qtw.QAbstractItemView.SingleSelection)

        self.ui.factorTableWidget.setSelectionBehavior(qtw.QAbstractItemView.SelectRows)
        self.ui.factorTableWidget.setSelectionMode(qtw.QAbstractItemView.SingleSelection)

        self.ui.subjectTableWidget.setSelectionBehavior(qtw.QAbstractItemView.SelectRows)
        self.ui.subjectTableWidget.setSelectionMode(qtw.QAbstractItemView.SingleSelection)

        self.ui.editPreloadCheckBox.toggled.connect(self.on_edit_preload)
        self.ui.convertPreloadButton.clicked.connect(self.on_convert_preload)
        self.ui.factorTableWidget.cellDoubleClicked.connect(self.factor_coloumn_dblclicked)
        self.ui.subjectTableWidget.cellDoubleClicked.connect(self.subject_coloumn_dblclicked)

        self.ui.trialIdentifierLengthSpinBox.valueChanged.connect(self.on_identifier_length)
        self.ui.trialBaseProbabilitySlider.valueChanged.connect(self.on_base_prob_change)
        self.ui.trialArmsWeightDoubleSlider.valueChanged.connect(self.on_arm_weight_change)

        self.freqTable = None

        if self.settings.value('show_tutorial_at_start', True):
            self.ui.tabWidget.setCurrentIndex(7)
            self.ui.showAtStartCheckBox.setChecked(False)
        else:
            self.ui.tabWidget.setCurrentIndex(0)
            self.ui.showAtStartCheckBox.setChecked(True)
        self.ui.balanceTreeWidget.setColumnCount(5)
        self.ui.balanceTreeWidget.setHeaderLabels([self.tr('Factor/Level'), self.tr('Marginal balance'), self.tr('Range'), self.tr('Variance'), self.tr('SD')])

        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id != 0:
            trial_setting = self.database.get_trial_setting(last_trial_id)
            #  when db file accidentally deleted but the last_trial_id persist
            if not trial_setting:
                trial_setting = self.database.get_first_trial_setting()
                if not trial_setting:
                    last_trial_id = self.database.insert_trial(self.tr('New trial'))
                    self.settings.setValue('last_trial_id', last_trial_id)
                    trial_setting = self.database.get_trial_setting(last_trial_id)
            else:
                self.settings.setValue('last_trial_id', trial_setting.value('id'))
        else:
            trial_setting = self.database.get_first_trial_setting()
            if not trial_setting:
                last_trial_id = self.database.insert_trial(self.tr('New trial'))
                self.settings.setValue('last_trial_id', last_trial_id)
                trial_setting = self.database.get_trial_setting(last_trial_id)
            else:
                self.settings.setValue('last_trial_id', trial_setting.value('id'))
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id != 0:
            trial_setting = self.database.get_trial_setting(last_trial_id)
            if trial_setting:
                self.trial = Trial(trial_setting)
                #if self.database.has_preload(self.trial.id) or self.database.has_subject(self.trial.id):
                self.ui.frequenciesCheckBox.toggled.connect(self.set_cur_count)
                self.ui.preloadCheckBox.toggled.connect(self.set_cur_count)
        self.load_trials()
        self.ui.actionSave.setEnabled(False)
        self.ui.actionLevels.setEnabled(False)
        if self.settings.value('show_tutorial_at_start', True, bool):
            self.ui.tabWidget.setCurrentIndex(7)
            self.ui.showAtStartCheckBox.setChecked(False)
        else:
            self.ui.tabWidget.setCurrentIndex(0)
            self.ui.showAtStartCheckBox.setChecked(True)
        self.ui.showAtStartCheckBox.toggled.connect(self.toggle_show_at_start)

    def toggle_show_at_start(self, check):
        self.settings.setValue('show_tutorial_at_start', not check)

    def on_about_minimpy(self):
        about = '{}\n{} {} {} ({})\n{} {}\n{}'.format(self.tr('MinimPy'),
                                                      self.tr('MinimPy'),
                                                      self.tr('version'),
                                                      self.tr('2.0'),
                                                      self.tr('minimpy2'),
                                                      self.tr('Copyright'),
                                                      self.tr('2020'),
                                                      self.tr('Dr. Mahmoud Saghaei'))
        qtw.QMessageBox.about(self, self.tr('minimpy2'), about)

    def on_about_minimisation(self):
        ab_min = AboutMinimisationDialog(self)
        ab_min.show()

    def set_help_handler(self, w):
        def help_handler():
            self.setCursor(qtc.Qt.ArrowCursor)
            name = w.objectName()
            for key in self.helpdict:
                if name in key:
                    qtw.QMessageBox.information(
                        self, self.tr('Help!'),
                        self.tr(self.helpdict[key])
                    )
            return True
        clickable(w).connect(help_handler)

    def on_config(self):
        cur_lang = self.settings.value('language', 'en_US', type=str)
        cur_random = self.settings.value('random_engine', 'random', type=str)
        try:
            config = Config(self)
            config.exec_()
        except:
            qtw.QMessageBox.critical(self, self.tr('Error'), self.tr(
                'Error in languages.lst file format\nPlease see the READ.ME file in locales folder'))
            sys.exit(1)
        new_lang = self.settings.value('language', 'en_US', type=str)
        new_random = self.settings.value('random_engine', 'random', type=str)
        if new_lang != cur_lang or new_random != cur_random:
            qtw.QMessageBox.information(self,
                                        self.tr('Restart needed'),
                                        self.tr('Changes will be applied after restart!'))

    def action_help(self):
        currentIndex = self.ui.tabWidget.currentIndex()
        pages = ['tialsTableWidget', 'trialSetting', 'treatmentTableWidget',
                 'factorTableWidget', 'freqGridLayout',
                 'subjectTableWidget', 'balanceTreeWidget', 'start']
        page = pages[currentIndex]
        for key in self.helpdict:
            if page in key:
                qtw.QMessageBox.information(
                    self, self.tr('Help!'),
                    self.tr(self.helpdict[key])
                )

    def on_arm_weight_change(self, value):
        self.ui.armWeightLabel.setText('{}'.format(value))

    def on_base_prob_change(self, value):
        self.ui.baseProbLabel.setText('{:4.2f}'.format(value / 100.0))

    def on_identifier_length(self, value):
        self.trial.identifier_length = value
        self.update_sample_size_label()

    def update_sample_size_label(self):
        ss, max_sample_size = self.trial.get_max_sample_size()
        self.ui.max_sample_size_label.setText(self.tr('Maximum sample size > {}').format(max_sample_size))

    def closeEvent(self, event):
        button = qtw.QMessageBox.question(self, self.tr("Warning"),
                                          self.tr('Are you sure you want to quit?'),
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            event.ignore()
        else:
            event.accept()

    def page_changed(self, currentTabIndex):
        self.setCursor(qtc.Qt.ArrowCursor)
        self.ui.actionNew.setEnabled(False)
        self.ui.actionDelete.setEnabled(False)
        self.ui.actionSave.setEnabled(False)
        self.ui.actionLevels.setEnabled(False)
        if self.trial == None and currentTabIndex != 0:
            qtw.QMessageBox.information(
                self, self.tr('No current trial!'),
                self.tr('Fist you have to define and select a trial as current!')
            )
            self.ui.actionNew.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.ui.actionSave.setEnabled(False)
            self.ui.actionLevels.setEnabled(False)
            self.ui.tabWidget.setCurrentIndex(0)
            return
        if self.prev_tab_index == 1:
            self.save_trial_setting()
            last_trial_id = self.settings.value('last_trial_id', 0, type=int)
            trial_setting = self.database.get_trial_setting(last_trial_id)
            self.trial = Trial(trial_setting)
        self.prev_tab_index = currentTabIndex
        if currentTabIndex == 0:  # trials
            self.ui.actionNew.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.load_trials()
        elif currentTabIndex == 1:  # trial  settings
            self.ui.actionSave.setEnabled(True)
            self.load_trial_settings()
        elif currentTabIndex == 2:  # treatments
            self.ui.actionNew.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.load_treatments()
        elif currentTabIndex == 3:  # factors
            self.ui.actionNew.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.ui.actionLevels.setEnabled(True)
            self.load_factors()
        elif currentTabIndex == 4:  # Frequencies
            self.ui.frequenciesCheckBox.setChecked(False)
            self.ui.preloadCheckBox.setChecked(False)
            self.ui.editPreloadCheckBox.setChecked(False)
            self.ui.actionSave.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.ui.convertPreloadButton.setEnabled(False)
            self.ui.convertWarningCheckBox.setVisible(False)
            self.load_frequencies()
        elif currentTabIndex == 5:  # Subjects
            self.ui.actionNew.setEnabled(True)
            self.ui.actionDelete.setEnabled(True)
            self.load_subjects()
        elif currentTabIndex == 6:  # Balance
            self.load_balance()

    def get_factor_level_dict(self, trial_id):
        factor_list = self.database.read_factors(trial_id)
        factors = []
        for factor in factor_list:
            f = {'id': factor[0], 'title': factor[1], 'weight': factor[2], 'levels': []}
            levels = self.database.factor_levels(factor[0])
            for level in levels:
                lv = {'id': level[0], 'title': level[1]}
                f['levels'].append(lv)
            if self.trial.new_subject_random:
                index = self.random.randint(0, len(f['levels']) - 1)
                f['selected_level_id'] = f['levels'][index]['id']
            else:
                f['selected_level_id'] = -1
            factors.append(f)
        return factors

    def add_new_subject(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        factors = self.get_factor_level_dict(last_trial_id)
        if len(factors) < 1:
            qtw.QMessageBox.information(
                self,
                self.tr('Factor'),
                self.tr('Define one factor at least')
            )
            return
        for f in factors:
            if len(f['levels']) == 0:
                qtw.QMessageBox.information(
                    self,
                    self.tr('Levels'),
                    self.tr('Define at least two levels for each factor')
                )
                return
        treatments = self.database.read_treatments(last_trial_id)
        if len(treatments) < 2:
            qtw.QMessageBox.information(
                self,
                self.tr('Treatment'),
                self.tr('Define two treatments at least')
            )
            return
        treatment = {'selected_treatment_id': -1, 'treatments': []}
        for t in treatments:
            treatment['treatments'].append({'id': t[0], 'title': t[1]})
        enrol_form = EnrolForm(self, factors, treatment, False)
        enrol_form.exec_()
        if treatment['selected_treatment_id'] == -1:
            return
        self.update_subjects_progress()

    def subject_coloumn_dblclicked(self, row, col):
        button = qtw.QMessageBox.question(self, self.tr("Warning"),
                                          self.tr('''Are you sure you want to edit subject at row "{}" ?\n
Editing subject may invalidate your research and the result of minimisation''').format(row),
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            return
        subject_id = int(self.ui.subjectTableWidget.item(row, 0).text())
        identifier = self.ui.subjectTableWidget.item(row, 1).text()
        treatment_id = self.database.get_subject_treatment_id(subject_id)
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        factors = self.get_factor_level_dict(last_trial_id)
        subject_levels = self.database.get_subject_levels(subject_id)
        for factor in factors:
            for subject_level in subject_levels:
                if factor['id'] == subject_level[0]:
                    factor['selected_level_id'] = subject_level[1]
                    break
        treatments = self.database.read_treatments(last_trial_id)
        treatment = {'selected_treatment_id': treatment_id, 'treatments': []}
        for t in treatments:
            treatment['treatments'].append({'id': t[0], 'title': t[1]})
        enrol_form = EnrolForm(self, factors, treatment, True, identifier)
        enrol_form.exec_()
        result = enrol_form.result()
        if result != qtw.QDialog.Accepted:
            return
        treatment_id = treatment['selected_treatment_id']
        for factor in factors:
            for subject_level in subject_levels:
                if factor['id'] == subject_level[0]:
                    subject_level[1] = factor['selected_level_id']
                    break
        self.database.delete_subject_levels(subject_id)
        self.database.insert_subject_levels(last_trial_id, subject_id, subject_levels)
        self.database.update_subject_treatment_id(subject_id, treatment_id)
        self.clear_subject_filters()
        self.ui.subjectTableWidget.selectRow(row)
        self.ui.subjectTableWidget.setFocus()

    def get_min_free_identifier(self, used_ids):
        n = 0
        while n in used_ids:
            n += 1
        return n

    def enrol_one(self, selected_indices, selected_ids):
        trial_id = self.settings.value('last_trial_id', 0, type=int)
        if trial_id == 0:
            return
        max_sample_size, f = self.trial.get_max_sample_size()
        subject_count = self.database.get_subject_count(trial_id)
        if subject_count == max_sample_size:
            qtw.QMessageBox.critical(
                self,
                self.tr('Error'),
                self.tr(
                    'Sample size consumed!\nNo further subject enrol possible!\nPlease convert cases to preload and continue')
            )
            return
        used_ids = self.database.get_used_identifiers(trial_id)
        if not self.trial.recycle_ids:
            used_ids.extend(self.database.get_discarded_identifiers(trial_id))
        if not used_ids:
            used_ids = [-1]
        if self.trial.identifier_order == SEQUENTIAL:
            identifier_value = self.get_min_free_identifier(used_ids)
        else:
            identifier_value = self.get_random_identifier(used_ids, int(max_sample_size))
        new_case = {'levels': selected_indices, 'allocation': -1, 'UI': identifier_value}
        # minimised group and preferred group
        m_treatment, p_treatment, probs = self.get_minimize_case(new_case, trial_id)
        treatments = self.database.read_treatments(trial_id)
        treatment_id = treatments[m_treatment][0]
        subject_id = self.database.insert_subject(identifier_value, treatment_id, trial_id)
        self.database.insert_subject_levels(trial_id, subject_id, selected_ids)
        if subject_count == 0:
            self.load_subjects()
        else:
            self.clear_subject_filters()
        self.select_row(self.ui.subjectTableWidget, subject_id)
        if p_treatment is None:
            p_treatment = 0
        return treatments[m_treatment][1], \
               m_treatment, \
               treatments[p_treatment][1], \
               p_treatment, \
               self.trial.format_subject_identifier(identifier_value), probs

    def select_row(self, table_widget, row_id):
        for r in range(table_widget.rowCount()):
            item = table_widget.item(r, 0)
            if item is None: continue
            if row_id == int(item.text()):
                table_widget.selectRow(r)
                return

    def get_minimize_case(self, new_case, trial_id):
        model = Model()
        num_treatments = self.database.get_num_treatments(trial_id)
        model.groups = list(range(num_treatments))
        model.variables = self.database.get_factors_level_indices(trial_id)
        model.variables_weight = self.database.get_factor_weights(trial_id)
        model.allocation_ratio = self.database.get_allocation_ratios(trial_id)
        model.allocations = self.database.get_allocations(trial_id)
        model.prob_method = self.trial.prob_method
        model.distance_measure = self.trial.dist_method
        model.high_prob = self.trial.base_prob
        model.min_group = self.database.get_min_allocation_ratio_group_index(trial_id)
        model.arms_weight = self.trial.arms_weight
        m = Minim(random=self.random, model=model)
        m.build_probs(model.high_prob, model.min_group)
        m.build_freq_table()
        if self.database.has_preload(trial_id):
            self.add_to_preload(m.freq_table, trial_id)
        minimised_group = m.enroll(new_case, m.freq_table)
        preffered_group= m.pref_group
        probs = m.selected_probs
        return minimised_group, preffered_group, probs

    def add_to_preload(self, freq_table, trial_id):
        preload = self.database.get_preload(trial_id)
        treatments = self.database.read_treatments(trial_id)
        factors = self.database.read_factors(trial_id)
        for row, group in enumerate(freq_table):
            for v, variable in enumerate(group):
                for l, level in enumerate(variable):
                    factor = factors[v]
                    levels = self.database.factor_levels(factor[0])
                    level = levels[l]
                    treatment = treatments[row]
                    key = (treatment[0], factor[0], level[0])
                    freq_table[row][v][l] += preload[key]

    def get_random_identifier(self, used_ids, max_sample_size):
        if (max_sample_size / len(used_ids) < 100):
            pv = list(range(max_sample_size))
            for used_id in used_ids:
                if used_id in pv:
                    pv.remove(used_id)
            index = self.random.randint(0, len(pv) - 1)
            return pv[index]
        else:
            v = self.random.randint(0, max_sample_size - 1)
            while v in used_ids:
                v = self.random.randint(0, max_sample_size - 1)
            return v

    def update_subjects_progress(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        ss, ss_label = self.trial.get_max_sample_size()
        ss -= self.database.get_count_discarded_identifiers(last_trial_id)
        self.ui.subjectProgressBar.setMaximum(ss)
        cnt = self.database.get_subject_count(last_trial_id)
        self.ui.subjectProgressBar.setValue(cnt)

    def load_subjects(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        self.ui.subjectTableWidget.setRowCount(0)
        cnt = self.database.get_subject_count(last_trial_id)
        self.update_subjects_progress()
        if cnt == 0:
            return
        factors = self.database.read_factors(last_trial_id)
        self.ui.subjectTableWidget.setColumnCount(len(factors) + 5)
        headers = ['#', self.tr('ID'), self.tr('Treatment')]
        for factor in factors:
            headers.append(factor[1])
        headers += [self.tr('Enrolled'), self.tr('Modified')]
        self.ui.subjectTableWidget.setHorizontalHeaderLabels(headers)
        header = self.ui.subjectTableWidget.horizontalHeader()
        header.setSectionResizeMode(
            self.ui.subjectTableWidget.columnCount() - 1,
            qtw.QHeaderView.ResizeToContents
        )
        header.setSectionResizeMode(
            self.ui.subjectTableWidget.columnCount() - 2,
            qtw.QHeaderView.ResizeToContents
        )
        self.ui.subjectTableWidget.insertRow(0)
        for i in range(1, len(headers)):
            lineEdit = qtw.QLineEdit()
            lineEdit.setPlaceholderText(self.tr('Filter'))
            lineEdit.editingFinished.connect(self.subject_colum_editing_finished)
            self.ui.subjectTableWidget.setCellWidget(0, i, lineEdit)
        self.ui.subjectTableWidget.hideColumn(0)
        self.load_subject_rows()

    def subject_colum_editing_finished(self):
        self.load_subject_rows()

    def clear_subject_filters(self):
        for c in range(1, self.ui.subjectTableWidget.columnCount()):
            lineEdit = self.ui.subjectTableWidget.cellWidget(0, c)
            lineEdit.setText('')
        self.load_subject_rows()

    def load_subject_rows(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        subjects = self.database.get_subjects(last_trial_id)
        self.ui.subjectTableWidget.setRowCount(1)
        fields = [None]
        for c in range(1, self.ui.subjectTableWidget.columnCount()):
            lineEdit = self.ui.subjectTableWidget.cellWidget(0, c)
            if lineEdit is None:
                self.load_subjects()
                return
            text = lineEdit.text().strip()
            fields.append(None if len(text) == 0 else text)
        # model = self.ui.subjectTableWidget.model()
        for subject in subjects:
            subject_id = subject[0]
            treatment_id = subject[1]
            identifier_value = subject[2]
            enrolled = subject[3]
            modified = subject[4]
            treatment = self.database.get_treatment_title(treatment_id)
            row = [subject_id, self.trial.format_subject_identifier(identifier_value), treatment]
            subject_levels = self.database.get_subject_levels(subject_id)
            row.extend(self.database.get_subject_level_titles(subject_levels))
            row.extend([enrolled, modified])
            for c, row_text in enumerate(row):
                if fields[c] == None:
                    continue
                if not fields[c] in row_text:
                    break
            else:
                r = self.ui.subjectTableWidget.rowCount()
                self.ui.subjectTableWidget.insertRow(r)
                # data = model.headerData(r, qtc.Qt.Vertical)
                for c, row_text in enumerate(row):
                    item = qtw.QTableWidgetItem(str(row_text))
                    item.setFlags(item.flags() ^ qtc.Qt.ItemIsEditable)
                    self.ui.subjectTableWidget.setItem(r, c, item)
        labels = [str(n) for n in range(1, len(subjects) + 1)]
        labels.insert(0, '')
        self.ui.subjectTableWidget.setVerticalHeaderLabels(labels)

    def set_cur_count(self, checked):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        show_freq = self.ui.frequenciesCheckBox.isChecked()
        show_preload = self.ui.preloadCheckBox.isChecked()
        if show_preload and not show_freq:
            self.ui.editPreloadCheckBox.setEnabled(True)
            self.on_edit_preload(self.ui.editPreloadCheckBox.isChecked())
        else:
            self.ui.editPreloadCheckBox.setEnabled(False)
            self.ui.editPreloadCheckBox.setChecked(False)
        self.ui.convertPreloadButton.setEnabled(False)
        if show_preload and show_freq:
            cur_count = self.database.get_preload_with_freq(last_trial_id)
        elif show_preload and not show_freq:
            cur_count = self.database.get_preload(last_trial_id)
        elif show_freq and not show_preload:
            cur_count = self.database.get_freq(last_trial_id)
            if self.database.has_subject(last_trial_id):
                self.ui.convertPreloadButton.setEnabled(True)
        else:
            cur_count = self.database.get_empty_freq(last_trial_id)
        if cur_count is None:
            return
        self.freqTable.set_counts(cur_count)

    def on_convert_preload(self):
        if not self.ui.convertWarningCheckBox.isChecked():
            qtw.QMessageBox.information(
                self, self.tr('Double check!'),
                self.tr('Need your final confirm')
            )
            self.ui.convertWarningCheckBox.setVisible(True)
            return
        button = qtw.QMessageBox.question(self, self.tr("Deleting preload"),
                                          self.tr('''Are you certainly sure you want to convert all subjects into preload?
ALL subjects will be deleted'''),
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            self.ui.convertWarningCheckBox.setChecked(False)
            self.ui.convertWarningCheckBox.setVisible(False)
            return
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        self.ui.convertWarningCheckBox.setChecked(False)
        self.ui.convertWarningCheckBox.setVisible(False)
        self.ui.convertPreloadButton.setEnabled(False)
        self.freqTable.add_to_preload()
        self.database.delete_subjects(last_trial_id)
        self.ui.frequenciesCheckBox.setChecked(False)
        self.ui.preloadCheckBox.setChecked(True)

    def on_edit_preload(self, check):
        self.freqTable.toggleReadOnly()

    def load_frequencies(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        self.freqTable = FreqTable(self, self.ui, last_trial_id)
        self.freqTable.build()
        if self.database.has_subject(self.trial.id):
            self.ui.frequenciesCheckBox.setChecked(True)
        elif self.database.has_preload(self.trial.id):
            self.ui.preloadCheckBox.setChecked(True)

    def factor_levels(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        selected = self.ui.factorTableWidget.selectedIndexes()
        if not selected:
            return
        factor_id = int(self.ui.factorTableWidget.item(selected[0].row(), 0).text())
        factor_title = self.ui.factorTableWidget.item(selected[0].row(), 1).text()
        factorLevels = FactorLevels(self, last_trial_id, factor_id, factor_title)
        factorLevels.exec_()
        self.load_factors()

    def getCurrentTrialFunc(self, trial):
        def getToggleFunc(checked):
            trial_id = int(trial[0])
            title = trial[1]
            if len(title) > 100:
                title = title[:97] + '...'
            self.setWindowTitle(self.tr('minimpy2 [{}]').format(title))
            self.settings.setValue('last_trial_id', trial_id)
            self.trial = Trial(self.database.get_trial_setting(trial_id))

        return getToggleFunc

    def action_new(self):
        self.setCursor(qtc.Qt.ArrowCursor)
        currentTabIndex = self.ui.tabWidget.currentIndex()
        if currentTabIndex == 0:  # trials
            self.add_new_trial()
        elif currentTabIndex == 2:  # treatments
            self.add_new_treatment()
        elif currentTabIndex == 3:  # factors
            self.add_new_factor()
        elif currentTabIndex == 5:  # subjects
            self.add_new_subject()

    def action_save(self):
        self.setCursor(qtc.Qt.ArrowCursor)
        currentTabIndex = self.ui.tabWidget.currentIndex()
        if currentTabIndex == 1:  # setting
            self.save_trial_setting()
        elif currentTabIndex == 4:
            self.freqTable.on_save_preload()

    def add_new_factor(self):
        self.setCursor(qtc.Qt.ArrowCursor)
        if self.check_trial_subjects_or_preload():
            return
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        title, ok = qtw.QInputDialog.getText(
            self,
            self.tr('Factor title'),
            self.tr('Title'),
            qtw.QLineEdit.EchoMode.Normal
        )
        if ok and len(title.strip()) != 0:
            if self.database.factor_title_exists(title, last_trial_id):
                qtw.QMessageBox.critical(self,
                                         self.tr('Error!'),
                                         self.tr('''Factor title '{}' already exist!'''.format(title))
                                         )
                return
            factor_id = self.database.insert_factor(last_trial_id, title)
            self.ui.factorTableWidget.blockSignals(True)
            factor = [str(factor_id), title, '', 1.0]
            self.add_factor_row(factor)
            self.ui.factorTableWidget.blockSignals(False)

    def add_new_treatment(self):
        if self.check_trial_subjects_or_preload():
            return
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        title, ok = qtw.QInputDialog.getText(
            self,
            self.tr('Treatment title'),
            self.tr('Title'),
            qtw.QLineEdit.EchoMode.Normal
        )
        if ok and len(title.strip()) != 0:
            if self.database.treatment_title_exists(title, last_trial_id):
                qtw.QMessageBox.critical(self,
                                         self.tr('Error!'),
                                         self.tr('''Treatment title '{}' already exist!'''.format(title))
                                         )
                return
            treatment_id = self.database.insert_treatment(last_trial_id, title)
            self.ui.treatmentTableWidget.blockSignals(True)
            cols = [str(treatment_id), title, 1]
            self.add_treatment_row(cols)
            self.ui.treatmentTableWidget.blockSignals(False)

    def add_new_trial(self):
        title, ok = qtw.QInputDialog.getText(
            self,
            self.tr('Trial title'),
            self.tr('Title'),
            qtw.QLineEdit.EchoMode.Normal
        )
        if ok and len(title.strip()) != 0:
            if self.database.trial_title_exists(title):
                qtw.QMessageBox.critical(self,
                                         self.tr('Error!'),
                                         self.tr('''A trial title '{}' already exist!'''.format(title))
                                         )
                return
            trial_id = self.database.insert_trial(title)
            self.settings.setValue('last_trial_id', trial_id)
            self.trial = Trial(self.database.get_trial_setting(trial_id))
            self.load_trials()

    def action_delete(self):
        self.setCursor(qtc.Qt.ArrowCursor)
        currentTabIndex = self.ui.tabWidget.currentIndex()
        if currentTabIndex == 0:
            self.delete_trial()
        elif currentTabIndex == 2:  # treatments
            self.delete_treatment()
        elif currentTabIndex == 3:  # factors
            self.delete_factor()
        elif currentTabIndex == 4:  # preload
            self.freqTable.on_clear_preload()
        elif currentTabIndex == 5:  # subjects
            self.delete_subject()

    def delete_subject(self):
        selected = self.ui.subjectTableWidget.selectedIndexes()
        if not selected:
            return
        identifier = self.ui.subjectTableWidget.item(selected[0].row(), 1).text()
        button = qtw.QMessageBox.question(self, self.tr("Confirm delete"),
                                          self.tr('Are you sure you want to delete subject "{}"?').format(identifier),
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            return
        subject_id = int(self.ui.subjectTableWidget.item(selected[0].row(), 0).text())
        subject = self.database.get_subject(subject_id)
        id_value = subject.value('identifier_value')
        trial_id = subject.value('trial_id')
        if self.trial.recycle_ids:
            id_value = None
        self.database.delete_subject(subject_id, id_value, trial_id)
        self.ui.subjectTableWidget.removeRow(selected[0].row())
        self.update_subjects_progress()

    def check_trial_subjects_or_preload(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        has_subject = self.database.has_subject(last_trial_id)
        has_preload = self.database.has_preload(last_trial_id)
        if has_subject or has_preload:
            qtw.QMessageBox.warning(self, self.tr('New factor not allowed!'),
                                    self.tr('''Trial already has subject or preload. 
                                    You can not add or delete factors, treatments or levels'''))
            return True
        return False

    def delete_treatment(self):
        if self.check_trial_subjects_or_preload():
            return
        selected = self.ui.treatmentTableWidget.selectedIndexes()
        if not selected:
            return
        title = self.ui.treatmentTableWidget.item(selected[0].row(), 1).text()
        button = qtw.QMessageBox.question(self, self.tr("Confirm delete"),
                                          self.tr('Are you sure you want to delete "%s"?') % title,
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            return
        treatment_id = int(self.ui.treatmentTableWidget.item(selected[0].row(), 0).text())
        self.database.delete_treatment(treatment_id)
        self.ui.treatmentTableWidget.blockSignals(True)
        self.ui.treatmentTableWidget.removeRow(selected[0].row())
        self.ui.treatmentTableWidget.blockSignals(False)

    def delete_factor(self):
        if self.check_trial_subjects_or_preload():
            return
        selected = self.ui.factorTableWidget.selectedIndexes()
        if not selected:
            return
        title = self.ui.factorTableWidget.item(selected[0].row(), 1).text()
        button = qtw.QMessageBox.question(self, self.tr("Confirm delete"),
                                          self.tr('Are you sure you want to delete "%s"?') % title,
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            return
        factor_id = int(self.ui.factorTableWidget.item(selected[0].row(), 0).text())
        self.database.delete_factor(factor_id)
        self.ui.factorTableWidget.blockSignals(True)
        self.ui.factorTableWidget.removeRow(selected[0].row())
        self.ui.factorTableWidget.blockSignals(False)

    def delete_trial(self):
        selected = self.ui.tialsTableWidget.selectedIndexes()
        if not selected:
            return
        title = self.ui.tialsTableWidget.item(selected[0].row(), 1).text()
        button = qtw.QMessageBox.question(self, self.tr("Confirm delete"),
                                          self.tr('Are you sure you want to delete "%s"?') % title,
                                          qtw.QMessageBox.Yes | qtw.QMessageBox.No)
        if button != qtw.QMessageBox.Yes:
            return
        trial_id = int(self.ui.tialsTableWidget.item(selected[0].row(), 0).text())
        self.database.delete_trial(trial_id)
        self.setWindowTitle(self.tr('minimpy2'))
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == trial_id:
            self.settings.setValue('last_trial_id', 0)
            self.trial = None
        self.ui.tialsTableWidget.removeRow(selected[0].row())

    def factor_item_changed(self, itemWidget):
        row, col = itemWidget.row(), itemWidget.column()
        column = 'title'
        value = self.ui.factorTableWidget.item(row, col).text()
        if col == 3:
            column = 'weight'
            value = float(value)
        factor_id = int(self.ui.factorTableWidget.item(row, 0).text())
        oldValue = self.database.update_factor(factor_id, column, value)
        if oldValue is not None:
            self.abort_table_widget_change(self.ui.factorTableWidget, row, col, oldValue)

    def abort_table_widget_change(self, table, row, col, oldValue):
        table.item(row, col).setText(oldValue)

    def treatment_item_changed(self, itemWidget):
        row, col = itemWidget.row(), itemWidget.column()
        column = 'title'
        value = self.ui.treatmentTableWidget.item(row, col).text()
        if col == 2:
            column = 'ratio'
            value = int(value)
        treatment_id = int(self.ui.treatmentTableWidget.item(row, 0).text())
        oldValue = self.database.update_treatment(treatment_id, column, value)
        if oldValue is not None:
            print('not save')
            self.abort_table_widget_change(self.ui.treatmentTableWidget, row, col, oldValue)

    def trial_cell_changed(self, row, col):
        trial_id = int(self.ui.tialsTableWidget.item(row, 0).text())
        column = 'title'
        value = self.ui.tialsTableWidget.item(row, col).text()
        if col == 2:
            column = 'code'
        oldValue = self.database.update_trial(trial_id, column, value)
        if oldValue is not None:
            self.abort_table_widget_change(self.ui.tialsTableWidget, row, col, oldValue)

    def load_treatments(self):
        self.ui.treatmentTableWidget.blockSignals(True)
        self.ui.treatmentTableWidget.setRowCount(0)
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            self.ui.treatmentTableWidget.blockSignals(False)
            return
        query = self.database.load_treatments(last_trial_id)
        self.ui.treatmentTableWidget.setColumnCount(3)
        ID = self.tr('ID')
        Title = self.tr('Title')
        Ratio = self.tr('Ratio')
        self.ui.treatmentTableWidget.setHorizontalHeaderLabels([ID, Title, Ratio])
        header = self.ui.treatmentTableWidget.horizontalHeader()
        header.setSectionResizeMode(0, qtw.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, qtw.QHeaderView.Stretch)
        header.setSectionResizeMode(2, qtw.QHeaderView.ResizeToContents)
        while query.next():
            cols = [str(query.value('id')), query.value('title'), query.value('ratio')]
            self.add_treatment_row(cols)
        self.ui.treatmentTableWidget.blockSignals(False)

    def add_treatment_row(self, cols):
        r = self.ui.treatmentTableWidget.rowCount()
        self.ui.treatmentTableWidget.insertRow(r)
        id_item = qtw.QTableWidgetItem(cols[0])
        id_item.setTextAlignment(qtc.Qt.AlignCenter)
        self.ui.treatmentTableWidget.setCellWidget(r, 0, qtw.QLabel())
        self.ui.treatmentTableWidget.setItem(r, 0, id_item)
        title_item = qtw.QTableWidgetItem(cols[1])
        self.ui.treatmentTableWidget.setItem(r, 1, title_item)
        ratio_spin = qtw.QSpinBox()
        ratio_spin.setMinimum(1)
        ratio_spin.setMaximum(20)
        ratio_spin.setValue(cols[2])
        ratio_item = qtw.QTableWidgetItem(str(cols[2]))
        self.ui.treatmentTableWidget.setItem(r, 2, ratio_item)
        ratio_spin.valueChanged.connect(lambda value: ratio_item.setText(str(value)))
        self.ui.treatmentTableWidget.setCellWidget(r, 2, ratio_spin)

    def add_one_trial_cell(self, r, c, trial):
        item = trial[c]
        oneItem = qtw.QTableWidgetItem(item)
        if c == 0:
            oneItem.setTextAlignment(qtc.Qt.AlignCenter)
            self.ui.tialsTableWidget.setCellWidget(r, c, qtw.QLabel())
        if c == 3:
            check = qtw.QRadioButton()
            check.setStyleSheet('text-align: center; margin-left:50%; margin-right:50%;')
            check.toggled.connect(self.getCurrentTrialFunc(trial))
            check.setChecked(item)
            self.ui.tialsTableWidget.setCellWidget(r, c, check)
        self.ui.tialsTableWidget.setItem(r, c, oneItem)

    def load_factors(self):
        self.ui.factorTableWidget.blockSignals(True)
        self.ui.factorTableWidget.setRowCount(0)
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        self.ui.factorTableWidget.setColumnCount(4)
        header = self.ui.factorTableWidget.horizontalHeader()
        header.setSectionResizeMode(0, qtw.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, qtw.QHeaderView.Stretch)
        header.setSectionResizeMode(2, qtw.QHeaderView.Stretch)
        self.ui.factorTableWidget.setHorizontalHeaderLabels([self.tr('ID'), self.tr('Factor'), self.tr('Levels'),
                                                             self.tr('Weight')])
        query = self.database.load_factors(last_trial_id)
        factors = []
        while query.next():
            factor = [str(query.value('id')), query.value('title'), query.value('weight')]
            factors.append(factor)
        for factor in factors:
            levels = self.database.get_levels(int(factor[0]))
            factor.insert(2, levels)
            self.add_factor_row(factor)
        self.ui.factorTableWidget.blockSignals(False)

    def factor_coloumn_dblclicked(self, row, col):
        if col != 2:
            return
        self.factor_levels()

    def add_factor_row(self, factor):
        r = self.ui.factorTableWidget.rowCount()
        self.ui.factorTableWidget.insertRow(r)
        id_item = qtw.QTableWidgetItem(factor[0])
        id_item.setTextAlignment(qtc.Qt.AlignCenter)
        self.ui.factorTableWidget.setCellWidget(r, 0, qtw.QLabel())
        self.ui.factorTableWidget.setItem(r, 0, id_item)
        title_item = qtw.QTableWidgetItem(factor[1])
        self.ui.factorTableWidget.setItem(r, 1, title_item)
        levels_item = qtw.QTableWidgetItem(factor[2])
        levels_item.setTextAlignment(qtc.Qt.AlignCenter)
        self.ui.factorTableWidget.setCellWidget(r, 2, qtw.QLabel())
        self.ui.factorTableWidget.setItem(r, 2, levels_item)
        weight_spin = qtw.QDoubleSpinBox()
        weight_spin.setMinimum(1.0)
        weight_spin.setMaximum(10.0)
        weight_spin.setSingleStep(0.10)
        weight_spin.setValue(factor[3])
        weight_item = qtw.QTableWidgetItem(str(factor[3]))
        self.ui.factorTableWidget.setItem(r, 3, weight_item)
        weight_spin.valueChanged.connect(lambda value: weight_item.setText(str(value)))
        self.ui.factorTableWidget.setCellWidget(r, 3, weight_spin)

    def load_trials(self):
        self.ui.tialsTableWidget.blockSignals(True)
        self.ui.tialsTableWidget.setRowCount(0)
        query = self.database.get_db().exec_('SELECT id, title, code FROM trials')
        trial_list = []
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        while query.next():
            row = [
                str(query.value('id')),
                query.value('title'),
                query.value('code'),
                query.value('id') == last_trial_id
            ]

            trial_list.append(row)
        if len(trial_list) == 0:
            return
        self.ui.tialsTableWidget.setRowCount(len(trial_list))
        self.ui.tialsTableWidget.setColumnCount(len(trial_list[0]))
        title = self.tr('Title')
        trial_id = self.tr('ID')
        code = self.tr('Code')
        current = self.tr('Current')
        header_lst = [trial_id, title, code, current]
        self.ui.tialsTableWidget.setHorizontalHeaderLabels(header_lst)
        header = self.ui.tialsTableWidget.horizontalHeader()
        header.setSectionResizeMode(0, qtw.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(1, qtw.QHeaderView.Stretch)
        header.setSectionResizeMode(2, qtw.QHeaderView.ResizeToContents)
        header.setSectionResizeMode(3, qtw.QHeaderView.ResizeToContents)
        for r in range(len(trial_list)):
            for c in range(len(trial_list[r])):
                self.add_one_trial_cell(r, c, trial_list[r])
        self.ui.tialsTableWidget.setColumnWidth(1, 400)
        self.ui.tialsTableWidget.blockSignals(False)

    def save_trial_setting(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        self.database.update_trial_setting(last_trial_id, self.ui)
        self.setWindowTitle(self.tr('minimpy2 [{}]').format(self.ui.trialTitleLineEdit.text()))
        self.load_trial_settings()

    def load_trial_settings(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        if self.database.has_subject(last_trial_id):
            self.ui.trialIdentifierTypeComboBox.setEnabled(False)
        else:
            self.ui.trialIdentifierTypeComboBox.setEnabled(True)
        self.database.load_trial_setting(last_trial_id, self.ui)
        self.on_identifier_length(self.trial.identifier_length)
        self.on_base_prob_change(self.trial.base_prob * 100)

    def load_balance(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        if not self.database.has_preload(last_trial_id) and not self.database.has_subject(last_trial_id):
            return
        table = []
        freq = self.database.get_preload_with_freq(last_trial_id)
        treatments = self.database.read_treatments(last_trial_id)
        factors = self.database.read_factors(last_trial_id)
        for treatment in treatments:
            row = []
            for factor in factors:
                t = treatment[0]
                f = factor[0]
                levels = self.database.factor_levels(f)
                for level in levels:
                    l = level[0]
                    k = (t, f, l)
                    row.append(freq[k])
            table.append(row)
        balance = self.get_trial_balance(table, last_trial_id)
        variables = []
        for factor in factors:
            f = factor[0]
            levels = self.database.factor_levels(f)
            variables.append(list(range(len(levels))))
        row = 0
        self.ui.balanceTreeWidget.clear()
        for idx, variable in enumerate(variables):
            wt = factors[idx][2]
            var_total = [0] * 4
            level_rows = []
            for level in variable:
                for i in range(4):
                    balance[row][i] *= float(wt)
                    var_total[i] += balance[row][i]
                level_rows.append(balance[row])
                row += 1
            var_total = [factors[idx][1]] + [var_total[j] / len(variable) for j in range(4)]
            var_total = list(map(str, var_total))
            variable_node = qtw.QTreeWidgetItem(self.ui.balanceTreeWidget, var_total)
            levels = self.database.factor_levels(factors[idx][0])
            level_names = [level[1] for level in levels]
            for level, level_row in enumerate(level_rows):
                level_str_list = list(map(str, [level_names[level]] + level_row))
                qtw.QTreeWidgetItem(variable_node, level_str_list)
        last_row = row
        rows = len(balance) - 2
        for col in range(4):
            balance[rows].append(1.0 * sum([balance[row][col] for row in range(rows)]) / rows)
            balance[rows + 1].append(max([balance[row][col] for row in range(rows)]))
        means = list(map(str, [self.tr('Mean')] + balance[last_row]))
        qtw.QTreeWidgetItem(self.ui.balanceTreeWidget, means)
        maxes = list(map(str, [self.tr('Max')] + balance[last_row + 1]))
        qtw.QTreeWidgetItem(self.ui.balanceTreeWidget, maxes)
        self.ui.balanceProgressBar.setValue(100 - 100 * balance[last_row][0])
        self.ui.mean_balance_label.setText(self.tr('Balance (mean MB = {:.4f})').format(balance[last_row][0]))
        subjects = self.database.get_subjects(last_trial_id)
        if len(subjects) == 0:
            self.ui.randomness_label.setText(self.tr('No subject enrolled!'))
            self.ui.randomnessProgressBar.setValue(0)
            return
        else:
            self.set_randomness(subjects, last_trial_id)

    def set_randomness(self, subjects, trial_id):
        id_dict = self.database.get_treatments_id_dict(trial_id)
        seq = [id_dict[subject[1]] for subject in subjects]
        mean = sum(seq) / len(seq)
        seq = list(map(lambda x: 1 if x > mean else 0, seq))
        rt = RunTest(seq, 0, 1)
        z, p = rt.get_p()
        if p == None:
            return
        self.ui.randomnessProgressBar.setValue(p * 100)
        self.ui.randomness_label.setText('Randomness (P = {:.4f})'.format(p))

    def get_trial_balance(self, table, trial_id):
        model = Model()
        m = Minim(random=self.random, model=model)
        levels = [[] for col in table[0]]
        balances = [[] for col in table[0]] + [[], []]
        for row in table:
            for col, value in enumerate(row):
                levels[col].append(value)
        treatments = self.database.read_treatments(trial_id)
        allocation_ratio = [treatment[2] for treatment in treatments]
        for row, level_count in enumerate(levels):
            adj_count = [(1.0 * level_count[i]) / allocation_ratio[i] for i in range(len(level_count))]
            balances[row].append(m.get_marginal_balance(adj_count))
            balances[row].append(max(adj_count) - min(adj_count))
            balances[row].append(m.get_variance(adj_count))
            balances[row].append(m.get_standard_deviation(adj_count))
        return balances

    def import_mnd_file(self):
        filename = qtw.QFileDialog.getOpenFileName(self,
                                                   self.tr('Select mnd file'),
                                                   qtc.QDir.currentPath(),
                                                   'mnd files (*.mnd)')
        if filename is None:
            return
        mnd = Mnd(filename[0])
        if mnd.data_file_valid():
            button = qtw.QMessageBox.question(self, self.tr("Warning"),
                                              mnd.get_detail() + '\n' + self.tr('Do you want to save this trial?'),
                                              qtw.QMessageBox.Yes | qtw.QMessageBox.No)
            if button != qtw.QMessageBox.Yes:
                return
            trial_id = mnd.import_data(self.database)
            self.settings.setValue('last_trial_id', trial_id)
            self.trial = Trial(self.database.get_trial_setting(trial_id))
            if self.ui.tabWidget.currentIndex() != 0:
                self.ui.tabWidget.setCurrentIndex(0)
            else:
                self.load_trials()
            qtw.QMessageBox.information(self,
                                        self.tr('Imported'),
                                        self.tr('''Data imported successfully!''')
                                        )
        else:
            qtw.QMessageBox.information(
                self, self.tr('Import error!'),
                self.tr('Data are not valid!')
            )

    def export_mnd_file(self):
        last_trial_id = self.settings.value('last_trial_id', 0, type=int)
        if last_trial_id == 0:
            return
        filename = qtw.QFileDialog.getSaveFileName(self,
                                                   self.tr('Export to file'),
                                                   qtc.QDir.currentPath(),
                                                   'mnd files (*.mnd)')
        if filename is None:
            return
        filename = filename[0]
        if not filename.endswith('.mnd'):
            filename += '.mnd'
        mnd = Mnd(filename)
        if mnd.export_data(last_trial_id, self.database):
            qtw.QMessageBox.information(self, self.tr('Export'), self.tr('Current trial exported successfully to "{}"').format(filename))


def clickable(widget):
    class Filter(qtc.QObject):
        clicked = qtc.Signal()
        def eventFilter(self, obj, event):
            if obj == widget:
                if event.type() == qtc.QEvent.MouseButtonRelease:
                    if obj.rect().contains(event.pos()):
                        self.clicked.emit()
                        # The developer can opt for .emit(obj) to get the object within the slot.
                        return True
            return False
    filter = Filter(widget)
    widget.installEventFilter(filter)
    return filter.clicked


def start_app():
    try:
        app = qtw.QApplication(sys.argv)
    except RuntimeError:
        app = qtc.QCoreApplication.instance()
    app.setWindowIcon(qtg.QIcon('images/logo.png'))
    settings = qtc.QSettings('net.saghaei', 'minimpy2')
    lang = settings.value('language', 'en_US', type=str)
    if lang != 'en_US':
        translator = qtc.QTranslator()
        translator.load('locales/{}'.format(lang))
        app.installTranslator(translator)
    mw = MainWindow(settings)
    app.mainWindow = mw
    mw.show()
    exit_code = app.exec_()
    return exit_code


if __name__ == '__main__':
    try:
        me = singleton.SingleInstance('me')
    except SingleInstanceException:
        sys.exit(1)
    start_app()