#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""This module reads the outputs of
qd_classifier.py, specificially classifed.fiat and classes.chunk.
"""

from gmisclib import fiatio
from gmisclib import chunkio
from gmisclib import die
import q_classifier_r as Q
# from g_classifiers import l_classifier_guts as LC
# from g_classifiers import qd_classifier_guts as QC



def read_classified(f):
	"""Read classified.fiat, as produced by l_classifier or qd_classifier
	This unpacks the two columns that are packed into ASCII representations
	and turns them back into python objects.
	@param f: file or filename
	@type f: file or str
	@rtype (dict, list(dict), list(str))
	@return: Much like L{fiatio.read<fiatio>}, it returns a tuple of header
		information, a list of dictionaries (each dictionary
		corresponding to a line) and a list of comment strings.
	"""
	if isinstance(f, str):
		f = open(f, 'r')
	header, data, comments = fiatio.read(f)

	for d in data:
		if 'P' in d:
			d['P'] = chunkio.stringchunk(d['P']).read_dict(float)
		if 'V' in d:
			d['V'] = chunkio.stringchunk(d['V']).read_NumArray()
	return (header, data, comments)



def model_fromchunk(chunk):
	"""A factory function to read in an arbitrary classifier model.
	@type chunk: L{chunkio.chunk}
	"""
	tmp = chunk.groupstart()
	if tmp == 'quadratic_class_model':
		rv = Q.qmodel.fromchunk(chunk)
	elif tmp == 'linear_class_description':
		rv = Q.lmodel.fromchunk(chunk)
	else:
		raise chunkio.BadFileFormat, 'Unknown type of classifier model: %s' % tmp
	chunk.groupend()
	return rv



def read_classifier(chunk):
	"""Read a classifier in from a L{gmisclib.chunkio.chunk}.
	This can yield either a quadratic or a linear classifier, depending what's
	available in the data file.   (This is not normally called directly
	by the user.)

	@type chunk: chunkio.chunk
	@param chunk: loosely, a data file.   More precisely, a source of tokens.
	@rtype: L{l_classifier} or L{qd_classifier}
	@return: a single classifier, containing models for several classes.
	"""
	typename = chunk.groupstart()
	models = chunk.read_dict_of(model_fromchunk)
	info = chunk.read_dict(str)
	chunk.groupend()
	return Q.classifier(typename, models, info=info,
			trainingset_name=info.get('trainingset', None),
			uid=info.get('Cuid', None)
			)


def read_classes(f):
	"""Read classes.chunk, as produced by l_classifier or qd_classifier
	It converts the header information to ints or floats as appropriate.
	It expects to read in a forest of equivalent classifiers, and it
	returns a list of them.  This is the normal API for reading
	classes.chunk.

	@param f: a filename or a file to read
	@type f: L{str} or L{file}
	@rtype: tuple(dict(str:str), list(some_kind_of_classifier))
	"""
	if isinstance(f, str):
		f = open(f, 'r')
	dc = chunkio.datachunk(f)
	hdr = read_classes_header(dc)
	classilist = dc.read_array_of(read_classifier)
	# if hdr['classifier_type'] == 'linear_discriminant_classifier':
	# 	classilist = dc.read_array_of(LC.classifier_reader)
	# else:
	# 	classilist = dc.read_array_of(QC.classifier_reader)
	return (hdr, classilist)


def read_classes_header(dc):
	"""Read in the header info.   This is part of the normal API.
	@param dc: a datachunk containing the classes.chunk file
		produced by a classifier run.
	@type dc: L{chunkio.datachunk}
	@rtype: dict(str: str, int, or float)
	@return: a dictionary containing the header information.
		See the docstring for the C{l_classifier} script for typical contents.
		Numbers are converted to L{float} or L{int} as appropriate.
	"""
	hdr = dc.read_dict(str)
	for k in ['nok', 'total', 'N_per_dim']:
		try:
			hdr[k] = int(round(float(hdr[k])))
			# This is to handle formats like "10.0".   Thanks to
			# Anastassia Loukina email Nov 10, 2011.
		except KeyError:
			pass
	for k in ['Pcorrect', 'K', 'KSigma', 'PSigma', 'Chance',
			'Perfection', 'PerfectionSigma', 'ChSigma',
			'Ftest', 'coverage']:
		try:
			hdr[k] = float(hdr[k])
		except KeyError:
			pass
	return hdr


if __name__ == '__main__':
	import sys
	print read_classes(sys.stdin)
