import numpy as np
from scipy import sparse as sp
from .utils import getArrayFromNumericTable, make_HomogenNumericTable
from ..utils import check_array, check_X_y, deprecated, as_float_array
from daal.algorithms.ridge_regression import training, prediction
from sklearn.linear_model.ridge import Ridge

def make2d(X):
    if X.ndim == 0:
        X = X[np.newaxis, np.newaxis]
    elif X.ndim == 1:
        X = X.reshape((X.size, 1))
    return X

def daal_fit(self, X, y):

    X = make2d(X)
    y = make2d(y)
    # Create Numeric Tables for training data and dependent variables
    trainData = make_HomogenNumericTable(X)
    trainDependentVariables = make_HomogenNumericTable(y)

    # Create an algorithm object to train the multiple linear regression model with the normal equations method
    algorithm = training.Batch_Float32NormEqDense() if X.dtype == np.float32 else training.Batch()

    # Pass a training data set and dependent values to the algorithm
    algorithm.input.set(training.data, trainData)
    algorithm.input.set(training.dependentVariables, trainDependentVariables)

    # Build the multiple linear regression model and retrieve the algorithm results
    algorithm.parameter.interceptFlag = self.fit_intercept
    #algorithm.parameter.ridgeParameters = make_HomogenNumericTable(np.array([[self.alpha]]))
    ridge_params = np.asarray(self.alpha, dtype=X.dtype)
    if ridge_params.size != 1 and ridge_params.size != y.shape[1]:
        raise ValueError("alpha length wrong")
    algorithm.parameter.ridgeParameters = make_HomogenNumericTable(ridge_params.reshape((1,ridge_params.size)))
    self.trainingResult_ = algorithm.compute()
    self.coef_ = getArrayFromNumericTable(self.trainingResult_.get(training.model).getBeta())
    if self.fit_intercept == True:
        self.intercept_ = self.coef_[:,0]
        self.coef_ = self.coef_[:,1:]
    if self.coef_.shape[0] == 1:
        self.coef_ = np.ravel(self.coef_)
        self.intercept_ = self.intercept_[0]
    return self


def daal_predict(self, X):
    # Create Numeric Tables for testing data and ground truth values
    X = make2d(X)
    testData = make_HomogenNumericTable(X)
    # Create an algorithm object to predict values of multiple linear regression
    algorithm = prediction.Batch()
    # Pass a testing data set and the trained model to the algorithm
    algorithm.input.setTable(prediction.data, testData)

    algorithm.input.setModel(prediction.model, self.trainingResult_.get(training.model))
    # Predict values of multiple linear regression and retrieve the algorithm results
    predictionResult = algorithm.compute()
    res = getArrayFromNumericTable(predictionResult.get(prediction.prediction))
    if res.shape[1] == 1:
        res = np.ravel(res)
    return res

def fit(self, X, y, sample_weight=None):
    """Fit Ridge regression model

    Parameters
    ----------
    X : {array-like, sparse matrix}, shape = [n_samples, n_features]
        Training data

    y : array-like, shape = [n_samples] or [n_samples, n_targets]
        Target values

    sample_weight : float or numpy array of shape [n_samples]
        Individual weights for each sample

    Returns
    -------
    self : returns an instance of self.
    """
    X, y = check_X_y(X, y, ['csr', 'csc', 'coo'], dtype=[np.float64, np.float32],
            multi_output=True, y_numeric=True)
    self.sample_weight_ = sample_weight
    self.fit_shape_good_for_daal_ = True if X.shape[0] > X.shape[1] else False
    if (not self.solver == 'auto' or
            not self.fit_intercept or
            sp.issparse(X) or
            not self.fit_shape_good_for_daal_ or
            not (X.dtype == np.float64 or X.dtype == np.float32) or
            sample_weight is not None):
        return super(Ridge, self).fit(X, y, sample_weight=sample_weight)
    else:
        self.n_iter_ = None
        return daal_fit(self, X, y)

def predict(self, X):
    """Predict using the linear model

    Parameters
    ----------
    X : {array-like, sparse matrix}, shape = (n_samples, n_features)
        Samples.

    Returns
    -------
    C : array, shape = (n_samples,)
        Returns predicted values.
    """
    X = check_array(X, accept_sparse=['csr', 'csc', 'coo'])
    good_shape_for_daal = True if X.ndim <= 1 else True if X.shape[0] > X.shape[1] else False

    if (not self.solver == 'auto' or
            not self.fit_intercept or
            sp.issparse(X) or
            not good_shape_for_daal or
            not self.fit_shape_good_for_daal_ or
            not (X.dtype == np.float64 or X.dtype == np.float32) or
            (hasattr(self, 'sample_weight_') and self.sample_weight_ is not None)):
        return self._decision_function(X)
    else:
        return daal_predict(self, X)
