from __future__ import absolute_import

import numpy as nm
import numpy.linalg as nla

from sfepy.base.base import output, get_default, pause, Struct
from sfepy.base.log import Log, get_logging_conf
from sfepy.base.timing import Timer
from sfepy.solvers.solvers import OptimizationSolver

import scipy.optimize as sopt
import scipy.optimize.linesearch as linesearch
import six
from six.moves import range

def conv_test(conf, it, of, of0, ofg_norm=None):
    """
    Returns
    -------
    flag : int
        * -1 ... continue
        *  0 ... small OF -> stop
        *  1 ... i_max reached -> stop
        *  2 ... small OFG -> stop
        *  3 ... small relative decrase of OF
     """

    status = -1
    output('opt: iter: %d, of: %e (||ofg||: %e)' % (it, of, ofg_norm))

    if (abs(of) < conf.eps_of):
        status = 0
    elif ofg_norm and (ofg_norm < conf.eps_ofg):
        status = 2
    elif (it > 0) and (abs(of0 - of) < (conf.eps_rd * abs(of0))):
        status = 3

    if (status == -1) and (it >= conf.i_max):
        status = 1

    return status

def wrap_function(function, args):
    ncalls = [0]
    times = []
    timer = Timer()
    def function_wrapper(x):
        ncalls[0] += 1
        timer.start()
        out = function(x, *args)
        times.append(timer.stop())
        return out
    return ncalls, times, function_wrapper

def check_gradient(xit, aofg, fn_of, delta, check):

    dofg = nm.zeros_like(aofg)
    xd = xit.copy()
    for ii in range(xit.shape[0]):
        xd[ii] = xit[ii] + delta
        ofp = fn_of(xd)

        xd[ii] = xit[ii] - delta
        ofm = fn_of(xd)

        xd[ii] = xit[ii]

        dofg[ii] = 0.5 * (ofp - ofm) / delta

        output('**********', ii, aofg[ii], dofg[ii])

    diff = abs(aofg - dofg)
    aux = nm.concatenate((aofg[:,nm.newaxis], dofg[:,nm.newaxis],
                          diff[:,nm.newaxis]), 1)
    
output(aux)