################################################################################
"""
Functions for Digit Recognition

Created on Wed Jun 01 00:00:00 2016

@author: Prasun Roy

@e-mail: prasunroy.pr@gmail.com
"""
################################################################################

# import modules
import matplotlib.pyplot as pplt
import numpy as np
import os
import pandas as pd
import skimage.feature as skim
import sklearn.preprocessing as pp
import time

from conf import _config
from conf import _configinfo

################################################################################

def _fscale(data, split=False, load=False, verbose=False):
    # initialize scaler
    scaler = pp.MinMaxScaler()
    
    # initialize variables
    config = _configinfo()
    sdpath = config['root_data_path'] + 'scaled.npy'
    
    # scale data
    if verbose: print('scaling features............... ', end = '')
    data = np.array(data, dtype='float64')
    if load and os.path.isfile(sdpath):
        m = np.load(sdpath)[0]
        r = np.load(sdpath)[1]
        r[r==0] = 1
        data = (data - m) / r
    elif split:
        train = data[:config['train_d']]
        valid = data[config['train_d']:]
        scaler.fit(train)
        m = scaler.data_min_
        r = scaler.data_range_
        train = scaler.transform(train)
        valid = scaler.transform(valid)
        data  = np.vstack((train, valid))
    else:
        data = scaler.fit_transform(data)
        m = scaler.data_min_
        r = scaler.data_range_
    if verbose: print('done')
    
    # save scaled config
    if not load: np.save(sdpath, np.vstack((m, r)))
    
    # return scaled data
    return data

################################################################################

def _haar(data, load=True, save=False, verbose=False):
    return data

################################################################################

def _hogs(data, load=True, save=False, verbose=False):
    # initialize config
    config = _config()
    
    # initialize variables
    datapath = config['hogs_data_path']
    data_hog = []
    
    # load hog data if exists
    if load and os.path.isfile(datapath):
        if verbose: print('loading descriptors............ ', end = '')
        data_hog = np.load(datapath)
        if verbose: print('done')
    
    # calculate hog data otherwise
    else:
        # initialize variables
        ix = config['shape_x']
        iy = config['shape_y']
        bn = config['bins_n']
        cx = config['cell_x']
        cy = config['cell_y']
        bw = config['blok_w']
        bh = config['blok_h']
        
        # perform hog
        t_beg = time.time()
        size = data.shape[0]
        loop = 0
        for image in data:
            if verbose: print('\rextracting descriptors......... %d%%'
                              %(loop*100//size), end = '')
            desc = skim.hog(image.reshape(ix, iy), orientations=bn,
                            pixels_per_cell=(cx, cy), cells_per_block=(bw, bh))
            data_hog.append(desc)
            loop = loop + 1
        data_hog = np.array(data_hog, dtype='float64')
        t_end = time.time()
        if verbose: print('\rextracting descriptors......... done @ %8.2f sec'
                          %(t_end - t_beg))
        
        # save data
        if save:
            if verbose: print('saving descriptors............. ', end = '')
            np.save(datapath, data_hog)
            if verbose: print('done')
    
    # return hog
    return data_hog

################################################################################

def _sift(data, load=True, save=False, verbose=False):
    return data

################################################################################

def _surf(data, load=True, save=False, verbose=False):
    return data

################################################################################

def _plot(classifier, train, valid, step=None, save=False, verbose=False):
    # initialize config
    config = _config()
    
    # initialize variables
    if step is None: step = config['steps_d']
    plot_figs_head = config['classifier'] + '-' + config['preprocess']
    plot_data_path = config['plot_data_path']
    plot_figs_path = config['plot_figs_path']
    
    m_train = train.shape[0]
    m_valid = valid.shape[0]
    X_valid = valid[:, 1:]
    y_valid = valid[:, 0]
    error_train = []
    error_valid = []
    sizes_train = []
    
    # calculate data for plot
    for i in range(0, m_train, step):
        if verbose: print('\rgenerating plot................ %d%%'
                          %(i*100//m_train), end = '')
        
        # randomly shuffle training data
        np.random.shuffle(train)
        
        # select subset of randomized training data
        X_train = train[:i+step, 1:]
        y_train = train[:i+step, 0]
        
        # train classifier with selected data
        classifier.fit(X_train, y_train)
        
        # cross-validate classifier
        p_train = classifier.predict(X_train)
        p_valid = classifier.predict(X_valid)
        
        # estimate errors
        error_train.append(sum(y_train != p_train) / len(y_train))
        error_valid.append(sum(y_valid != p_valid) / m_valid)
        
        sizes_train.append(i+step)
    
    error_train = np.array(error_train, dtype='float64')
    error_valid = np.array(error_valid, dtype='float64')
    sizes_train = np.array(sizes_train, dtype='uint32')
    
    if verbose: print('\rgenerating plot................ done')
    
    # plot data
    pplt.plot(sizes_train, error_train, 'rs-', label='training error')
    pplt.plot(sizes_train, error_valid, 'gs-', label='cross-validation error')
    pplt.title(plot_figs_head.upper()+' Learning Curve')
    pplt.xlabel('number of training instances')
    pplt.ylabel('classification error')
    pplt.legend()
    xmin,xmax = pplt.xlim()
    ymin,ymax = pplt.ylim()
    pplt.axis([xmin, xmax+step, ymin, ymax+0.01])
    pplt.grid(True)
    
    # save data
    if save:
        if verbose: print('saving plot.................... ', end = '')
        
        data = pd.DataFrame({'x1_TrainSizes':sizes_train,
                             'y1_TrainError':error_train,
                             'y2_ValidError':error_valid})
        
        data.to_csv(plot_data_path, index=False)
        pplt.savefig(plot_figs_path)
        
        if verbose: print('done')
    
    # display plot
    pplt.show()

################################################################################









