# -*- coding: utf-8 -*-

from __future__ import division, print_function

import glog as log
import numpy as np
import scipy
from scipy import ndimage

from .initialize import *
from .propagate import *


def compute_cost(AL, Y):
    """
    Implement the cost function.

    Arguments:
    AL -- probability vector corresponding to your label predictions, shape (1, number of examples)
    Y -- true "label" vector, shape (1, number of examples)

    Returns:
    cost -- cross-entropy cost
    """

    m = Y.shape[1]

    # Compute loss from aL and y.
    cost = (1./m) * (-np.dot(Y,np.log(AL).T) - np.dot(1-Y, np.log(1-AL).T))

    # To make sure your cost's shape is what we expect
    # (e.g. this turns [[17]] into 17).
    cost = np.squeeze(cost)
    assert(cost.shape == ())

    return cost


def update_parameters(parameters, grads, learning_rate):
    """
    Update parameters using gradient descent

    Arguments:
    parameters -- python dictionary containing your parameters
    grads -- python dictionary containing your gradients, output of propagate.backward

    Returns:
    parameters -- python dictionary containing your updated parameters
                  parameters["W" + str(l)] = ...
                  parameters["b" + str(l)] = ...
    """

    # number of layers in the neural network
    L = len(parameters) // 2

    # Update rule for each parameter. Use a for loop.
    for l in range(L):
        lnum = str(l+1)
        parameters["W" + lnum] = parameters["W" + lnum] - learning_rate * grads["dW" + lnum]
        parameters["b" + lnum] = parameters["b" + lnum] - learning_rate * grads["db" + lnum]

    return parameters


def L_layer(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=True):
    """
    Implements a L-layer neural network: [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID.

    Arguments:
    X -- data, numpy array of shape (number of examples, num_px * num_px * 3)
    Y -- true "label" vector (containing 0 if cat, 1 if non-cat), of shape (1, number of examples)
    layers_dims -- list containing the input size and each layer size, of length (number of layers + 1).
    learning_rate -- learning rate of the gradient descent update rule
    num_iterations -- number of iterations of the optimization loop
    print_cost -- if True, it prints the cost every 100 steps

    Returns:
    parameters -- parameters learnt by the model. They can then be used to predict.
    """

    np.random.seed(1)

    costs = []

    parameters = deep_parameters(layers_dims)

    # Loop (gradient descent)
    for i in range(0, num_iterations):
        # Forward propagation: [LINEAR -> RELU]*(L-1) -> LINEAR -> SIGMOID.
        AL, caches = forward(X, parameters)
        cost = compute_cost(AL, Y)
        grads = backward(AL, Y, caches)

        parameters = update_parameters(parameters, grads, learning_rate)

        # Print the cost every 100 training example
        if print_cost and i % 100 == 0:
            log.info("Cost after iteration %i: %f" %(i, cost))
        if print_cost and i % 100 == 0:
            costs.append(cost)

    # plot the cost
    # plt.plot(np.squeeze(costs))
    # plt.ylabel('cost')
    # plt.xlabel('iterations (per tens)')
    # plt.title("Learning rate =" + str(learning_rate))
    # plt.show()

    return parameters


def predict(X, parameters, label_y=[1]):
    """
    Predict the results of a  L-layer neural network.

    Arguments:
    X -- data set of examples you would like to label
    parameters -- parameters of the trained model
    label_y -- true labels, [1] for 1 image

    Returns:
    p -- predictions for the given dataset X
    """

    m = X.shape[1]
    n = len(parameters) // 2 # number of layers in the neural network
    p = np.zeros((1,m))

    probs, caches = forward(X, parameters)

    # convert probs to 0/1 predictions
    for i in range(0, probs.shape[1]):
        if probs[0,i] > 0.5:
            p[0,i] = 1
        else:
            p[0,i] = 0

    # log.info('accuracy: {0}'.format(np.sum((p == label_y)/m)))
    return p


def classify(img_path, parameters):
    """
    Classify cat / non-cat.

    Arguments:
    img_path -- image path to classify
    parameters -- parameters of the trained model

    Returns:
    img_result -- 'cat' or 'non-cat'
    """

    classes = np.array([b'non-cat', b'cat'])
    num_px = 64

    img = np.array(ndimage.imread(img_path, flatten=False))
    img_resized = scipy.misc.imresize(img, size=(num_px,num_px)).reshape((num_px*num_px*3,1))

    img_pred = predict(img_resized, parameters)

    img_class = np.squeeze(img_pred)
    img_result = classes[int(img_class),].decode("utf-8")

    return img_result
