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

import os
import json
import re
import urllib
import cStringIO
from utils.UnicodeIO import UnicodeReader, UnicodeWriter
from db.Corpus_DB import Corpus_DB

class Home_Core(object):
	def __init__(self, request, response):
		self.request = request
		self.response = response
		self.params = {}
		self.content = {}   # All variables returned by an API call
		self.table = []     # Primary variable returned by an API call as rows of records
		self.header = []    # Header for primary variable
		self.configs = self.GetConfigs()
		self.menus = self.GetMenus()

################################################################################
# Server, Dataset, Model, and Attribute

	def GetConfigs(self):
		server = self.GetServer()
		dataset = self.GetDataset(server)
		model = self.GetModel(server, dataset)
		attribute = self.GetAttribute(server, dataset, model)
		url = self.GetURL()
		configs = {
			'server' : server,
			'dataset' : dataset,
			'model' : model,
			'attribute' : attribute,
			'url' : url,
			'is_text' : self.IsTextFormat(),
			'is_graph' : self.IsGraphFormat(),
			'is_json' : self.IsJsonFormat(),
			'is_csv' : self.IsCSVFormat(),
			'is_tsv' : self.IsTSVFormat()
		}
		return configs

	def GetServer(self):
		return self.request.env['HTTP_HOST']

	def GetDataset(self, server):
		return self.request.application

	def GetModel(self, server, dataset):
		return self.request.controller

	def GetAttribute(self, server, dataset, model):
		return self.request.function
	
	def GetURL(self):
		return self.request.env['wsgi_url_scheme'] + '://' + self.request.env['HTTP_HOST'] + self.request.env['PATH_INFO']
	
################################################################################
# Menus for datasets, models, attributes, etc.

	def GetMenus(self):
		server = self.configs['server']
		dataset = self.configs['dataset']
		model = self.configs['model']
		attribute = self.configs['attribute']

		operations = self.GetOperations(server)
		datasets = self.GetDatasets(server)
		models = self.GetModels(server, dataset, model, attribute)
		visualizations = self.GetVisualizations(server, dataset, model, attribute)
		attributes = self.GetAttributes(server, dataset, model, attribute)
		menus = {
			'server' : server,
			'dataset' : dataset,
			'model' : model,
			'attribute' : attribute,

			'datasets' : datasets,
			'operations' : operations,
			'models' : models,
			'visualizations' : visualizations,
			'attributes' : attributes
		}
		return menus

	def GetOperations(self, server):
		operations = [
			{ 'value' : 'dataset', 'name' : 'Upload a new dataset' }
		]
		self.configs.update({
			'AvailableOperations' : operations
		})
		return operations

	EXCLUDE_SYSTEM_FOLDERS = frozenset([ 'admin', 'examples', 'welcome', 'init', 'dataset', 'echo' ])
	EXCLUDE_TEMP_FOLDERS = re.compile(r'^temp_.*$')
	def IsExcludedDataset(self, folder):
		if folder in Home_Core.EXCLUDE_SYSTEM_FOLDERS:
			return True
		elif Home_Core.EXCLUDE_TEMP_FOLDERS.match(folder) is not None:
			return True
		else:
			return False

	def GetDatasets(self, server):
		folders = []
		applications_path = '{}/applications'.format(self.request.env['applications_parent'])
		for folder in os.listdir(applications_path):
			if not self.IsExcludedDataset(folder):
				applications_subpath = '{}/{}'.format(applications_path, folder)
				if os.path.isdir(applications_subpath):
					folders.append(folder)
		datasets = sorted(folders)
		if self.configs['dataset'] == 'init':
			self.content.update({
				'AvailableDatasets' : datasets
			})
		return datasets

	def GetModels(self, server, dataset, model, attribute):
		models = []
		if not self.IsExcludedDataset(dataset):
			with Corpus_DB() as corpus_db:
				rows = corpus_db.GetModels()
			models = [{
				'value' : row['model_key'],
				'name' : row['model_desc']
			} for row in rows ]
			if model == 'default' and attribute == 'index':
				self.content.update({
					'AvailableModels' : models
				})
		return models
	
	def GetVisualizations(self, server, dataset, model, attribute):
		visualizations = []
		if not self.IsExcludedDataset(dataset):
			with Corpus_DB() as corpus_db:
				rows = corpus_db.GetModels()
			models = frozenset(row['model_key'] for row in rows)
			if 'bow' in models and 'lda' in models:
				visualizations.append({
					'value' : 'TermTopicMatrix1',
					'name'  : 'Term-Topic Matrix 0.1'
				})
				visualizations.append({
					'value' : 'TermTopicMatrix2',
					'name'  : 'Term-Topic Matrix 0.2'
				})
				visualizations.append({
					'value' : 'TermTopicMatrix3',
					'name'  : 'Term-Topic Matrix 0.3'
				})
				visualizations.append({
					'value' : 'ScatterPlot1',
					'name'  : 'Scatter Plot 1.0'
				})
				visualizations.append({
					'value' : 'DocTopicMatrix',
					'name'  : 'DocTopicMatrix'
				})
			if 'bow' in models and 'lda' in models and 'itm' in models:
				visualizations.append({
					'value' : 'GroupInBox',
					'name'  : 'Group-in-a-Box'
				})
			if model == 'default' and attribute == 'index':
				self.content.update({
					'AvailableVisualizations' : visualizations
				})
		return visualizations

	def GetAttributes(self, server, dataset, model, attribute):
		attributes = []
		if not self.IsExcludedDataset(dataset):
			if model != 'default':
				if model == 'corpus':
					attributes = [
						'DocumentByIndex',
						'DocumentById',
						'LoadDocuments',
						'SearchDocuments',
						'MetadataFields',
						'MetadataByName'
					]
				if model == 'bow':
					attributes = [
						'TermFreqs',
						'TermProbs',
						'TermCoFreqs',
						'TermCoProbs',
						'TermG2',
						'SentenceCoFreqs',
						'SentenceCoProbs',
						'SentenceG2'
					]
				if model == 'lda':
					attributes = [
						'Vocab',
						'DocList',
						'TermList',
						'TopicList',
						'TermTopicMatrix',
						'DocTopicMatrix',
						'TopicCovariance',
						'TopTerms',
						'TopDocs'
					]
				if model == 'itm':
					attributes = [
						'Update',
						'Inspect',
						'GroupInBox',
						'gib'
					]
				if self.configs['attribute'] == 'index':
					self.content.update({
						'AvailableAttributes' : attributes
					})
		return attributes

################################################################################
# Parameters

	def GetStringParam( self, key ):
		if key in self.request.vars:
			return self.request.vars[key].decode('utf-8', 'replace')
		else:
			return None
		
	def GetNonNegativeIntegerParam( self, key ):
		try:
			n = int(self.request.vars[key])
			if n >= 0:
				return n
			else:
				return 0
		except:
			return None

	def GetNonNegativeFloatParam( self, key ):
		try:
			n = float(self.request.vars[key])
			if n >= 0:
				return n
			else:
				return 0.0
		except:
			return None
	
################################################################################
# Generate a response

	def IsDebugMode( self ):
		return 'debug' in self.request.vars
	
	def IsTextFormat( self ):
		return not self.IsGraphFormat() and not self.IsJsonFormat()
	
	def IsGraphFormat( self ):
		return 'format' in self.request.vars and 'graph' == self.request.vars['format'].lower()

	def IsJsonFormat( self ):
		return 'format' in self.request.vars and 'json' == self.request.vars['format'].lower()

	def IsCSVFormat( self ):
		return 'format' in self.request.vars and 'csv' == self.request.vars['format'].lower()
	
	def IsTSVFormat( self ):
		return 'format' in self.request.vars and 'tsv' == self.request.vars['format'].lower()
	
	def IsMachineFormat( self ):
		return self.IsJsonFormat() or self.IsGraphFormat() or self.IsCSVFormat() or self.IsTSVFormat()
	
	def HasAllowedOrigin( self ):
		return 'HTTP_ORIGIN' in self.request.env
	
	def GetAllowedOrigin( self ):
		return self.request.env.HTTP_ORIGIN
	
	def GenerateResponse( self ):
		if self.IsDebugMode():
			return self.GenerateDebugResponse()
		else:
			return self.GenerateNormalResponse()
	
	def GenerateDebugResponse( self ):
		envObject = self.request.env
		envJSON = {}
		for key in envObject:
			value = envObject[ key ]
			if isinstance( value, dict ) or \
			   isinstance( value, list ) or isinstance( value, tuple ) or \
			   isinstance( value, str ) or isinstance( value, unicode ) or \
			   isinstance( value, int ) or isinstance( value, long ) or isinstance( value, float ) or \
			   value is None or value is True or value is False:
				envJSON[ key ] = value
			else:
				envJSON[ key ] = 'Value not JSON-serializable'
		
		data = {
			'env' : envJSON,
			'cookies' : self.request.cookies,
			'vars' : self.request.vars,
			'get_vars' : self.request.get_vars,
			'post_vars' : self.request.post_vars,
			'folder' : self.request.folder,
			'application' : self.request.application,
			'controller' : self.request.controller,
			'function' : self.request.function,
			'args' : self.request.args,
			'extension' : self.request.extension,
			'now' : str( self.request.now ),
			'configs' : self.configs,
			'params' : self.params
		}
		data.update( self.content )
		dataStr = json.dumps( data, encoding = 'utf-8', indent = 2, sort_keys = True )
		
		self.response.headers['Content-Type'] = 'application/json'
		return dataStr

	def GenerateNormalResponse( self ):
		if self.IsJsonFormat():
			data = { 'configs' : self.configs }
			data.update(self.content)
			dataStr = json.dumps( self.content, encoding = 'utf-8', indent = 2, sort_keys = True )
			self.response.headers['Content-Type'] = 'application/json'
			if self.HasAllowedOrigin():
				self.response.headers['Access-Control-Allow-Origin'] = self.GetAllowedOrigin()
			return dataStr

		if self.IsCSVFormat():
			f = cStringIO.StringIO()
			writer = UnicodeWriter(f)
			writer.writerow( [ d['name'] for d in self.header ] )
			for record in self.table:
				row = [ record[d['name']] for d in self.header ]
				writer.writerow(row)
			dataStr = f.getvalue()
			f.close()
			self.response.headers['Content-Type'] = 'text/csv; charset=utf-8'
			if self.HasAllowedOrigin():
				self.response.headers['Access-Control-Allow-Origin'] = self.GetAllowedOrigin()
			return dataStr
		
		if self.IsTSVFormat():
			headerStr = u'\t'.join( d['name'] for d in self.header )
			rowStrs = []
			for record in self.table:
				rowStrs.append( u'\t'.join( u'{}'.format( record[d['name']]) for d in self.header ) )
			tableStr = u'\n'.join(rowStrs)
			dataStr = u'{}\n{}\n'.format( headerStr, tableStr ).encode('utf-8')
			self.response.headers['Content-Type'] = 'text/tab-separated-values; charset=utf-8'
			if self.HasAllowedOrigin():
				self.response.headers['Access-Control-Allow-Origin'] = self.GetAllowedOrigin()
			return dataStr
	
		data = {
			'configs' : self.configs,
			'menus' : self.menus,
			'params' : self.params
		}
		data.update( self.content )
		data['content'] = json.dumps( self.content, encoding = 'utf-8', indent = 2, sort_keys = True )
		self.response.headers['Content-Type'] = 'text/html; charset=utf-8'
		if self.HasAllowedOrigin():
			self.response.headers['Access-Control-Allow-Origin'] = self.GetAllowedOrigin()
		return data
