"""
For each filter in Maxim's current filter list, perform one or
more autofocus runs to measure the focus offset required for each filter.
"""

import logging
import os
import sys
import time

import relimport
from iotalib import config, config_observatory, logutil, observatory

# Loop through the filter list this many times
NUM_MEASUREMENTS_PER_FILTER = 3

# List of filter index positions to measure.
# If many filters are known to use essentially the same
# filter position, you can save time by skipping those filters
# and manually editing the config file and duplicating focus
# positions across those filters.
FILTER_INDICES_TO_MEASURE = [0, 1, 8]

# The exposure length to use for each filter.
# This list must contain the same number of elements as FILTER_INDICES_TO_MEASURE.
# It can be useful to specify longer exposure lengths for narrowband filters.
FILTER_EXPOSURE_LENGTHS = [10, 10, 10]

# Template used to generate config file.
FOCUS_OFFSETS_FILE_TEMPLATE = """
# Focus values for each filter.
# This config file is parsed as a Python file, so any valid Python
# syntax (if statements, string formatting, etc) is allowed.

# AUTOGENERATED BY %(script_name)s on %(timestamp)s

# This Python dictionary maps a filter index to a focus value.
# Focus positions can be expressed in absolute or relative units.
# The important part is that the *difference* between any two filter
# measurements is an accurate offset that can be used when moving
# from one filter to the other.
# Note that it may not be valid to mix best-focus values that were
# measured at significantly different times or temperatures since
# the overall focus of the instrument may have changed.

best_focus_value = {
%(focus_values)s
}
"""

# Optionally write values to this config file
CONFIG_FILENAME = "focus_offsets.cfg"


def main():
    logutil.setup_log("measure_filter_focus_offsets.log")

    config_observatory.read()

    observatory.setup_mount()
    observatory.setup_camera()
    observatory.setup_focuser()
    observatory.setup_autofocus()

    best_focus_results = {}  # Map from filter index to a list of best focus values

    filter_names = observatory.camera.get_filter_names()

    for loop_number in range(NUM_MEASUREMENTS_PER_FILTER):
        for filter_index_index in range(len(FILTER_INDICES_TO_MEASURE)):
            filter_index = FILTER_INDICES_TO_MEASURE[filter_index_index]
            exp_length_seconds = FILTER_EXPOSURE_LENGTHS[filter_index_index]

            logging.info(
                "*** Iteration %d of %d, filter %d of %d ***",
                loop_number + 1,
                NUM_MEASUREMENTS_PER_FILTER,
                filter_index_index + 1,
                len(FILTER_INDICES_TO_MEASURE),
            )

            logging.info(
                "Setting filter %d (%s)", filter_index, filter_names[filter_index]
            )
            observatory.camera.set_active_filter(filter_index)

            logging.info("Setting exposure to %s seconds", exp_length_seconds)
            observatory.autofocus.set_exposure_length(exp_length_seconds)

            logging.info("Determining best focus position")
            focus_result = observatory.autofocus.run_autofocus()

            logging.info("Best focus result: %s", focus_result)

            best_focus_values_for_filter = best_focus_results.get(filter_index, [])
            if focus_result is not None:
                best_focus_values_for_filter.append(focus_result)
            best_focus_results[filter_index] = best_focus_values_for_filter

    config_lines = ""

    relative_to_value = None
    for filter_index in FILTER_INDICES_TO_MEASURE:
        filter_name = filter_names[filter_index]
        values = best_focus_results[filter_index]
        average_value = None
        if len(values) > 0:
            average_value = int(round(sum(values) / len(values)))

        if average_value is None:
            average_value_str = "None"
        else:
            average_value_str = "%.2f" % average_value

        values_str = ", ".join(["%.2f" % x for x in values])

        line = "%s: %s, # Filter: %s, Measurements: (%s)" % (
            filter_index,
            average_value_str,
            filter_name,
            values_str,
        )

        config_lines += "    " + line + "\n"

    logging.info("Final results:\n%s", config_lines)

    focus_offsets_cfg_text = FOCUS_OFFSETS_FILE_TEMPLATE % dict(
        script_name=os.path.basename(__file__),
        timestamp=time.ctime(),
        focus_values=config_lines,
    )

    print(focus_offsets_cfg_text)

    config_file_path = config.get_config_path(CONFIG_FILENAME)

    while True:
        response = input("Overwrite %s with new values? (y/n) " % CONFIG_FILENAME)
        response = response.strip().lower()
        if response == "n":
            break
        elif response == "y":
            f = open(config_file_path, "w")
            f.write(focus_offsets_cfg_text)
            f.close()
            logging.info("Configuration written to %s", config_file_path)
            break
        else:
            print("Invalid response")


if __name__ == "__main__":
    main()
