"""CSV File Operations.

This module provides useful functions to write csv files.

.. _src-common-file-csv_files:
    https://github.com/hivebattery/gui/blob/master/driver/src/common/file/csv_files.py

"""
from __future__ import with_statement
from __future__ import absolute_import

from os.path import join, exists
from decimal import Decimal
from io import open
import numpy as np
import re

from src.components.form import F_MIN

CURRENT_RANGE_VALS = ['0.00002', '0.0001', '.0006']
"""list of str: All possible current ranging values.
"""


def build_line(arr, sep, delete=True):
    """Creates a line from a list of strings.

    Given a list of tuples made up of a string and an integer representing the max number of characters for that column,
    formats and pads the string so that it can be neatly printed out to the csv file.

    Args:
        arr (list of (int, str)): The list containing column max character limits and strings.
        sep (str): The char that should be used to join each formatted string.
        delete (bool): Whether the tuples should be deleted after they've been used.

    Returns:
        str: A formatted line of csv.

    """
    line = "%s" % sep.join(map(lambda (l, val): "".join([" " for i in range(l - len(val))]) + val, arr))
    if delete:
        del arr[:]

    return line


def write_current_voltage_csv(dir_name, file_name, all_data_raw, freqs_explicit, samples, periods, curr_range,
                              batt_volt, serial_n, start_time, log):
    """Write the entirety of the csv file from the EIS output.

    Before writing the actual file, some preparative steps are needed:
        1) Extract the exact time of retrieval for each frequency.
        2) Determine the actual name of the file to account for existing files.
        3) Open the file to start writing.
        4) Determine the max number of characters for each column.
        5) Build each section of the file, in the following order:
            a) 'Data Type' line
            b) 'Frequencies' line
            c) 'Time from start' line
            d) 'Samples per period' line
            e) 'Per sweep' data
            f) The actual current and voltage data
        6) Write and close the file
        7) Log a success message indicating the file was written correctly.

    Args:
        dir_name (str): The name of the directory where the csv file will be stored.
        file_name (str): The name of the csv file.
        all_data_raw (list of (datetime, list of float)): The current and voltage data generated by EIS, one for each
            frequency requested.
        freqs_explicit (list of str): A list with the explicit, stringified values of the frequencies requested.
        samples (int): The number of samples.
        periods (int): The number of periods.
        curr_range (float): The current ranging returned by the device.
        batt_volt (float): The battery voltage returned by the device.
        serial_n (str): The serial number.
        start_time (datetime): The exact time when the EIS was started.
        log ((list of (str, int)) -> None): Logs messages according to their codes. See `src.main.log_messages`.

    """
    k = samples * periods
    all_data = []
    times = []
    delimiter = ','

    for end_time, data_set in all_data_raw:  #: Step 1
        voltage = data_set[:k]
        current = data_set[k:]

        times.append(end_time)

        all_data.append(["%.7f" % x for x in voltage])
        all_data[-1] += [("%" + ".%if" % (6 - curr_range)) % x for x in current]

    final_name = file_name
    suffix = 0

    while exists(join(dir_name, '%s.csv' % final_name)):  #: Step 2
        if suffix == 0:
            final_name += '0'

        suffix += 1
        final_name = re.sub(r'[0-9]*$', str(suffix), final_name)

    full_path = join(dir_name, '%s.csv' % final_name)  #: Step 3
    f = open(full_path, 'wb')

    n_freqs = len(freqs_explicit)
    per_sweep_data_names = ['File Name', 'Device Serial Number', 'Start Date and Time', 'Number of Frequencies',
                            'Current Resolution (Amps)', 'Battery OCV (Volts)']
    current_voltage_data_str = []

    first_column_l = -float('inf')

    for data_name in per_sweep_data_names:  #: Step 4
        first_column_l = max(first_column_l, len(data_name))

    column_widths = [first_column_l]

    for freq_id in range(len(all_data)):
        column_widths.append(10 if freq_id != 0 else 17)
        column_widths.append(10)

    data_type_line = [(first_column_l, 'Data Type')]  #: Step 5.a

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        data_type_line.append((max(column_l, 10 if freq_id != 0 else 17), 'V'))
        data_type_line.append((max(column_l, 10), 'I'))

    frequencies = [(first_column_l, 'Frequency (Hz)')]  #: Step 5.b

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        freq_explicit = "%.2E" % (Decimal(freqs_explicit[freq_id]) / 1000)
        frequencies.append((max(column_l, 10 if freq_id != 0 else 17), freq_explicit))
        frequencies.append((max(column_l, 10), freq_explicit))

    times_line = [(first_column_l, 'Time from start (s)')]  #: Step 5.c

    for freq_id in range(len(all_data)):
        if freq_id == 0:
            time_diff = 0
        else:
            end_time = times[freq_id - 1]
            column_l = column_widths[(freq_id + 1) * 2]
            time_diff = (end_time - start_time).seconds

        times_line.append((max(column_l, 10 if freq_id != 0 else 17), str(time_diff)))
        times_line.append((max(column_l, 10), str(time_diff)))

    samples_line = [(first_column_l, 'Samples per period')]  #: Step 5.d

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        freq_bit = int(np.round(np.log2(float(freqs_explicit[freq_id]) / F_MIN))) & 23
        freq_explicit = 2 ** (27 - freq_bit) if freq_bit >= 21 else 128

        samples_line.append((max(column_l, 10 if freq_id != 0 else 17), str(freq_explicit)))
        samples_line.append((max(column_l, 10), str(freq_explicit)))

    second_column_l = column_widths[1]

    #: Step 5.e
    per_sweep_data_vals = map(str, ['%s.csv' % file_name, serial_n, '%02i/%02i/%02i %02i:%02i:%02i' %
                                                                    (start_time.month, start_time.day,
                                                                     start_time.year - 2000, start_time.hour,
                                                                     start_time.minute, start_time.second),
                                    n_freqs, CURRENT_RANGE_VALS[curr_range], "%.3f" % batt_volt])

    per_sweep_data = [build_line([(first_column_l, per_sweep_data_names[i]),
                                  (second_column_l, per_sweep_data_vals[i])], delimiter) for i in
                      range(len(per_sweep_data_names))]

    #: Step 5.f
    for i in range(k):
        line = [(first_column_l, str(i + 1))]

        for freq_id in range(len(all_data)):
            column_l = column_widths[(freq_id + 1) * 2]
            voltage = str(all_data[freq_id][i])
            current = str(all_data[freq_id][i + k])

            line.append((max(column_l, 10 if freq_id != 0 else 17), voltage))
            line.append((max(column_l, 10), current))

        current_voltage_data_str.append(build_line(line, delimiter))

    f.write("\n".join(per_sweep_data) + '\n\n')  #: Step 6
    f.write(build_line(data_type_line, delimiter) + '\n')
    f.write(build_line(frequencies, delimiter) + '\n')
    f.write(build_line(times_line, delimiter) + '\n')
    f.write(build_line(samples_line, delimiter) + '\n')
    f.write("\n".join(current_voltage_data_str))
    f.close()

    log([("Successfully wrote file '%s.csv' to path %s." % (final_name, dir_name), 0)])  #: Step 7

ad8f187daa146ce19a660eb91506bbb710bd482a
"""CSV File Operations.

This module provides useful functions to write csv files.

.. _src-common-file-csv_files:
    https://github.com/hivebattery/gui/blob/master/driver/src/common/file/csv_files.py

"""
from __future__ import with_statement
from __future__ import absolute_import

from os.path import join, exists
from decimal import Decimal
from io import open
import numpy as np
import re

from src.components.form import F_MIN

CURRENT_RANGE_VALS = ['0.00002', '0.0001', '.0006']
"""list of str: All possible current ranging values.
"""


def build_line(arr, sep, delete=True):
    """Creates a line from a list of strings.

    Given a list of tuples made up of a string and an integer representing the max number of characters for that column,
    formats and pads the string so that it can be neatly printed out to the csv file.

    Args:
        arr (list of (int, str)): The list containing column max character limits and strings.
        sep (str): The char that should be used to join each formatted string.
        delete (bool): Whether the tuples should be deleted after they've been used.

    Returns:
        str: A formatted line of csv.

    """
    line = "%s" % sep.join(map(lambda (l, val): "".join([" " for i in range(l - len(val))]) + val, arr))
    if delete:
        del arr[:]

    return line


def write_current_voltage_csv(dir_name, file_name, all_data_raw, freqs_explicit, samples, periods, curr_range,
                              batt_volt, serial_n, start_time, log):
    """Write the entirety of the csv file from the EIS output.

    Before writing the actual file, some preparative steps are needed:
        1) Extract the exact time of retrieval for each frequency.
        2) Determine the actual name of the file to account for existing files.
        3) Open the file to start writing.
        4) Determine the max number of characters for each column.
        5) Build each section of the file, in the following order:
            a) 'Data Type' line
            b) 'Frequencies' line
            c) 'Time from start' line
            d) 'Samples per period' line
            e) 'Per sweep' data
            f) The actual current and voltage data
        6) Write and close the file
        7) Log a success message indicating the file was written correctly.

    Args:
        dir_name (str): The name of the directory where the csv file will be stored.
        file_name (str): The name of the csv file.
        all_data_raw (list of (datetime, list of float)): The current and voltage data generated by EIS, one for each
            frequency requested.
        freqs_explicit (list of str): A list with the explicit, stringified values of the frequencies requested.
        samples (int): The number of samples.
        periods (int): The number of periods.
        curr_range (float): The current ranging returned by the device.
        batt_volt (float): The battery voltage returned by the device.
        serial_n (str): The serial number.
        start_time (datetime): The exact time when the EIS was started.
        log ((list of (str, int)) -> None): Logs messages according to their codes. See `src.main.log_messages`.

    """
    k = samples * periods
    all_data = []
    times = []
    delimiter = ','

    for end_time, data_set in all_data_raw:  #: Step 1
        voltage = data_set[:k]
        current = data_set[k:]

        times.append(end_time)

        all_data.append(["%.7f" % x for x in voltage])
        all_data[-1] += [("%" + ".%if" % (6 - curr_range)) % x for x in current]

    final_name = file_name
    suffix = 0

    while exists(join(dir_name, '%s.csv' % final_name)):  #: Step 2
        if suffix == 0:
            final_name += '0'

        suffix += 1
        final_name = re.sub(r'[0-9]*$', str(suffix), final_name)

    full_path = join(dir_name, '%s.csv' % final_name)  #: Step 3
    f = open(full_path, 'wb')

    n_freqs = len(freqs_explicit)
    per_sweep_data_names = ['File Name', 'Device Serial Number', 'Start Date and Time', 'Number of Frequencies',
                            'Current Resolution (Amps)', 'Battery OCV (Volts)']
    current_voltage_data_str = []

    first_column_l = -float('inf')

    for data_name in per_sweep_data_names:  #: Step 4
        first_column_l = max(first_column_l, len(data_name))

    column_widths = [first_column_l]

    for freq_id in range(len(all_data)):
        column_widths.append(10 if freq_id != 0 else 17)
        column_widths.append(10)

    data_type_line = [(first_column_l, 'Data Type')]  #: Step 5.a

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        data_type_line.append((max(column_l, 10 if freq_id != 0 else 17), 'V'))
        data_type_line.append((max(column_l, 10), 'I'))

    frequencies = [(first_column_l, 'Frequency (Hz)')]  #: Step 5.b

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        freq_explicit = "%.2E" % (Decimal(freqs_explicit[freq_id]) / 1000)
        frequencies.append((max(column_l, 10 if freq_id != 0 else 17), freq_explicit))
        frequencies.append((max(column_l, 10), freq_explicit))

    times_line = [(first_column_l, 'Time from start (s)')]  #: Step 5.c

    for freq_id in range(len(all_data)):
        if freq_id == 0:
            time_diff = 0
        else:
            end_time = times[freq_id - 1]
            column_l = column_widths[(freq_id + 1) * 2]
            time_diff = (end_time - start_time).seconds

        times_line.append((max(column_l, 10 if freq_id != 0 else 17), str(time_diff)))
        times_line.append((max(column_l, 10), str(time_diff)))

    samples_line = [(first_column_l, 'Samples per period')]  #: Step 5.d

    for freq_id in range(len(all_data)):
        column_l = column_widths[(freq_id + 1) * 2]
        freq_bit = int(np.round(np.log2(float(freqs_explicit[freq_id]) / F_MIN))) & 23
        freq_explicit = 2 ** (27 - freq_bit) if freq_bit >= 21 else 128

        samples_line.append((max(column_l, 10 if freq_id != 0 else 17), str(freq_explicit)))
        samples_line.append((max(column_l, 10), str(freq_explicit)))

    second_column_l = column_widths[1]

    #: Step 5.e
    per_sweep_data_vals = map(str, ['%s.csv' % file_name, serial_n, '%02i/%02i/%02i %02i:%02i:%02i' %
                                                                    (start_time.month, start_time.day,
                                                                     start_time.year - 2000, start_time.hour,
                                                                     start_time.minute, start_time.second),
                                    n_freqs, CURRENT_RANGE_VALS[curr_range], "%.3f" % batt_volt])

    per_sweep_data = [build_line([(first_column_l, per_sweep_data_names[i]),
                                  (second_column_l, per_sweep_data_vals[i])], delimiter) for i in
                      range(len(per_sweep_data_names))]

    #: Step 5.f
    for i in range(k):
        line = [(first_column_l, str(i + 1))]

        for freq_id in range(len(all_data)):
            column_l = column_widths[(freq_id + 1) * 2]
            voltage = str(all_data[freq_id][i])
            current = str(all_data[freq_id][i + k])

            line.append((max(column_l, 10 if freq_id != 0 else 17), voltage))
            line.append((max(column_l, 10), current))

        current_voltage_data_str.append(build_line(line, delimiter))

    f.write("\n".join(per_sweep_data) + '\n\n')  #: Step 6
    f.write(build_line(data_type_line, delimiter) + '\n')
    f.write(build_line(frequencies, delimiter) + '\n')
    f.write(build_line(times_line, delimiter) + '\n')
    f.write(build_line(samples_line, delimiter) + '\n')
    f.write("\n".join(current_voltage_data_str))
    f.close()

    log([("Successfully wrote file '%s.csv' to path %s." % (final_name, dir_name), 0)])  #: Step 7
