# filefeatures
#  	This file implements a text editor file handling features
# textClient
#
#	Opens client side of a socket 
#
# 	Arguments:
#		host The network address of the host
#		port port on which server is listening for clients
#
#	Returns:
#		sock The Channel identifier 

proc textClient {host port} {
	global sock 
	# client side of socket
	if [catch {set sock [socket $host $port]}] {
		puts "Error: Server is not listening at $port on $host"
		exit 
	}
		
	fconfigure $sock -buffering line
}

# set sock [textClient $host $port]
textClient $host $port

fileevent $sock readable [list getInfo $sock]

# getInfo 
#
#	Interpret data send by server and take appropriate action
#
#	Arguments:
#		sock Channel identifier
#

proc getInfo {sock} {
	global pidVal	
	global lines
	global fileName

	if [catch {gets $sock Name }]  {
		close $sock
		puts  "Client Closed"
	} else {
		switch -regexp -- $Name {
			{quit} { #Close client
				close $sock
				puts "Client Shut down by server"
			}
			{dirList} { #List of directories
				regexp {(dirList,)(.*)} $Name dirCmd a  dirshow
				regsub -all {\^A} $dirshow "\n" dirshow
				set a 1
				foreach line [split $dirshow \n] {
					if { $a == 1} { 
					} else {
						.openfile.directorylist.listbox insert end $line
					}
					incr a 
				}
			}
			{fileList} { #List of directories
				regexp {(fileList,)(.*)} $Name fileCmd b fileshow
				regsub -all {\^A} $fileshow "\n" fileshow
				set a 1
				foreach line [split $fileshow  \n] {
					if { $a == 1} {
					} else {		
						.openfile.filelist.listbox insert end $line
					}
					incr a
				}
			}
			{textDisplay} { #Show contents of file
				global fileOldHandle
				global lines
				global extent
				regexp {textDisplay,([^,]+),(.*)} $Name a extentList lines(content) 
	# textDisplay 	
				if { $extentList == " " } {
					# puts "No Extentlist"
					# do something as array has no owners 
				} else {
					array set extent $extentList
				}
				displayFile 
				setAccessControl
			 	#  else  {
				#	set lines(content) "  "
				#	.text insert end " \n"
				#	.text mark set insert "1.1"
        			#	.text tag add free "1.0" "1.0 +1 lines linestart"
				# }
        			set editor_title "$fileName: Multi user editor"
				wm title . $editor_title
				
			}
			{serverPushDocumentRequest} { #Send latest owner buffer
				# updateServerBuffer 	
				serverPushDocument
			}
			{ERROR} { #List of directories
				global ERROR
				regexp {(ERROR,)(.*)} $Name fileCmd b errorval
				if {$errorval == $ERROR(permission)} {
						tk_messageBox  -icon error -message "Permission Denied to create $fileName in directory" -title "Permission Denied" -type ok
						set fileName ""
				} elseif {$errorval == $ERROR(clash)} { 
						tk_messageBox  -icon error -message "Clash " -title "Clash" -type ok
				}
			}
		}
	} 
} 
# lfindallindices
#
#       Find all instances of value in list
#
#       Search list for first and last occurence of the element
#       Element occurs contiguously only
#       Arguments:
#               lists: Temporary variable to hold List
#               value: pidValue of client
proc lfindallindices { lists value } {
        set firstindex [lsearch -exact $lists $value]
        # set the first owned line as firstindex
        if { $firstindex >=0 } {
                set endindex  $firstindex
                # set the end of owned region initially as firstindex
                set index $firstindex
                while { $index >= 0 } {
                        set lists [lreplace $lists $index $index]
                        # remove already found element
                        set index [lsearch -exact $lists $value]
                        # search for next element
                        if { $index >= 0 } {
                        	incr endindex
                        	# if next element found increment endindex
                        }
                }
                set indices [list $firstindex $endindex]
                return $indices
        } else {
                return $firstindex
                # return NOTFOUND
        }
}

# updateMe
#
#	Only update Me
proc updateMe {} {
	global fileName
	global sock
	append line_in updateServerAllBuffer, 
	append line_in $fileName, 
	append line_in ONE
        puts $sock $line_in
        flush $sock
	unset line_in
}

# updateAll
#
#	Only update All
proc updateAll {} {
	global fileName
	global sock
	append line_in updateServerAllBuffer, 
	append line_in $fileName, 
	append line_in ALL
        puts $sock $line_in
        flush $sock
	unset line_in
}

# serverPushDocument
#
# 	Send latest owner buffer to server	
#
proc serverPushDocument {} {
	global fileName
	global pidVal
	global sock
	global extent

	set newContents [getLatestOwned]
	# get the latest owned region by the client
        append line_in serverPushDocument,
        append line_in $fileName
	# pid value used to identify client.
        append line_in ,
	if { $newContents != "" }  {
        	append line_in $newContents
	} else {
		append line_in " "
	}
	#  Send updated buffer 
        # puts $line_in
        puts $sock $line_in
        flush $sock
	unset line_in
}

# getLatestOwned
#
# 	Get latest owned buffer 
#
proc getLatestOwned {} {
	global lines
	global fileName
	global pidVal
	global offset
	global extent
	set ownedRange [.text tag ranges $pidVal]
#	if { ![info exists ownedRange] }  
	if { $ownedRange=="" } { 
	# there is no owned region
		return ""
	} 
	set first [lindex $ownedRange 0]
	set last [lindex $ownedRange 1]
	# change the way first and last are found 
	# find first and last by looking at the tag  ranges
	# I don't think the commented code is needed
	# if { ![info exists updateComplete] }  {
		# not waiting for update from server 
	#	set present [.text index insert]
	#	set offset [subtractIndices $first $present]
		# find offset from start of owned region of current 
		# cursor position and record it.
	# }
	list indices are one less than text indices
	# set tempstring [.text get ${first}.0 "${last}.0 lineend"]
	# set tempstring [.text get $first "$last -1 lines lineend"]
	#CHANGED
	set linecount [subtractLines $first $last]
	set tempstring [.text get "$first +1 c" "$first lineend"]
	for {set count 1} { $count < $linecount } { incr count} {
	append tempstring "\n"
	append tempstring [.text get "$first + 1c + $count lines" "$first + $count lines lineend"]
			
	}
	#CHANGED
	set newContents [split $tempstring \n]
#	puts $newContents
# 	set newContents [lrange $lines(content) $first $last ]
	unset tempstring
#	puts "getLatestOwned contents are $newContents"
	return $newContents
}

# createFileLatest
#
#	Create a temporary file to store all the changes
#

proc createFileLatest {} {
	global pidVal
	set fileLatestHandle [open templatest.$pidVal w+]
	puts -nonewline $fileLatestHandle [ .text get 1.0 end ]
	flush $fileLatestHandle
	close $fileLatestHandle
}

#cancelFileOperation
#
# 	Don't really want to change all the directories, please
#	restore my working directory 
#	

proc cancelFileOperation {} {
	global sock
	append lineout restoreDirectory
	puts $sock $lineout
	flush $sock
	catch { destroy .openfile }
	unset lineout 

}


# compareVersions
#
#	Compare temporary files  to see if the file has changed since 
#	last save.
#

proc compareVersions {} {
	global pidVal
	if [file exists tempold.$pidVal] {
		
		if [catch { exec diff tempold.$pidVal templatest.$pidVal >patchfile.$pidVal} errorcode] {
				return 1
		} else {
		#	puts "no difference"
			file delete -force  patchfile.$pidVal
			return 0
		}
 	} else  {
		return 0
	}		
	
}

# listDirectories
#
#	List the directories in current directory of server	
#
proc listDirectories {} {
	global sock
	.openfile.directorylist.listbox insert end "."
	.openfile.directorylist.listbox insert end ".."
	puts $sock getDir
	flush $sock
}

# listFiles 
#
#	List the files in current directory of server	
# 	Only files which can be edited are listed
proc listFiles {}  {
	global sock
	puts $sock "sendFileList"
	flush $sock
}

# displayDocument
#
#	Request server to send back text for the current file
#
#	Arguments:
#		File Name 	
#
proc displayDocument { } {
	global sock
	global filePrevious
	global fileName
	global pidVal
	set filePrevious $fileName
	# get document from the server 
	append textList clientOpen,
# serverPopDocument
# change to clientOpen.
	
	append textList $fileName,
	append textList $pidVal
	puts $sock $textList 
	# ask server to send document
	flush $sock
	unset textList
	.menu.refresh.m entryconfigure "Me" -state normal
	.menu.refresh.m entryconfigure "All Clients" -state normal
	# Allow updating 
}

# saveFile
#
# 	Save Client buffer back to file on the server
#
proc saveFile { } { 
	global sock
	global pidVal
	global fileName 
	global filePrevious
	if [string match "" $fileName] {
		return
	}
	catch { destroy .openfile } 
	createFileLatest  
	if [string match $fileName $filePrevious] {
	# Save option or save as option, requires to decide
	# the file to be sent
		# Decide that  it is save
	#	puts "SAVE OPTION"
		puts $fileName
		puts $filePrevious 
		set sendFileName patchfile.$pidVal
		set saveInfo saveFile
		compareVersions	
	} else {
		# Decide that  it is saveas
	#	puts "Save AS OPTION"
        #	puts $fileName
	#	puts $filePrevious 
		set sendFileName templatest.$pidVal
		set saveInfo saveasFile	
	}
	# There will be a sendFile if any changes are there
	if [ file exists $sendFileName] {
		# The old copy should be replaced
		file copy -force templatest.$pidVal tempold.$pidVal
		set filePatchHandle [open  $sendFileName r]	
		set textBuffer [read $filePatchHandle]
		regsub -all "\n" $textBuffer {^A} textBuffer
		append line_in $saveInfo
		append line_in , 
		append line_in $fileName 
		append line_in , 
		append line_in $textBuffer
		puts $sock $line_in
		flush $sock
		unset line_in
	}
}
# creatFile 	
proc creatFile { } { 
	global sock
	global offset
	global fileName 
	global pidVal
	append line_in creatFile
	append line_in , 
	append line_in $fileName 
	append line_in , 
	append line_in $pidVal
	puts $sock $line_in
	flush $sock
	unset line_in
	set offset 0
        grab release .openfile  
        catch { destroy .openfile }
} 

# newFile
#
# Open a new file after closing the previous file
#
#
proc newFile {} { 
	global pidVal
	global fileName
	global sock
#	set retval [file exists tempold.$pidVal]
	set retval [.text tag ranges $pidVal]
#	puts "retval is $retval"
#	if {$retval==0} {
	if {$retval!=""} {
	# There are owned lines, Save the owned lines
	#	puts "There are owned lines"
		 set newContents [getLatestOwned]
		# puts "Free Line Edit: New Contents"
       		# puts $newContents
        	# get latest owned  and send to server
        	# Editing Free selected Area
        	append line_out serverPushDocument,
        	append line_out $fileName
        	append line_out ,
        	append line_out $newContents
		puts $sock $line_out   
        	unset line_out
	} 
	if { $fileName != ""} {
#		puts " i am in newFile, $fileName"
        	append line_in newFile,
        	append line_in $fileName
        	puts $sock $line_in
        	flush $sock
        	unset line_in
	}
	clearEditor
	set fileName ""
	.text insert end " "
        .text tag add free "insert linestart " "insert  +1 lines linestart"
#	return 1
#	} else {
# 		Close the open file and clear the editor
#		set retval [closeFile ]
#		return $retval 
#	}
}
# creatnewFile 
proc creatnewFile { } {
	newFile
	fileopenDialog "New File" "creatFile"
}
# loadFile
# 
#       Enable options to save and close a file. Get text to be
#       displayed from the server. Destroy the openfile Dialog box.
#
proc loadFile {} {
        global fileName
	global editor_title 
        if [string match "" $fileName] {
                return
        }
        .menu.file.m entryconfigure "Save" -state normal
        .menu.file.m entryconfigure "Close" -state normal
        .text delete 1.0 end
        displayDocument 
        grab release .openfile  
        catch { destroy .openfile }
}

#exitFile
#
#       Close the editor after prompting user to save any unsaved
#       files
#
proc exitFile { } {
	global fileName

	if { ![info exists fileName]} {
                exit
	}
#	set retval [closeFile ]
	newFile
        # set retval [fileSaveCheck ]
        # check if file needs to be saved, if so prompt user to
        # save it
 
      # if {$retval==1} {
        # if user does not cancel exit operation
        #        clearEditor
                exit
       # }
}

# closeFile
#
#       This procedure closes the file on the server. Before doing so
#       it checks whether file needs to be saved. The user may decide
#       that the file does not need to be closed after all and cancel
#       the whole operation
#
proc closeFile {} {
        global sock
        global fileName  
	global pidVal

	set newContents [getLatestOwned]
	# puts "Free Line Edit: New Contents"
        # puts $newContents
        # get latest owned  and send to server
        # Editing Free selected Area
        append line_out serverPushDocument,
        append line_out $fileName
        append line_out ,
        append line_out $newContents
	puts $sock $line_out   
        unset line_out
        if { [info exists ownedstart] } {
        	.text tag remove $pidVal $ownedstart $ownedfinish
                # previously owned region no longer
                # of interest
        }

     #   set retval [fileSaveCheck ]
        # check if file needs to be saved, if so prompt user to
        # save it
 
     #   if {$retval==1} {
        # if user does not cancel save operation
        
                append line_in newFile,
                append line_in $fileName
                puts $sock $line_in
                flush $sock
                unset line_in
	        clearEditor
     #   }
	set fileName ""
        return 1
}

# openFile
# 	
#	This procedure opens a file dialog box for opening files
#
proc openFile {} {
#	catch {newFile } retval
	newFile
#	if {$retval==0} {
#		return
#	} else {
		catch {fileopenDialog "Open File.." "loadFile"}
#	}
}

# fileSaveCheck
#       
#       Check to see if file needs to be saved, if so then ask user
#       whether he wants to close the file without saving. If user
#       cancels the whole file saving operation, then return a zero
#
#       Returns:
#               0 for Cancelling of current operation
#               1 for no cancellation
#               
proc fileSaveCheck {} {
        createFileLatest
        # create a temporary file with the
        # latest contents of text buffer
 
        set retval [compareVersions]
        # compare if the text buffer has changed
        # since it was loaded from the file
 
        if {$retval==1} {
        # if textBuffer has changed
 
                set choice [ tk_messageBox -type yesnocancel -default yes \
                        -message "Save changes before closing?" \
                        -icon question]
                if {$choice =="yes"} {
                        saveFile
                } elseif {$choice =="cancel"} { 
                        return 0
                }
        }       
        return 1
}

