-- CSci 555, Functional Programming, Fall 2010
-- Assignment #2

-- H. Conrad Cunningham
-- Original: 15 February 2000
-- Revision: 18 February 2007

-- Add an import of the Char module to allow functions like isAlphaNum to
-- work

import Char

--  BEGIN function defintions from section 7.6 of
--  S. Thompson. _Haskell: The Craft of Functional Progamming_ 
--  Second Edition, Addison Wesley, 1999.

-- Type synonyms for words and lines.
type Word = String
type Line = [Word]

-- The "whitespace" characters.
whitespace :: String
whitespace = ['\n','\t',' ']

-- Get a word from the front of a string.
getWord :: String -> String
getWord []                = [] 
getWord (x:xs) 
    | elem x whitespace   = []
    | otherwise           = x : getWord xs
    
-- Drop the first word of a string.
dropWord :: String -> String
dropWord []               = []
dropWord (x:xs) 
    | elem x whitespace   = (x:xs)
    | otherwise           = dropWord xs

-- Remove the whitespace character(s) from the front of a string.
dropSpace :: String -> String
dropSpace []              = []
dropSpace (x:xs) 
    | elem x whitespace   = dropSpace xs
    | otherwise           = (x:xs)

-- Split a string into words.
splitWords :: String -> [Word]
splitWords st = split (dropSpace st)

split :: String -> [Word]
split [] = []
split st = (getWord st) : split (dropSpace (dropWord st))

-- Split into lines of length at most lineLen.
lineLen :: Int
-- lineLen = 80
lineLen = 10	-- for testing purposes

-- Get a line from a list (name changed from Thompson book),
getLine2 :: Int -> [Word] -> Line
getLine2 len []           = []
getLine2 len (w:ws)
    | length w <= len     = w : restOfLine  
    | otherwise           = []
    where
        newlen      = len - (length w + 1)
        restOfLine  = getLine2 newlen ws

-- Get a line from a list, with error repaired.
-- If a word is longer than the total line length, textbook solution for
-- splitLines did not terminate.  The repair is to make getLine keep the
-- entire word as a line by itself.

getLine3 :: Int -> [Word] -> Line
getLine3 len []      = []
getLine3 len (w:ws)
    | lenword <= len = w : getLine3 newlen ws
    | len == lineLen = [w]  -- word too long, line by itself
    | otherwise      = []
    where
        lenword = length w
        newlen  = len - (lenword + 1)

-- Drop the first line from a list of words.
-- dropLine :: Int -> [Word] -> Line
-- dropLine = dropLine 	-- DUMMY DEFINITION

-- Split into lines. (Changed to use getLine3.)
splitLines :: [Word] -> [Line]
splitLines [] = []
splitLines ws = getLine3 lineLen ws
                    : splitLines (dropLine lineLen ws)

-- Fill a text string into lines.
fill :: String -> [Line]
fill = splitLines . splitWords

-- END function definitions from Thompson textbook
