# Copyright 2006 Mounir Errami. m.errami@gmail.com
# This file is part of Bibus, a bibliographic database that can
# work together with OpenOffice.org to generate bibliographic indexes.
#
# Bibus is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# Bibus is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Bibus; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA.
#
import Format.Converter
import copy,sys
import BIB, RefList
import win32com.client
import win32com.gen_py
import pywintypes
from pywintypes import IID
from pywintypes import *
import bibMSW.mswRef
import bibMSW.mswFormatter
import bibMSW.mswUtils
import bibMSW.mswCONST
import wx

# For documentation on the Document and Field Interfaces used see
#http://msdn2.microsoft.com/en-us/library/ms254954(en-us,VS.80).aspx
#It details the word objects scriptable from Python
#Example: for the Field Interface: see http://msdn2.microsoft.com/en-us/library/microsoft.office.interop.word.field_members.aspx
class mswDoc:
	"""Class that defines interaction between Bibus and MS Word active document
	mswDoc is calss that represents Microsfot Word Application instance!!!!!!
	the active document in ms word is accessed using the property :
	mswDoc.ActiveDocument
	"""
	def __init__(self):
		"""Grab the active word document
		Bibus doesn't keep any info about the doc, so a new doc is created
		for each operation (insertion, update, etc...)
		All information is kept in the active doc itself (ALT+F9 in msword dispay elements in each inserted field)
		This class contains functions to interact with a msword doc """
		self.application = win32com.client.Dispatch("Word.Application") #doc = msword application
		#self.application.Visible = False
		self.hasDocuments = False
		self.bibStyle = BIB.FORMAT_DICO
		self.mswFormatter = bibMSW.mswFormatter.mswFormatter(self.bibStyle,self.application)
		self.conv = self.__getConverter(self.bibStyle)
		self.biblioList = []
		#self.printConverterInfo()
	def hasOpenedDocuments(self):
		if self.application.Documents.Count>0:
			return True
		else:
			#self.application.Quit()
			return False
		
	def MSWInsert(self, ref):
		"""Inserts a Field in the document corresponding to the selected ref in Bibus so that
		The Field is an Addin Type, so its value is hidden to the user (to display in the word doc, ALT+F9)
		Field.Result.Text is what you see in the doc
		Field.Code.Text is hidden and is <Author> Mounir Errami</Author><Year>2005</Year> etc...
		The XML keys depends are set according to bibus conf depending on the type of reference to insert => xmlKeys = BIB.EDIT[BIB.BIB_TYPE[int(ref[0][2])]][0]"""
		allRefText =bibMSW.mswCONST.me_MSWBIBUS_ADDIN_REF_TAG
		for t in BIB.BIB_FIELDS[:-1]:
			if not unicode(ref[0][BIB.BIBLIOGRAPHIC_FIELDS[t]]):
				continue
			elif t!="BibliographicType":
 				allRefText=allRefText+"<"+str(t)+">"+unicode(ref[0][BIB.BIBLIOGRAPHIC_FIELDS[t]])+"</"+str(t)+">"
			else:
				allRefText=allRefText+"<"+str(t)+">"+BIB.BIB_TYPE[ref[0][BIB.BIBLIOGRAPHIC_FIELDS[t]]]+"</"+str(t)+">"
		allRefText = allRefText+"<"+bibMSW.mswCONST.me_MSWBIBUS_CITE_RANGES+">"+"for ranges"+"</"+bibMSW.mswCONST.me_MSWBIBUS_CITE_RANGES+">"
		allRefText = allRefText+"<"+bibMSW.mswCONST.me_MSWBIBUS_CITE_DUPLICATES+"></"+bibMSW.mswCONST.me_MSWBIBUS_CITE_DUPLICATES+">"
		#resText is the text displayed to the user when reference is inserted ([1] or Smith et al, 2005)
		#resText depends on the style and has to be set accordingly
		resText = "{"+ref[0][1]+"}"
		nfo =bibMSW.mswRef.mswRef(self,None,bibMSW.mswCONST.me_MSWBIBUS_CITE_TAG,resText,allRefText)
		
	def MSWgetBibusBiblio(self):
		"""Gets the bibliographic field (only one per document)
		This field has the following particularity
		Field.Data ==me_MSWBIBUS_BIBLIO_TAG
		Note: citation fields within the doc have the following particularity
		Field.Data ==me_MSWBIBUS_CITE_TAG"""
		for myField in self.application.ActiveDocument.Fields:
    			if myField.Type == bibMSW.mswCONST.me_wdFieldAddin :
				if myField.Data==bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_TAG :
					return myField
		return None

	def MSWCreateBiblio(self):
		"""Create the bibliographic Field"""
		f_biblioField=self.MSWgetBibusBiblio()
		if f_biblioField!=None:
			f_biblioField.Result.Select()
		else:# f_biblioField == None:
			ar=self.application.ActiveDocument.Range()
			ar.Collapse(0x0)
			ar.InsertBreak(bibMSW.mswCONST.me_wdPageBreak)
			rToInsert = ar.Select()
			self.application.Selection.Collapse(0x0)
		try:
			f_biblioField =self.application.ActiveDocument.Fields.Add(self.application.Selection.Range,bibMSW.mswCONST.me_wdFieldEmpty,"BiblioInsert",False) 
			f_biblioField.Code.Text="ADDIN BibusBiblio"
			f_biblioField.Data=bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_TAG
			#print " Passing "
		except (pywintypes.com_error):
			pass
		self.MSWUpdateBiblio()
			
	def MSWUpdateBiblio(self):
		"""Fill the bibliographic Field
		Adds information from each citation field and concatenate into the biblioField.Result.Text (visible to the user)
		"""
		f_biblioField=self.MSWgetBibusBiblio()
		if f_biblioField == None:
			#print "COuldn't create Biblio"
			self.MSWCreateBiblio()
			return
		biblioResStr=""
		biblioCodeStr=""
		biblioResStr,biblioCodeStr = self.createBiblioStrings()
		f_biblioField.Result.Text = biblioResStr
		f_biblioField.Code.Text = biblioCodeStr
		f_biblioField.Data = bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_TAG
	
		
		
		
	def isFieldBiblio(self,field):
		"""Check if a field is a bibliographic field"""
		if field.Type != bibMSW.mswCONST.me_wdFieldAddin :
			return False
		if field.Data!=bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_TAG:
			return False
		return True
		
	def __setStyle(self,style):
		"""Set the bibus Style to insert the ref"""
		self.bibStyle = style
		self.conv = self.__getConverter(self.bibStyle)
	
	def __getConverter(self,style):
		"""Set the Converter or read from default
		Take into account the different versions of bibus styles"""
		if style:
			if style['version'] == 1.0:
				return Format.Converter.Converter(conv=style)
			else:
				return Format.Converter.Converter(conv=style['fields'])
		else:
			return Format.Converter.Converter()
		
	def printConverterInfo(self):
		"""This a development function called via menu print conv info"""
		#self.mswFormatter.fuseCitations(self.application.ActiveDocument.Fields)
		#print aField.Result, aField.Result.Start , aField.Result.End
		#wdW = 0x2
		#aField.Result.Delete()
		#print aField.Result.Find.Execute("Cheong", ReplaceWith = "allo")
		#if find == True:
		#	print "Found"
		#print aField.Result.Text
		
		citationFields = bibMSW.mswUtils.getCitationFields(self.application.ActiveDocument.Fields)
		#for f in self.application.ActiveDocument.Fields:
		#	code = f.Code.Text
		#	if code.find("BibusRef")!=-1:
		#		f.Data = bibMSW.mswCONST.me_MSWBIBUS_CITE_TAG
		#	print f.Result.Text
		#fuseCitations
		self.mswFormatter.fuseCitations(citationFields)
		#citationFields = bibMSW.mswUtils.getCitationFields(self.application.ActiveDocument.Fields)
		#for f in self.application.ActiveDocument.Fields:
		#	print f.Result.Text
		##self.mswFormatter.addDuplicateInfo(citationFields)	
		#print int("a")
		#self.trySequence()
		return

	def trySequence(self):
		"""Development function to find out the best sequence when finalizing
		i.e. shortest time and correct result"""
		self.updateCitationText()
		self.fuseCitations(self.application.ActiveDocument.Fields)
		self.preFormatCitations()
		#self.MSWCreateBiblio()
		#self.formatBiblioString()
		
	def createBiblioStrings(self):
		"""
		Adds information from each citation field and concatenate into 
		biblioResultString (visible to the user) and into
		biblioCodeString (invisible, unless ALT+F9 entered)
		biblioCodeString is in XML format and it will be used for final formatting
		Far far far too long
		"""
		#bibRangeList = [] 
		#relativeStart = 0
		biblioResultString="\n"+ self.mswFormatter.getBiblioTitle() +", bibus Generated - Not formatted"
		biblioCodeString=bibMSW.mswCONST.me_MSWBIBUS_ADDIN_BIB_TAG
		i=1
		entryList=[]
		self.biblioList = []
		newEntry= bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_NEWENTRY
		for myField in self.application.ActiveDocument.Fields:
			#bibRangeEntryList = []
			biblioCodeEntry=""
			biblioResultEntry=""
			if bibMSW.mswUtils.isFieldCitation(myField)==False:
				continue
			else:
				stringToAnalyze = myField.Code.Text
				wordCitationFields = bibMSW.mswUtils.getXmlKeysFromString(stringToAnalyze)
				wordCitationBiblioType = bibMSW.mswUtils.getXmlValueFromString("BibliographicType",stringToAnalyze)
				jfield = 0
				myId =""
				for field in wordCitationFields: 
					if self.conv[wordCitationBiblioType].has_key(field)==False:
						continue
					func,param = self.conv[bibMSW.mswUtils.getXmlValueFromString("BibliographicType",stringToAnalyze)][field]# NEED VALUE FROM BIB_TYPE, ORI CODE = func,param = self.conv[BIB_TYPE[dbref[1]]][field]
					#if field == "Title" :
					#	print " field func=", param
					valueToFormat = bibMSW.mswUtils.getXmlValueFromString(field,stringToAnalyze)
					tmpdata =  apply(func,(valueToFormat,self.conv['locale']) + param)
					biblioCodeEntry =biblioCodeEntry+"<"+field+">"+tmpdata+"</"+field+">"
					if jfield>=3 :#This suppresses Id, identifier, adress
						biblioResultEntry=biblioResultEntry+tmpdata+";"
					jfield=jfield+1
					if field == "Identifier":
						myId =	valueToFormat
				myId = bibMSW.mswUtils.getXmlValueFromString("Identifier",stringToAnalyze)
				if entryList.count(myId)==0: 
					entryList.append(myId)
					biblioResultString= biblioResultString +"\n%s"%i+".\t"+biblioResultEntry
					biblioCodeString =biblioCodeString+"\n<"+newEntry+">"
					tIdConst= bibMSW.mswCONST.me_MSWBIBUS_BIBLIO_IDENTIFIER
					biblioCodeString =biblioCodeString+"<"+tIdConst+">"+str(i)+"</"+tIdConst+">"
					biblioCodeString =biblioCodeString+biblioCodeEntry
					biblioCodeString =biblioCodeString+"</"+newEntry+">"
					self.biblioList.append(biblioCodeEntry)
					i=i+1
		#print self.biblioList
		return (biblioResultString,biblioCodeString)
	
	def formatBiblioString(self):
		"""This function formats the biblio according to the Style chosen by the user"""
		biblioString = self.mswFormatter.getFormattedBiblioString(self.biblioList, self)
		bibField = self.MSWgetBibusBiblio()
		nfo = bibMSW.mswRef.mswRef(self,bibField)
		nfo.setResult(biblioString)
		#print "rangePosList=",self.mswFormatter.getBiblioRangesFromMSW(self.application)
		self.mswFormatter.formatBibliographicIndex(self)
		
	def getMSWFormatter(self):
		return self.mswFormatter
		
	def getMSWApplication(self):
		return self.application
		
		
	def updateCitationText(self):
		"""Update the citation text if numbering is selected"""
		#print "Update called"
		
		oBrackets,cBrakets = self.mswFormatter.getBracket()
		Fields = self.application.ActiveDocument.Fields
		isCitation = bibMSW.mswCONST.me_MSWBIBUS_CITE_TAG
		idList = []
		if self.mswFormatter.isNumberEntries()==True :#and self.mswFormatter.isSortByPosition() == True :
			refIds = {}
			nbUnik =1
			
			for myField in Fields:
				#print "myField.Result=" ,myField.Result.Text, "Data =",myField.Data
				if bibMSW.mswUtils.isFieldCitation(myField)==True:
					idToComp = bibMSW.mswUtils.getXmlValueFromString("Identifier",myField.Code.Text)
					if idList.count(idToComp)==0:
						idList.append(idToComp)
					myField.Result.Text=oBrackets+str(idList.index(idToComp)+1)+cBrakets
		else :
			citationFields =[]
			for myField in Fields:
				if bibMSW.mswUtils.isFieldCitation(myField)==False:
					continue
				nfo = bibMSW.mswRef.mswRef(self,myField)
				citationFields.append(myField)
				idToCite,newCiteText= self.mswFormatter.applyCitationTemplate(myField)
				nfo.setResult(oBrackets+idToCite+cBrakets)
				myField.Code.Text = newCiteText #if modify Code before changing Result, miscrosoft word bugs and won't accept the changes!
				myField.Data = bibMSW.mswCONST.me_MSWBIBUS_CITE_TAG #if modify Code before changing Result, miscrosoft word bugs and won't accept the changes!
				self.mswFormatter.normalizeCitationRangeFont(myField.Result)

	def preFormatCitations(self): 
		mFields =  bibMSW.mswUtils.getCitationFields(self.application.ActiveDocument.Fields)
		self.mswFormatter.preFormatCitations(mFields)
		return mFields
			
	def fuseCitations(self,mFields):
		#mFields =  bibMSW.mswUtils.getCitationFields(self.application.ActiveDocument.Fields)
		self.mswFormatter.fuseCitations(mFields)
		return mFields

	def minimizeWindow(self):
		"""Faster when MS Word window is minimzed"""
		#print " Version =>%s<"%self.application.Version
		Version = float(self.application.Version)
		if Version <=9:
			return			
		if self.application.WindowState !=bibMSW.mswCONST.me_wdWindowStateMinimize:
			self.application.Activate()
			self.application.WindowState = bibMSW.mswCONST.me_wdWindowStateMinimize
	
	def normalWindow(self):
		"""Display windows to normal size, called after minimisation"""
		self.application.Activate()
		self.application.WindowState =  bibMSW.mswCONST.me_wdWindowStateNormal
	
	def finaliseDoc(self,awxWindow):
		"""Finalization of citation, checks if document if saved
		if save fails, the user can still go on if he wants to"""
		activeDoc = self.application.ActiveDocument
		if activeDoc.Saved == False:
			dlg = wx.MessageDialog(awxWindow, "Your file must be saved before proceeding","The file must be saved!",wx.OK|wx.CANCEL|wx.ICON_EXCLAMATION,wx.DefaultPosition)
			if dlg.ShowModal()==wx.ID_OK:
				try:
					self.normalWindow()
					activeDoc.Save()
				except (pywintypes.com_error):
					ndlg = wx.MessageDialog(awxWindow, "File could not be saved. Do you want Bibus to proceed anyway? (Make a copy of your document before)","Proceed anyway?",wx.YES_NO|wx.NO_DEFAULT|wx.ICON_QUESTION,wx.DefaultPosition)
					if ndlg.ShowModal()==wx.ID_YES:
						 s="proceed"
					else :
						s= "aborting"
						return
			else:
				s= "returning"
				return
		wdps = 0xa
		activeDoc.Content.Copy()
		self.application.Visible = 1
		newDoc = self.application.Documents.Add()
		newDoc.Activate()
		newDoc.Content.Paste()
		
		
		
