implementation module UtilNewlinesFile

import StdFile, StdInt, StdChar, StdString, StdArray, StdBool, StdClass, StdTuple, StdMisc
import UtilSequence

:: NewlineConvention
	=	NewlineConventionNone | NewlineConventionMac | NewlineConventionUnix | NewlineConventionDos

instance == NewlineConvention where
	(==) NewlineConventionNone NewlineConventionNone
		=	True
	(==) NewlineConventionMac NewlineConventionMac
		=	True
	(==) NewlineConventionUnix NewlineConventionUnix
		=	True
	(==) NewlineConventionDos NewlineConventionDos
		=	True
	(==) _ _
		=	False

instance toString NewlineConvention where
	toString NewlineConventionNone
		=	""
	toString NewlineConventionMac
		=	"\xd"
	toString NewlineConventionUnix
		=	"\xa"
	toString NewlineConventionDos
		=	"\xd\xa"

// slice that returns a unique array
(%.) infixl 9 :: !.{#Char} !(!Int,!Int) -> .{#Char}
(%.) string indices
	=	code
		{
			.inline %.
			.d 1 2 ii
				jsr sliceAC
			.o 1 0
			.end
		}

// this should be added to the Clean rts, so that the string doesn't have to be copied
downSize :: Int *{#Char} -> *{#Char}
downSize newSize string
	=	string %. (0, newSize-1)

convertLine :: *{#Char} -> (NewlineConvention, *{#Char})
convertLine line
	#! maxIndex
			=	size line - 1
	| maxIndex >= 0
		#! lastChar
			=	line.[maxIndex]
		| lastChar == '\xa'
			| maxIndex >= 1
				#! lastButOneChar
					=	line.[maxIndex-1]
				|  lastButOneChar == '\xd'
					=	(NewlineConventionDos, {downSize maxIndex line & [maxIndex-1] = '\n'})
				// otherwise
					=	(NewlineConventionUnix, {line & [maxIndex] = '\n'})
			// otherwise
				=	(NewlineConventionUnix, {line & [maxIndex] = '\n'})
		| lastChar == '\xd'
			=	(NewlineConventionMac, {line & [maxIndex] = '\n'})
		// otherwise
			=	(NewlineConventionNone, line)
	// otherwise
		=	(NewlineConventionNone, line)

readAnyLine :: !*File -> (NewlineConvention, !.{#Char}, !*File)
readAnyLine file
	# (line, file)
		=	freadline file
	  (convention, line)
	  	=	convertLine line
	=	(convention, line, file)

readLine file
	:==	(line, file`)
	where
		(_, line, file`)
			=	readAnyLine file

writeAnyLine :: !{#Char} !{#Char} !*File -> *File
writeAnyLine line newlineString file
	# maxIndex
			=	size line - 1
	  lastChar
			=	line.[maxIndex]
	| maxIndex >= 0 && lastChar == '\n'
		= file :- fwrites (line %. (0, maxIndex-1)) :- fwrites newlineString
	// otherwise
		= file :- fwrites line
