# -*- encoding: utf-8 -*-

import os, glob
from collections import Counter
from warnings import warn

import numpy as np
import rpy2.robjects as ro
import pandas as pd
import pandas.rpy.common as com

import stylo2_utils as utils

def run_stylo2(inputFile, outputFile, options):
	"""Reads CSV from input file, processes all set-like columns (strings of
		form "word;word..."), and invokes stylo (in R) on obtained DataFrame
		saving results to outputFile. Options are used to build R command.
	"""
	# Change working dir to tmp dir so R plots will be saved there
	temp_dir = utils.goto_new_tempdir()

	try:
		data = pd.read_csv(inputFile, sep=';', quotechar='"', encoding='utf-8')
		data = _process_dataframe(data)
		token = os.path.basename(inputFile)
		_stylo(data, options, token)
	except RuntimeError:
		raise Exception("Tabela cech nie mogła zostać przetworzona.")
	except Exception:
		raise Exception("Dane nie mogły zostać pogrupowane. Być może w korpusie " \
						"znajdują się pliki o identycznej treści.")

	plotFile = token + "_001." + options['filetype']
	utils.move_file_from_dir(plotFile, temp_dir, outputFile)

def _process_dataframe(dataframe):
	"""Processes whole dataframe: sets row names, deletes unnecessay columns, divides
		set-like columns (of form "word;word...") into separate cols for every word
		and normalizes columns when neccessary."""
	INDEX_COLUMN 		= 'doc_name'
	SET_COLUMNS			= ['base', 'interp_signs', 'orth', 'lex_classes',
							'bigrams', 'trigrams', 'proper_names']
	REMOVE_COLUMNS		= ['doc_name', 'true', 'class']
	NORMALIZE_COLUMNS	= ['avg_tok_count', 'interp_per_sentence']

	_set_index_column(dataframe, INDEX_COLUMN, os.path.basename)
	_remove_columns(dataframe, REMOVE_COLUMNS)
	_normalize_columns(dataframe, NORMALIZE_COLUMNS)

	processed_set_columns = _process_set_columns(dataframe, SET_COLUMNS)
	processed_set_columns.append(dataframe)
	new_dataframe = pd.concat(processed_set_columns, axis=1)

	new_dataframe.columns = _decode_columns(new_dataframe)
	del new_dataframe['tok_count']
	return new_dataframe

def _stylo(data, config, token):
	"""Invokes stylo on processed DataFrame"""
	ro.r('library(stylo)')
	r_data = com.convert_to_r_dataframe(data)
	ro.globalenv['stylo_data'] = r_data

	command = _build_command(config, len(data.columns), token)
	print command
	ro.r(command)

def _set_index_column(dataframe, column_name, process):
	"""Sets index (row names) of dataframe form one of its columns"""
	if column_name in dataframe:
		index = [process(p) for p in dataframe[column_name]]
	else:
		warn("Matrix doesn't have doc_name column.", RuntimeWarning)
		index = range(0, len(dataframe))
	dataframe.index = index
	return dataframe

def _process_set_columns(dataframe, cols):
	"""Apply process_set_column to every non-empty column in cols and return list
		of created dataframes."""
	processed_set_columns = []
	tok_count = dataframe['tok_count'] if ('tok_count' in dataframe) else None

	for col in cols:
		if col in dataframe:
			if str(dataframe[col]) != 'NaN':
				column_dataframe = _process_set_column(dataframe[col], tok_count)
				processed_set_columns.append(column_dataframe)
			else:
				warn('Empty column: ' + col, RuntimeWarning)
			del dataframe[col]

	return processed_set_columns

def _process_set_column(column, tok_count, limit=0):
	"""Divides single set-like column (of form "word;word...") column into separate
		cols for every word, normalizes them and creates DataFrame out of them."""
	splitted = column.apply(utils.str_to_counter)
	all_counts = np.sum(splitted)
	if not limit:
		limit = len(all_counts)
	filtered_counts = all_counts.most_common(limit)
	filtered_counts = Counter(dict(filtered_counts))
	splitted = splitted.apply(lambda c: c & filtered_counts)

	new_data = {}
	for lemma in filtered_counts:
		new_data[column.name + ':' + lemma] = splitted.apply(lambda c: c[lemma])

	column_dataframe = pd.DataFrame(new_data)
	column_dataframe.index = column.index

	for colname in column_dataframe.columns:
		col = column_dataframe[colname]
		column_dataframe[colname] = _normalize_column(col, tok_count)

	return column_dataframe

def _build_command(config, data_size, graph_filename, data_obj_name = "stylo_data"):
	"""Builds R command from given options. Some options are adjusted to interface
		and thus hardcoded."""
	ADDITIONAL_CONSTS = {"mfw.min": data_size, "mfw.max": data_size,
						"plot.custom.height": 4, "plot.custom.width": 4,
						"plot.font.size": 6,
						"gui":"FALSE", "titles.on.graphs": "FALSE",
						"custom.graph.filename": graph_filename}
	command = "stylo(frequencies=" + data_obj_name
	command += ", write." + config['filetype'] + ".file=TRUE"
	command += utils.dict_to_str(config, ['filetype'])
	command += utils.dict_to_str(ADDITIONAL_CONSTS)
	command += ")"
	return command

def _decode_columns(dataframe):
	"""Translates column names co they won't contain polish characters."""
	cols = dataframe.columns
	t = [utils.remove_accents(colname) for colname in cols]
	return pd.core.index.Index(t)

def _normalize_columns(dataframe, cols):
	for colname in cols:
		if colname in dataframe:
			dataframe[colname] = normalize_column(dataframe[colname])

def _normalize_column(data, column_sum=None):
	"""Normalize single column of Pandas DataFrame.
		data - Pandas Series
		column_sum - normalization term, if None we normalize by sum of column
	"""
	if column_sum is None:
		column_sum = np.sum(data)
	normalized_data = data / (column_sum * 1.0)	# multiplication for proper data conversion (floats)
	return normalized_data

def _remove_columns(dataframe, cols):
	for col in cols:
		if col in dataframe:
			del dataframe[col]
