# ============================================================================== #
    # create_table_for_save_as.py - AcouSed                                   #
    # Copyright (C) 2024  INRAE                                              #
    #                                                                        #
    # 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, see <https://www.gnu.org/licenses/>. #

    #                                                      by Brahim MOUDJED #
# ============================================================================== #

# -*- coding: utf-8 -*-

import os
import time
import sqlite3
import logging

import numpy as np

from PyQt5.QtWidgets import QFileDialog, QApplication, QMessageBox

import settings as stg
from settings import ABS_name

logger = logging.getLogger()

class CreateTableForSaveAs:

    def __init__(self):
        self.create_AcousticFile = """
        CREATE TABLE AcousticFile(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          acoustic_data INTEGER,
          acoustic_file STRING,
          ABS_name STRING,
          path_BS_noise_data STRING,
          filename_BS_noise_data STRING,
          noise_method FLOAT,
          noise_value FLOAT,
          data_preprocessed STRING
        )
        """

        self.create_Measure = """
        CREATE TABLE Measure(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          acoustic_data INTEGER,
          Date DATE,
          Hour TIME,
          frequency FLOAT,
          sound_attenuation FLOAT,
          kt_read FLOAT,
          kt_corrected FLOAT,
          NbProfiles FLOAT,
          NbProfilesPerSeconds FLOAT,
          NbCells FLOAT,
          CellSize FLOAT,
          PulseLength FLOAT,
          NbPingsPerSeconds FLOAT,
          NbPingsAveragedPerProfile FLOAT,
          GainRx FLOAT,
          GainTx FLOAT
        )
        """

        self.create_BSRawData = """
        CREATE TABLE BSRawData(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          acoustic_data INTEGER,
          time BLOB, depth BLOB, BS_raw_data BLOB,
          time_reshape BLOB, depth_reshape BLOB, BS_raw_data_reshape BLOB,
          time_cross_section BLOB, depth_cross_section BLOB,
          BS_cross_section BLOB, BS_stream_bed BLO  B,
          depth_bottom, val_bottom, ind_bottom,
          time_noise BLOB, depth_noise BLOB, BS_noise_raw_data BLOB,
          SNR_raw_data BLOB, SNR_cross_section BLOB, SNR_stream_bed BLOB,
          BS_raw_data_pre_process_SNR BLOB,
          BS_raw_data_pre_process_average BLOB,
          BS_cross_section_pre_process_SNR BLOB,
          BS_cross_section_pre_process_average BLOB,
          BS_stream_bed_pre_process_SNR BLOB,
          BS_stream_bed_pre_process_average BLOB,
          BS_mean BLOB
        )
        """

        self.create_Settings = """
        CREATE TABLE Settings(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          acoustic_data INTEGER,
          temperature FLOAT,
          tmin_index FLOAT, tmin_value FLOAT,
          tmax_index FLOAT, tmax_value FLOAT,
          rmin_index FLOAT, rmin_value FLOAT,
          rmax_index FLOAT, rmax_value FLOAT,
          freq_bottom_detection_index FLOAT,
          freq_bottom_detection_value STRING,
          SNR_filter_value FLOAT, Nb_cells_to_average_BS_signal FLOAT
        )
        """

        self.create_SedimentsFile = """
        CREATE TABLE SedimentsFile(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          path_fine STRING,
          filename_fine STRING,
          radius_grain_fine BLOB,
          path_sand STRING,
          filename_sand STRING,
          radius_grain_sand BLOB,
          time_column_label STRING,
          distance_from_bank_column_label STRING,
          depth_column_label STRING,
          Ctot_fine_column_label STRING,
          D50_fine_column_label STRING,
          Ctot_sand_column_label STRING,
          D50_sand_column_label STRING
        )
        """

        self.create_SedimentsData = """
        CREATE TABLE SedimentsData(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          sample_fine_name STRING,
          sample_fine_index INTEGER,
          distance_from_bank_fine FLOAT,
          depth_fine FLOAT,
          time_fine FLOAT,
          Ctot_fine FLOAT,
          Ctot_fine_per_cent FLOAT,
          D50_fine FLOAT,
          frac_vol_fine BLOB,
          frac_vol_fine_cumul BLOB,
          sample_sand_name STRING,
          sample_sand_index INTEGER,
          distance_from_bank_sand FLOAT,
          depth_sand FLOAT,
          time_sand FLOAT,
          Ctot_sand FLOAT,
          Ctot_sand_per_cent FLOAT,
          D50_sand FLOAT,
          frac_vol_sand BLOB,
          frac_vol_sand_cumul BLOB
        )
        """

        self.create_Calibration = """
        CREATE TABLE Calibration(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          path_calibration_file STRING,
          filename_calibration_file STRING,
          range_lin_interp BLOB,
          M_profile_fine BLOB,
          ks BLOB,
          sv BLOB,
          X_exponent BLOB,
          alpha_s BLOB,
          zeta BLOB,
          FCB BLOB,
          depth_real BLOB,
          lin_reg BLOB
        )
        """

        self.create_Inversion = """
        CREATE TABLE Inversion(
          ID INTEGER PRIMARY KEY AUTOINCREMENT,
          J_cross_section_freq1 BLOB,
          J_cross_section_freq2 BLOB,
          VBI_cross_section BLOB,
          SSC_fine BLOB,
          SSC_sand BLOB
        )
        """

        self.open_file_dialog()


    def open_file_dialog(self):
        name, _ = QFileDialog.getSaveFileName(
            caption="Save As",
            directory="",
            filter="AcouSed Files (*.acd)",
            options=QFileDialog.DontUseNativeDialog
        )

        if name != "":
            filename = os.path.basename(name)
            if os.path.splitext(filename)[1] != ".acd":
                filename += ".acd"

            logger.debug(f"selected save file: '{filename}'")

            stg.dirname_save_as = os.path.dirname(name)
            stg.filename_save_as = filename

            try:
                os.chdir(stg.dirname_save_as)
            except OSError as e:
                logger.warning(f"chdir: {str(e)}")

            start = time.time()
            self.create_table()
            print(f"end : {time.time() - start} sec")
        else:
            msgBox = QMessageBox()
            msgBox.setWindowTitle("Save Error")
            msgBox.setIcon(QMessageBox.Warning)
            msgBox.setText("No file saved")
            msgBox.setStandardButtons(QMessageBox.Ok)
            msgBox.exec()

    def create_table(self):
        cnx = sqlite3.connect(stg.filename_save_as)
        cur = cnx.cursor()

        self.create_table_acoustic_file(cnx, cur)
        self.create_table_measure(cnx, cur)
        self.create_table_BSRawData(cnx, cur)
        self.create_table_settings(cnx, cur)
        self.create_table_sediments_file(cnx, cur)
        self.create_table_sediments_data(cnx, cur)
        self.create_table_calibration(cnx, cur)
        self.create_table_inversion(cnx, cur)

        cnx.commit()

        cur.close()
        cnx.close()

    def create_table_acoustic_file(self, cnx, cur):
        start_table_File = time.time()

        cur.execute("DROP TABLE if exists AcousticFile")

        cur.execute(self.create_AcousticFile)

        for i in stg.acoustic_data:
            logger.debug(f"stg.acoustic_data: {stg.acoustic_data[i]}")
            logger.debug("stg.filename_BS_raw_data: "
                         + f"{stg.filename_BS_raw_data[i]}")
            logger.debug(f"stg.ABS_name: {stg.ABS_name}")
            logger.debug(f"stg.path_BS_raw_data: {stg.path_BS_raw_data[i]}")

            cur.execute(
                """
                INSERT into AcousticFile(
                  acoustic_data,
                  acoustic_file,
                  ABS_name,
                  path_BS_noise_data,
                  filename_BS_noise_data,
                  noise_method,
                  noise_value,
                  data_preprocessed)
                VALUES(?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.acoustic_data[i],
                    stg.filename_BS_raw_data[i].split('.')[0],
                    stg.ABS_name[i],
                    stg.path_BS_noise_data[i],
                    stg.filename_BS_noise_data[i],
                    stg.noise_method[i],
                    stg.noise_value[i],
                    stg.data_preprocessed[i]
                )
            )

        cnx.commit()

        logger.info(f"table File : {time.time() - start_table_File} sec")

    def create_table_measure(self, cnx, cur):
        start_table_Measure = time.time()

        # Drop Table if exists
        cur.execute("DROP TABLE if exists Measure")

        # Execute the CREATE TABLE statement
        cur.execute(self.create_Measure)

        logger.debug(f"stg.date: {stg.date}, stg.hour: {stg.hour}")

        for i in stg.acoustic_data:
            for j in range(stg.freq[i].shape[0]):
                cur.execute(
                    """
                    INSERT into Measure(
                      acoustic_data,
                      Date, Hour,
                      frequency,
                      sound_attenuation,
                      kt_read, kt_corrected,
                      NbProfiles, NbProfilesPerSeconds,
                      NbCells, CellSize,
                      PulseLength,
                      NbPingsPerSeconds,
                      NbPingsAveragedPerProfile,
                      GainRx, GainTx
                    )
                    VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                    """,
                    (
                        stg.acoustic_data[i], #stg.date[i], stg.hour[i],
                        str(stg.date[i].year) + str('-')
                        + str(stg.date[i].month) + str('-')
                        + str(stg.date[i].day),
                        str(stg.hour[i].hour) + str(':') + str(stg.hour[i].minute),
                        stg.freq[i][j],
                        stg.water_attenuation[i][j],
                        stg.kt_read[j], stg.kt_corrected[j],
                        stg.nb_profiles[i][j], stg.nb_profiles_per_sec[i][j],
                        stg.nb_cells[i][j], stg.cell_size[i][j],
                        stg.pulse_length[i][j],
                        stg.nb_pings_per_sec[i][j],
                        stg.nb_pings_averaged_per_profile[i][j],
                        stg.gain_rx[i][j], stg.gain_tx[i][j]
                    )
                )

        # Commit the transaction after executing INSERT.
        cnx.commit()

        logger.info(f"table Measure : {time.time() - start_table_Measure} sec")

    def create_table_BSRawData(self, cnx, cur):
        start_table_BSRawData = time.time()

        cur.execute('DROP TABLE if exists BSRawData')

        # Drop Table BSRawData_i if exists
        cur.execute(self.create_BSRawData)

        for i in stg.acoustic_data:
            cur.execute(
                """
                INSERT into BSRawData(
                  acoustic_data,
                  time, depth,
                  BS_raw_data,
                  time_reshape,
                  depth_reshape,
                  BS_raw_data_reshape,
                  time_cross_section, depth_cross_section,
                  BS_cross_section, BS_stream_bed,
                  depth_bottom, val_bottom, ind_bottom,
                  time_noise, depth_noise, BS_noise_raw_data,
                  SNR_raw_data, SNR_cross_section, SNR_stream_bed,
                  BS_raw_data_pre_process_SNR, BS_raw_data_pre_process_average,
                  BS_cross_section_pre_process_SNR, BS_cross_section_pre_process_average,
                  BS_stream_bed_pre_process_SNR, BS_stream_bed_pre_process_average,
                  BS_mean
                )
                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
                       ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.acoustic_data[i], stg.time[i].tobytes(),
                    stg.depth[i].tobytes(), stg.BS_raw_data[i].tobytes(),
                    stg.time_reshape[i].tobytes(), stg.depth_reshape[i].tobytes(),
                    stg.BS_raw_data_reshape[i].tobytes(),
                    stg.time_cross_section[i].tobytes(),
                    stg.depth_cross_section[i].tobytes(),
                    stg.BS_cross_section[i].tobytes(), stg.BS_stream_bed[i].tobytes(),
                    stg.depth_bottom[i].tobytes(), np.array(stg.val_bottom[i]).tobytes(),
                    np.array(stg.ind_bottom[i]).tobytes(),
                    stg.time_noise[i].tobytes(), stg.depth_noise[i].tobytes(),
                    stg.BS_noise_raw_data[i].tobytes(),
                    stg.SNR_raw_data[i].tobytes(), stg.SNR_cross_section[i].tobytes(),
                    stg.SNR_stream_bed[i].tobytes(),
                    stg.BS_raw_data_pre_process_SNR[i].tobytes(),
                    stg.BS_raw_data_pre_process_average[i].tobytes(),
                    stg.BS_cross_section_pre_process_SNR[i].tobytes(),
                    stg.BS_cross_section_pre_process_average[i].tobytes(),
                    stg.BS_stream_bed_pre_process_SNR[i].tobytes(),
                    stg.BS_stream_bed_pre_process_average[i].tobytes(),
                    stg.BS_mean[i].tobytes()
                )
            )

            logger.debug(f"stg.ind_bottom: {stg.ind_bottom[i]}")
            logger.debug(np.array([stg.ind_bottom[i]]),
                         np.array(stg.ind_bottom[i]).shape)

        # Commit the transaction after executing INSERT.
        cnx.commit()

        logger.info(f"table BSRawData : {time.time() - start_table_BSRawData} sec")

    def create_table_settings(self, cnx, cur):
        start_table_Settings = time.time()

        cur.execute("DROP TABLE if exists Settings")

        cur.execute(self.create_Settings)

        logger.debug(f"acoustic_data: {stg.acoustic_data}")
        logger.debug(f"temperature: {stg.temperature}")
        logger.debug(f"rmin: {stg.rmin}, rmax: {stg.rmax}")
        logger.debug(f"tmin: {stg.tmin}, tmax: {stg.tmax}")

        for i in stg.acoustic_data:
            cur.execute(
                """
                INSERT into Settings(
                  acoustic_data, temperature,
                  tmin_index, tmin_value, tmax_index, tmax_value,
                  rmin_index, rmin_value, rmax_index, rmax_value,
                  freq_bottom_detection_index, freq_bottom_detection_value,
                  SNR_filter_value, Nb_cells_to_average_BS_signal
                )
                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.acoustic_data[i], stg.temperature,
                    stg.tmin[i][0], stg.tmin[i][1],
                    stg.tmax[i][0], stg.tmax[i][1],
                    stg.rmin[i][0], stg.rmin[i][1],
                    stg.rmax[i][0], stg.rmax[i][1],
                    stg.freq_bottom_detection[i][0],
                    stg.freq_bottom_detection[i][1],
                    stg.SNR_filter_value[i],
                    stg.Nb_cells_to_average_BS_signal[i]
                )
            )

        cnx.commit()

        logger.info(f"table Settings : {time.time() - start_table_Settings} sec")

    def create_table_sediments_file(self, cnx, cur):
        start_table_SedimentsFile = time.time()

        cur.execute("DROP TABLE if exists SedimentsFile")

        cur.execute(self.create_SedimentsFile)

        if stg.path_fine != "" and path_sand != "":
            cur.execute(
                """
                INSERT into SedimentsFile(
                  path_fine, filename_fine, radius_grain_fine,
                  path_sand, filename_sand, radius_grain_sand,
                  time_column_label, distance_from_bank_column_label,
                  depth_column_label, Ctot_fine_column_label,
                  D50_fine_column_label,
                  Ctot_sand_column_label, D50_sand_column_label
                )
                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.path_fine, stg.filename_fine,
                    stg.radius_grain_fine.tobytes(),
                    stg.path_sand, stg.filename_sand,
                    stg.radius_grain_sand.tobytes(),
                    stg.columns_fine[0], stg.columns_fine[1],
                    stg.columns_fine[2], stg.columns_fine[3],
                    stg.columns_fine[4],
                    stg.columns_sand[3], stg.columns_sand[4]
                )
            )

        cnx.commit()

        logger.info(f"table SedimentsFile : {time.time() - start_table_SedimentsFile} sec")


    def create_table_sediments_data(self, cnx, cur):
        start_table_SedimentsData = time.time()

        cur.execute("DROP TABLE if exists SedimentsData")

        cur.execute(self.create_SedimentsData)

        for f in range(len(stg.sample_fine)):
            cur.execute(
                """
                INSERT into SedimentsData(
                  sample_fine_name, sample_fine_index,
                  distance_from_bank_fine,
                  depth_fine, time_fine, Ctot_fine,
                  Ctot_fine_per_cent, D50_fine,
                  frac_vol_fine, frac_vol_fine_cumul,
                  sample_sand_name, sample_sand_index,
                  distance_from_bank_sand,
                  depth_sand, time_sand, Ctot_sand,
                  Ctot_sand_per_cent, D50_sand,
                  frac_vol_sand, frac_vol_sand_cumul
                )
                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
                       ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.sample_fine[f][0] , stg.sample_fine[f][1],
                    stg.distance_from_bank_fine[f], stg.depth_fine[f],
                    stg.time_fine[f], stg.Ctot_fine[f],
                    stg.Ctot_fine_per_cent[f], stg.D50_fine[f],
                    stg.frac_vol_fine[f].tobytes(),
                    stg.frac_vol_fine_cumul[f].tobytes(),
                    stg.sample_sand[f][0], stg.sample_sand[f][1],
                    stg.distance_from_bank_sand[f], stg.depth_sand[f],
                    stg.time_sand[f], stg.Ctot_sand[f],
                    stg.Ctot_sand_per_cent[f], stg.D50_sand[f],
                    stg.frac_vol_sand[f].tobytes(),
                    stg.frac_vol_sand_cumul[f].tobytes()
                )
            )

        cnx.commit()

        logger.info(f"table SedimentsData : {time.time() - start_table_SedimentsData} sec")

    def create_table_calibration(self, cnx, cur):
        start_table_Calibration = time.time()

        cur.execute("DROP TABLE if exists Calibration")

        cur.execute(self.create_Calibration)

        if len(stg.range_lin_interp) != 0:
            cur.execute(
                """
                INSERT into Calibration(
                  path_calibration_file, filename_calibration_file,
                  range_lin_interp, M_profile_fine,
                  ks, sv, X_exponent, alpha_s, zeta,
                  FCB, depth_real, lin_reg
                )
                VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                """,
                (
                    stg.path_calibration_file, stg.filename_calibration_file,
                    stg.range_lin_interp.tobytes(),
                    stg.M_profile_fine.tobytes(),
                    np.array(stg.ks).tobytes(), np.array(stg.sv).tobytes(),
                    np.array(stg.X_exponent).tobytes(),
                    np.array(stg.alpha_s).tobytes(),
                    np.array(stg.zeta).tobytes(),
                    stg.FCB.tobytes(), stg.depth_real.tobytes(),
                    np.array(stg.lin_reg).tobytes()
                )
            )

        cnx.commit()

        logger.info(f"table Calibration : {time.time() - start_table_Calibration} sec")

    def create_table_inversion(self, cnx, cur):
        start_table_Inversion = time.time()

        cur.execute("DROP TABLE if exists Inversion")

        cur.execute(self.create_Inversion)

        for i in range(len(stg.SSC_fine)):
            cur.execute(
                """
                INSERT into Inversion(
                  J_cross_section_freq1, J_cross_section_freq2,
                  VBI_cross_section, SSC_fine, SSC_sand
                )
                VALUES(?, ?, ?, ?, ?)
                """,
                (
                    stg.J_cross_section[i][0].tobytes(),
                    stg.J_cross_section[i][1].tobytes(),
                    stg.VBI_cross_section[i].tobytes(),
                    stg.SSC_fine[i].tobytes(),
                    stg.SSC_sand[i].tobytes()
                )
            )

        cnx.commit()

        logger.info(f"table Inversion : {time.time() - start_table_Inversion} sec")
