#!/usr/bin/python3
# -*- coding: utf-8 -*-

from __future__ import print_function
from math import pi as PI
from math import sqrt
from scipy import optimize
import numpy as np


class FitOnChoppers(object):
    class Parameter:
        def __init__(self, value):
            self.value = value
            self.init_value = value
            self.value_err = 0.0

        def set(self, value):
            self.value = value

        def __call__(self):
            return self.value

    def fit(self, function, parameters, y, x=None):
        def f(params):
            i = 0
            for p in parameters:
                p.set(params[i])
                i += 1

            return (y - function(x))

        if x is None:
            x = arange(y.shape[0])
        p = [param() for param in parameters]
        result = optimize.leastsq(f, p, full_output=1)

        p = [param() for param in parameters]
        print("-------")
        chisq = sum(f(p)**2)
        print("Chi_sq=", chisq)

        print("-----")
        # [inamura 170306]-->
        # err = sqrt( diag(result[1]) )
        s_sq = chisq / (len(y) - len(parameters))
        err = np.sqrt(np.diag(result[1] * s_sq))
        # <--[inamura 170306]
        i = 0
        for param in parameters:
            param.value_err = err[i]
            print("Param[%2d]  -Initial=%12.8f -Final=%12.8f +-%12.8f" %
                  (i, param.init_value, param(), err[i]))
            i += 1

        print("-------")

        return chisq


'''
Constant value and Unit convert using on Chopper group
'''


class UnitOnChoppers(object):
    '''

    '''

    def __init__(self, filename=None):
        '''
        Constructor
        @param filename (String) New table of Constant values to be imported.
        '''

        self.Const = {}
        self.CUnit = {}

        self.Const['Mass'] = 1.67495e-27
        self.CUnit['Mass'] = "kg"

        self.Const['Planck'] = 6.626068e-34
        self.CUnit['Planck'] = "J*s"

        self.Const['hbar'] = self.Const['Planck'] / (2 * PI)
        self.CUnit['hbar'] = "J*s"

        self.Const['Boltzmann'] = 1.3806503e-23
        self.CUnit['Boltzmann'] = "J/K"

        self.Const['BohrMag'] = 9.27400949e-24
        self.CUnit['BohrMag'] = "J/T"

        if filename is not None:
            self.SetConstTable(filename)

    def ToLamda(self, invalue, inunit):
        '''
        Convert input value to Lambda [Ang] of neutrons
        @param self
        @param invalue (Double) value to be converted
        @param inunit (String) unit of invalue
        @retval tuple (Double,String) Lambda and unit
        '''
        if inunit == "meV":
            ret = 9.045 / sqrt(invalue)
        elif inunit == "1/Ang":
            ret = 6.283 / invalue
        elif inunit == "K":
            ret = 30.81 / sqrt(invalue)
        elif inunit == "Ang":
            ret = invalue
        elif inunit == "m/s":
            ret = (3.956e-3) / invalue
        else:
            print("Invalid Unit {}".format(inunit))
            print("You can use [meV],[1/Ang],[K],[Ang] and [m/s].")
            ret = None

        return (ret, "Ang")

    def ToEnergy(self, invalue, inunit):
        '''
        Convert input value to Energy [meV] of neutrons
        @param self
        @param invalue (Double) value to be converted
        @param inunit (String) unit of invalue
        @retval tuple (Double,String) Energy and unit
        '''

        if inunit == "meV":
            ret = invalue
        elif inunit == "1/Ang":
            ret = 2.072 * (invalue**2)
        elif inunit == "K":
            ret = 0.08617 * invalue
        elif inunit == "Ang":
            ret = 81.81 / (invalue**2)
        elif inunit == "m/s":
            ret = (5.227e-6) * (invalue**2)
        else:
            print("Invalid Unit {}".format(inunit))
            print("You can use [meV],[1/Ang],[K],[Ang] and [m/s].")
            ret = None

        return (ret, "meV")

    def ToVelocity(self, invalue, inunit):
        '''
        Convert input value to velocity [m/s] of neutrons
        @param self
        @param invalue (Double) value to be converted
        @param inunit (String) unit of invalue
        @retval tuple (Double,"m/s") velocity and unit
        '''

        if inunit == "meV":
            ret = (0.4374e+3) * (sqrt(invalue))
        elif inunit == "1/Ang":
            ret = (0.6296e+3) * (invalue)
        elif inunit == "K":
            ret = (0.1284e+3) * (sqrt(invalue))
        elif inunit == "Ang":
            ret = (3.956e+3) / (invalue)
        elif inunit == "m/s":
            ret = invalue
        else:
            print("Invalid Unit {}".format(inunit))
            print("You can use [meV],[1/Ang],[K],[Ang] and [m/s].")
            ret = None

        return (ret, "m/s")
