-- PD 2008-09: Práctica 9 (16 de diciembre de 2009)
-- Departamento de Ciencias de la Computación e I.A.
-- Universidad de Sevilla
-- ============================================================================

-- ----------------------------------------------------------------------------
-- Importación de librerías auxiliares                                       --
-- ----------------------------------------------------------------------------

import System
import IO
import List( nub )
import Char
import Test.QuickCheck

-- ---------------------------------------------------------------------
-- Introducción                                                       --
-- ---------------------------------------------------------------------

{-
 Un "extractor de datos de la pantalla" es una herramienta para extraer
 datos de sitios web a partir de su código fuente. En este ejercicio,
 escribirá uno de los más odiado capturadores de datos: uno que extrae
 direcciones de correo electrónico. ¿Por qué es odiado?  Porque se usa
 para recopilar direcciones de correo para enviarles correos basura. Sin
 embargo, en este ejercicio mostraremos una aplicación útil del
 capturador. Vamos a extrar nombres y correos de páginas escritas en
 HTML (HyperText Markup Language). Por ejemplo, a partir del siguiente
 HTML (que se encuentra en 
 http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html)

    <html>
    <head>
     <title>Simple</title>
    </head>
    <body>
    
    <h1>Fichero simple</h1>
    Este fichero es simplemente para probar 
    el extractor de direcciones de correo. 
    Se encuentra en este 
    <a href="http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html">sitio</a>.
    
    Correos:
    <a href="mailto:jlara@gmail.com">Juan Lara</a> y 
    <a href="mailto:eruiz@hotmail.com">Eva Ruiz</a>. 
    
    </body>
    </html>
    
 Vamos a extraer la lista de los elementos "<a>", que contienen URLs
 (Uniform ResourceLocators). Si una URL comienza con http: es la
 dirección de una página web; si comienza con mailto: el resto es una
 dirección de correo. Para el documento anterior, la lista de enlaces
 (cada uno con datos extras al final, que son útiles de la técnica que
 usamos) es la siguiente:

    ["http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html\">sitio</a>.\n\nCorreos:\n"
    ,"mailto:jlara@gmail.com\">Juan Lara</a> y \n"
    ,"mailto:eruiz@hotmail.com\">Eva Ruiz</a>. \n\n</body>\n</html>\n"]

 A partir de esta lista, podemos extraer una lista de nombres y
 direcciones de correo:

    [("Juan Lara","jlara@gmail.com")
    ,("Eva Ruiz","eruiz@hotmail.com")]

 En el enunciado de la práctica se incluye el documento HTML de prueba y
 las dos listas anteriores: ejHTML, ejEnlances y ejLibroDirecciones. 
 También contiene dos funciones útiles: cogeHasta y borraHasta.

 Nótese que el tipo de ejEnlances en [Enlace] y el tipo de
 ejLibroDirecciones es [(Nombre,Correo)]. En otras palabras:
 ejEnlances es una lista de enlaces y ejLibroDirecciones es una lista
 de pares compuestos por un nombre y una dirección de correo. Es la
 primera vez que introducimos definiciones de tipos (y lo volveremos a
 estudiar en el tutorial 6). Las declaraciones de tipos son las
 siguientes:

    type Enlace = String
    type Nombre = String
    type Correo = String
    type HTML = String
    type URL = String

 Estas declaraciones de tipos son simplemente alias para el tipo
 String. Los alias no son estrictamente necesarios, pero hacen los
 programas más legibles.
-}

-- Declaraciones de tipos:
type Enlace = String
type Nombre = String
type Correo = String
type HTML = String
type URL = String

-- Ejemplos de datos:
ejURL =
    "http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html"

ejHTML :: String
ejHTML =    
    "<html>\n"
 ++ "<head>\n"
 ++ " <title>Simple</title>\n"
 ++ "</head>\n"
 ++ "<body>\n"
 ++ "\n"
 ++ "<h1>Fichero simple</h1>\n"
 ++ "Este fichero es simplemente para probar \n"
 ++ "el extractor de direcciones de correo. \n"
 ++ "Se encuentra en este \n"
 ++ "<a href=\"http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html\">sitio</a>.\n"
 ++ "\n"
 ++ "Correos:\n"
 ++ "<a href=\"mailto:jlara@gmail.com\">Juan Lara</a> y \n"
 ++ "<a href=\"mailto:eruiz@hotmail.com\">Eva Ruiz</a>. \n"
 ++ "\n"
 ++ "</body>\n"
 ++ "</html>\n"

ejEnlances :: [Enlace]
ejEnlances = 
    ["http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html\">sitio</a>.\n\nCorreos:\n"
    ,"mailto:jlara@gmail.com\">Juan Lara</a> y \n"
    ,"mailto:eruiz@hotmail.com\">Eva Ruiz</a>. \n\n</body>\n</html>\n"]

ejLibroDirecciones :: [(Nombre,Correo)]
ejLibroDirecciones = 
    [("Juan Lara","jlara@gmail.com")
    ,("Eva Ruiz","eruiz@hotmail.com")]

-- Interacción con el sistema:

-- (obtieneURL u) obtiene el contenido del sitio u. Por ejemplo,
--    *Main> do {d <- obtieneURL ejURL ; putStr d}
--    <html>
--    <head>
--     <title>Simple</title>
--    </head>
--    <body>
--    
--    <h1>Fichero simple</h1>
--    Este fichero es simplemente para probar 
--    el extractor de direcciones de correo. 
--    Se encuentra en este 
--    <a href="http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html">sitio</a>.
--    
--    Correos:
--    <a href="mailto:jlara@gmail.com">Juan Lara</a> y 
--    <a href="mailto:eruiz@hotmail.com">Eva Ruiz</a>. 
--    
--    </body>
--    </html>
obtieneURL :: String -> IO String
obtieneURL url =
    do system ("wget --recursive=off -O /tmp/zzz.html --quiet " ++ url)
       readFile "/tmp/zzz.html"

-- (correosDeURL u) escribe la lista de los correos del sitio u. Por
-- ejemplo, 
--    *Main> correosDeURL ejURL
--    Juan Lara: jlara@gmail.com
--    Eva Ruiz: eruiz@hotmail.com
correosDeURL :: URL -> IO ()
correosDeURL url =
  do html <- obtieneURL url
     let correos = (correosDeHTML html)
     putStr (escribeDirecciones correos)

-- (correosPorNombreDeURL u l) escribe la lista de los correos del sitio
-- cuyos nombres contienen la cadena n. Por ejemplo,
--    *Main> correosPorNombreDeURL ejURL "la"
--    Juan Lara: jlara@gmail.com
--    *Main> correosPorNombreDeURL ejURL "a"
--    Juan Lara: jlara@gmail.com
--    Eva Ruiz: eruiz@hotmail.com
--    *Main> correosPorNombreDeURL ejURL "o"
correosPorNombreDeURL :: URL -> Nombre -> IO ()
correosPorNombreDeURL url nombre =
  do html <- obtieneURL url
     let correos = (correosPorNombreDeHTML html nombre)
     putStr (escribeDirecciones correos)

-- Funciones predefinidas:

-- (indiceSubcadena xs ys) es la longitud de la cadena que precede a xs
-- en la cadena ys, si xs es subcadena de ys; y es la longitud de ys en
-- caso contrario. Por ejemplo,
--    indiceSubcadena "nombre:" "el nombre: Luis"  ==>  3
--    indiceSubcadena "edad:" "el nombre: Luis"    ==>  15
indiceSubcadena :: String -> String -> Int
indiceSubcadena xs ys = 
    head ([i | i <- [0..n-1], prefijo xs (drop i ys) ] ++ [n])
    where n = length ys

-- (cogeHasta xs ys) es la cadena que precede a xs en la cadena ys, si
-- xs es subcadena de ys; y es ys en caso contrario. Por ejemplo,
--    cogeHasta "nombre:" "el nombre: Luis"  ==>  "el "
--    cogeHasta "edad:" "el nombre: Luis"    ==>  "el nombre: Luis"
cogeHasta :: String -> String -> String
cogeHasta xs ys  =  take (indiceSubcadena xs ys) ys

-- (borraHasta xs ys) es la cadena que sigue a xs en la cadena ys, si
-- xs es subcadena de ys; y es la cadena vacía en caso contrario. Por
-- ejemplo, 
--    borraHasta "nombre:" "el nombre: Luis"  ==>  " Luis"
--    borraHasta "edad:" "el nombre: Luis"    ==>  ""
borraHasta :: String -> String -> String
borraHasta xs ys  =  drop (indiceSubcadena xs ys + length xs) ys

-- ---------------------------------------------------------------------
-- Ejercicios                                                         --
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Capturador básico de datos de la pantalla                          --
-- ---------------------------------------------------------------------

-- ---------------------------------------------------------------------
-- Ejercicio 1.1. Definir la función
--    igualCadena :: String -> String -> Bool
-- tal que (igualCadena c1 c2) se verifica si las cadenas c1 y c2 son
-- iguales sin diferenciar mayúsculas de minúsculas. Por ejemplo,
--    igualCadena "seViLLa" "Sevilla"  ==>  True
-- ---------------------------------------------------------------------

igualCadena :: String -> String -> Bool
igualCadena c1 c2 = undefined 

-- ---------------------------------------------------------------------
-- Ejercicio 1.2. Comprobar con QuickCheck que para cualquier cadena su
-- transformación a mayúscula es igual (según igualCadena) que su
-- transformación a minúsculas.
-- ---------------------------------------------------------------------

-- La propiedad es
prop_igualCadena :: String -> Property
prop_igualCadena cs = undefined 

-- La comprobación es

-- ---------------------------------------------------------------------
-- Ejercicio 2.1. Definir la función
--    prefijo :: String -> String -> Bool
-- tal que (prefijo xs ys) se verifica si xs es prefijo de ys, pero sin
-- diferenciar mayúsculas de minúsculas. Por ejemplo,
--    prefijo "Ab" "aBcD"  ==>  True
--    prefijo "Bc" "aBcD"  ==>  False
-- ---------------------------------------------------------------------

prefijo :: String -> String -> Bool
prefijo xs ys = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 2.2. Comprobar con QuickCheck que para cualquier cadena xs
-- y cualquier entero n, la cadena formada por los n primeros elementos
-- de xs es prefijo de la cadena xs con sus caracteres en minúsculas de
-- y de dicha cadena con sus caracteres en mayúsculas.
-- ---------------------------------------------------------------------

-- La propiedad es
prop_prefijo :: String -> Int -> Property
prop_prefijo xs n = undefined

-- La comprobación es

-- ---------------------------------------------------------------------
-- Ejercicio 3.1. Definir la función
--    contiene :: String -> String -> Bool
-- tal que (contiene xs ys ) se verifica si la cadena xs contiene como
-- subcadena a la ys sin distinguir mayúsculas de minúsculas. Por ejemplo,
--    contiene "aBcD" "bC"  ==>  True
--    contiene "aBcD" "bD"  ==>  False
-- ---------------------------------------------------------------------

contiene :: String -> String -> Bool
contiene xs ys = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 3.2. Comprobar con QuickCheck que si xs es una cadena, n y
-- m son dos enteros e ys es la subcadena de xs de longitud n a partir
-- del elemento m+1, entonces xs en mayúsculas contiene a ys y lo mismo
-- sucede con xs en minúsculas.
-- --------------------------------------------------------------------- 

-- La propiedad es
prop_contiene :: String -> Int -> Int -> Property
prop_contiene xs n m  = undefined 

-- La comprobación es

-- ---------------------------------------------------------------------
-- Ejercicio 4.1. Definir la función
--    divide :: String -> String -> [String]
-- tal que (divide c1 c2) es la lista de las cadenas obtenidas
-- dividiendo la cadena c2 según las ocurrencias de la cadena c1. Por
-- ejemplo, 
--    *Main> divide "," "cadena,separada,por,comas"
--    ["cadena","separada","por","comas"]
--    *Main> divide "el" "copia el numero y el final"
--    ["copia "," numero y "," final"]
--    *Main> divide "final" "copia el numero y el final"
--    ["copia el numero y el ",""]
--    *Main> divide "" "copia el numero y el final"
--    *** Exception: No se puede  dividir por la cadena vacia
-- La función devuelve un error al intentar dividir por la cadena
-- vacía. 
-- Nota: Pueden usarse las predefinidas cogeHasta y borraHasta.
-- ---------------------------------------------------------------------

divide :: String -> String -> [String]
divide = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 4.2. Definir la función
--    reconstruye :: String -> [String] -> String
-- tal que (reconstruye c cs) es la cadena obtenida uniendo las cadenas
-- de cs con la cadena c entre cada una de ellas. Por ejemplo,
--    *Main> reconstruye "," ["cadena","separada","por","comas"]
--    "cadena,separada,por,comas"
--    *Main> reconstruye "el" ["copia "," numero y "," final"]
--    "copia el numero y el final"
--    *Main> reconstruye "final" ["copia el numero y el ",""]
--    "copia el numero y el final"
--    *Main> reconstruye "final" []
--    "*** Exception: cannot reconstruye from empty list
-- La función devuelve un error al intentar reconstruir la lista vacía.
-- ---------------------------------------------------------------------

reconstruye :: String -> [String] -> String
reconstruye = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 4.3. Comprobar con QuickCheck que al dividir una cadena c2
-- mediante un separador c1 (no vacío) y después recontruirla (con el
-- mismo separador) se obtiene una cadena igual a la inicial.
-- ---------------------------------------------------------------------

-- La propiedad es
prop_divide :: String -> String -> Property
prop_divide c1 c2 = undefined

-- La comprobación es

-- ---------------------------------------------------------------------
-- Ejercicio 5.1. Definir la función
--    enlacesDeHTML :: HTML -> [Enlace]
-- tal que (enlacesDeHTML d) es la lista de los enlaces del documento
-- HTML d. Por ejemplo,
--    *Main> enlacesDeHTML ejHTML
--    ["http://www.cs.us.es/~jalonso/cursos/pd/practicas/ejemplo.html\">sitio</a>.\n\nCorreos:\n"
--    ,"mailto:jlara@gmail.com\">Juan Lara</a> y \n"
--    ,"mailto:eruiz@hotmail.com\">Eva Ruiz</a>. \n\n</body>\n</html>\n"]
-- ---------------------------------------------------------------------

enlacesDeHTML :: HTML -> [Enlace]
enlacesDeHTML d = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 6. Definir la función
--    cogeCorreos :: [Enlace] -> [Enlace]
-- tal que (cogeCorreos es) es la lista de ls direcciones de correo
-- contenidos en la lista de enlaces es. Por ejemplo,
--    *Main> cogeCorreos (enlacesDeHTML ejHTML)
--    ["mailto:jlara@gmail.com\">Juan Lara</a> y \n"
--    ,"mailto:eruiz@hotmail.com\">Eva Ruiz</a>. \n\n</body>\n</html>\n"]
-- ---------------------------------------------------------------------

cogeCorreos :: [Enlace] -> [Enlace]
cogeCorreos es = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 7. Definir la función
--    enlace2par :: Enlace -> (Nombre, Correo)
-- tal que (enlace2par e) es el par formado por el nombre y el correo
-- correspondiente al enlace e. Por ejemplo,
--    *Main> enlace2par "mailto:jlara@gmail.com\">Juan Lara</a> y \n"
--    ("Juan Lara","jlara@gmail.com")
-- ---------------------------------------------------------------------

enlace2par :: Enlace -> (Nombre, Correo)
enlace2par enlace = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 8. Definir la función
--    correosDeHTML :: HTML -> [(Nombre,Correo)]
-- tal que (correosDeHTML d) es la lista de pares (nombre,correo)
-- extraida del documento HTML d. Por ejemplo,
--    *Main> correosDeHTML ejHTML
--    [("Juan Lara","jlara@gmail.com")
--    ,("Eva Ruiz","eruiz@hotmail.com")]
-- Definirla de forma que en el resultado no haya repeticiones. Para
-- ello, se puede usar la predefinida nub.
-- ---------------------------------------------------------------------

correosDeHTML :: HTML -> [(Nombre,Correo)]
correosDeHTML html = undefined 

-- ---------------------------------------------------------------------
-- Ejercicio 9. Definir la función
--    buscaCorreo :: Nombre -> [(Nombre, Correo)] -> [(Nombre, Correo)]
-- tal que (buscaCorreo n ds) es la lista de las direcciones de ds cuyos
-- nombres contienen la cadena n. Por ejemplo,
--    *Main> buscaCorreo "la" ejLibroDirecciones
--    [("Juan Lara","jlara@gmail.com")]
--    *Main> buscaCorreo "a" ejLibroDirecciones
--    [("Juan Lara","jlara@gmail.com"),("Eva Ruiz","eruiz@hotmail.com")]
--    *Main> buscaCorreo "o" ejLibroDirecciones
--    []
-- ---------------------------------------------------------------------

buscaCorreo :: Nombre -> [(Nombre, Correo)] -> [(Nombre, Correo)]
buscaCorreo nombre direcciones = undefined 

-- ---------------------------------------------------------------------
-- Ejercicio 10. Definir la función
--    correosPorNombreDeHTML :: HTML -> Nombre -> [(Nombre,Correo)]
-- tal que (correosPorNombreDeHTML d n) es la lista de los pares
-- formados por los nombres y correos tales que los nombres contienen la
-- cadena n. Por ejemplo,
--    *Main> correosPorNombreDeHTML ejHTML "la"
--    [("Juan Lara","jlara@gmail.com")]
-- ---------------------------------------------------------------------

correosPorNombreDeHTML :: HTML -> Nombre -> [(Nombre,Correo)]
correosPorNombreDeHTML html nombre = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 11.1. Definir la función
--    escribeDirecciones1 :: [(Nombre, Correo)] -> String
-- tal que (escribeDirecciones1 ds) es la cadena de las direcciones de
-- la lista de direcciones ds con el siguiente formato
--    nombre: dirección de correo
-- Por ejemplo,
--    *Main> putStr (escribeDirecciones1 ejLibroDirecciones)
--    Juan Lara: jlara@gmail.com
--    Eva Ruiz: eruiz@hotmail.com
-- Nota: Usar la función unlines
-- ---------------------------------------------------------------------

escribeDirecciones1 :: [(Nombre, Correo)] -> String
escribeDirecciones1 ds = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 11.2. Modificar la función
--    escribeDirecciones :: [(Nombre, Correo)] -> String
-- tal que (escribeDirecciones ds) es la cadena de las direcciones de
-- la lista de direcciones ds con el siguiente formato
--    apellidos, nombre: dirección de correo
-- Por ejemplo,
--    *Main> putStr (escribeDirecciones ejLibroDirecciones)
--    Lara, Juan   jlara@gmail.com
--    Ruiz, Eva    eruiz@hotmail.com
-- Nota: En la definición conviene usar la función formatea que se
-- especifica a continuación.
-- ---------------------------------------------------------------------

escribeDirecciones :: [(Nombre, Correo)] -> String
escribeDirecciones = undefined

-- (formatea n) es el nombre con el formato "appellidos, nombre". Por
-- ejemplo,
--    *Main> formatea "Juan Lara Ruiz"
--    "Lara Ruiz, Juan"
--    *Main> formatea "Juan Lara"
--    "Lara, Juan"
--    *Main> formatea "Lara, Juan"
--    "Lara, Juan"
--    *Main> formatea "jlara@gmail.com"
--    "jlara@gmail.com"
formatea :: String -> String
formatea = undefined

-- ---------------------------------------------------------------------
-- Ejercicio 12.1. Comprobar el funcionamiento de la función correosDeURL
-- con el ejemplo de URL (ejURL) y otros sitios.
-- ---------------------------------------------------------------------

-- La comprobación es

-- ---------------------------------------------------------------------
-- Ejercicio 12.2. Comprobar el funcionamiento de la función
-- correosPorNombreDeURL con el ejemplo de URL (ejURL) y otros sitios.
-- ---------------------------------------------------------------------

-- La comprobación es

-- Para comprobar todo el fichero:
comprobacion_de_todo = 
    quickCheck prop_igualCadena >> 
    quickCheck prop_prefijo >>
    quickCheck prop_contiene >>
    quickCheck prop_contiene2 >>
    quickCheck prop_divide

-- La comprobación es

   


