"""
@package pmsco.calculators.calculator
abstract scattering program interface.

this module declares the basic interface to scattering programs.
for each scattering program (EDAC, MSC, SSC, ...) a specific interface must be derived from CalcInterface.
the derived interface must implement the run() method.
the run() method and the scattering code may use only the parameters declared in the interface.

TestCalcInterface is provided for testing the PMSCO code quickly without calling an external program.

@author Matthias Muntwiler

@copyright (c) 2015-19 by Paul Scherrer Institut @n
Licensed under the Apache License, Version 2.0 (the "License"); @n
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
  http://www.apache.org/licenses/LICENSE-2.0
"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import time
import numpy as np

import pmsco.data as md

__author__ = 'matthias muntwiler'


class Calculator(object):
    """
    Interface class to the calculation program.
    """
    def run(self, params, cluster, scan, output_file):
        """
        run a calculation with the given parameters and cluster.

        the result is returned as the method result and in a file named <code>output_file + '.etpi'</code>,
        or <code>output_file + '.etpai'</code> depending on scan mode.
        all other intermediate files are deleted unless keep_temp_files is True.

        @param params: a pmsco.project.CalculatorParams object with all necessary values except cluster and output files set.

        @param cluster: a pmsco.cluster.Cluster(format=FMT_EDAC) object with all atom positions set.

        @param scan: a pmsco.project.Scan() object describing the experimental scanning scheme.

        @param output_file: base name for all intermediate and output files

        @return: (str, dict) result_file, and dictionary of created files {filename: category}

        @return: (str, dict) result_file, and dictionary of created files.
            @arg the first element is the name of the main ETPI or ETPAI result file to be further processed.
            @arg the second element is a dictionary that lists the names of all created data files with their category.
                 the dictionary key is the file name,
                 the value is the file category (cluster, atomic, etc.).
        """
        return None, None


class AtomicCalculator(Calculator):
    """
    abstract interface class to the atomic scattering calculation program.
    """
    pass


class InternalAtomicCalculator(AtomicCalculator):
    """
    dummy atomic scattering class if scattering factors are calculated internally by the multiple scattering calculator.
    """
    pass


class TestCalculator(Calculator):
    """
    interface class producing random data for testing the MSCO code without calling an external program.
    """
    def run(self, params, cluster, scan, output_file):
        """
        produce a random test data set.

        the scan scheme is generated from the given parameters.
        the intensities are random values.

        @return: result_file, files_cats
            the result file contains an ETPI or ETPAI array with random intensity data.
        """

        # set up scan
        params.fixed_cluster = 'a' in scan.mode

        # generate file names
        base_filename = output_file
        clu_filename = base_filename + ".clu"
        if params.fixed_cluster:
            etpi_filename = base_filename + ".etpai"
        else:
            etpi_filename = base_filename + ".etpi"

        cluster.save_to_file(clu_filename)

        # generate data and save in ETPI or ETPAI format
        result_etpi = scan.raw_data.copy()
        result_etpi['i'] = np.random.random_sample(result_etpi.shape)

        # slow down the test for debugging
        time.sleep(5)

        md.save_data(etpi_filename, result_etpi)

        files = {clu_filename: 'cluster', etpi_filename: 'region'}
        return etpi_filename, files
