#encoding: utf8

from __future__ import print_function
from collections import defaultdict

import codecs
import contextlib
import sys


@contextlib.contextmanager
def stringIO_hack(file):
	yield file


class MappingFileOperator(object):
	"""
	A base class for manipulating the output files of
	plWN -> SUMO automatic mapping
	"""

	def __init__(self):
		self._header = None
		self._linebuffer = None

	def print_contents(self, out_fileobj=sys.stdout):
		"""
		Prints the stored contents of the file
		in the same format as they were read
		"""

		self._print_header(out_fileobj)

		for line in self._linebuffer:
			self._print(line, out_fileobj)

	def read_mapping_file(self, mapping_file, selected=(), linefunc=None):
		"""
		Reads and remembers the whole file.
		The header is stored in _header, and the content rows in _linebuffer
		The header and each row is a list of unicode strings,
		which are cells in the csv file

		'selected' is a sequence of column identifiers (or column numbers), 
		which are either indexes or names in the header

		'linefunc' is a function that is called for every row, with values
		from columns identified in 'selected' passed as arguments

		'selected' must not be empty and 'linefunc' must not be None
		for the calling to happen
		"""

		self._linebuffer = []

		with codecs.open(mapping_file, 'r', 'utf8') if type(mapping_file) is str else stringIO_hack(mapping_file) as f:
			self._header = next(f).strip().split(u';')

			selected_idxs = [None] * len(selected)

			for i in xrange(len(selected)):
				id_ = selected[i]
				try:
					if type(id_) == int:
						selected_idxs[i] = id_
					else:
						selected_idxs[i] = self._header.index(id_)
				except ValueError:
					try:
						idx = int(id_)
					except ValueError:
						raise ValueError('Invalid column identifier', id_)
					else:
						# Bounds check
						# It's certainly an int now
						if (
							-idx > len(self._header)
							if idx < 0
							else idx >= len(self._header)
						):
							raise IndexError(
								'Column index out of bounds',
								idx,
								len(self._header))

						selected_idxs[i] = idx

			if selected_idxs and linefunc is not None:
				def doline(items):
					linefunc(*(items[idx] for idx in selected_idxs))
			else:
				def doline(items):
					pass

			for line in f:
				items = line.strip().split(';')
				self._linebuffer.append(items)
				doline(items)

	@staticmethod
	def _print(record, out_fileobj=sys.stdout):
		print(u';'.join(record).encode('utf-8'), file=out_fileobj)

	def _print_header(self, out_fileobj=sys.stdout):
		self._print(self._header, out_fileobj)

	def _print_line(self, idx, out_fileobj=sys.stdout):
		self._print(self._linebuffer[idx], out_fileobj)

