#!/usr/bin/python
# coding=utf-8

import re
import sys
import codecs

cidades = {
    u'ABRANTES'              : u'Abrantes',
    u'ÁGUEDA'                : u'Águeda',
    u'ALGÉS'                 : u'Algés',
    u'ANGRA DO HEROÍSMO'     : u'Angra do Heroismo',
    u'AVEIRO'                : u'Aveiro',
    u'BARCELOS'              : u'Barcelos',
    u'BEJA'                  : u'Beja',
    u'BRAGA'                 : u'Braga',
    u'BRAGANÇA'              : u'Bragança',
    u'CALDAS DA RAINHA'      : u'Caldas da Rainha',
    u'CAPARICA'              : u'Caparica',
    u'CASTELO BRANCO'        : u'Castelo Branco',
    u'COIMBRA'               : u'Coimbra',
    u'COVILHÃ'               : u'Covilhã',
    u'ELVAS'                 : u'Elvas',
    u'ESTORIL'               : u'Estoril',
    u'ÉVORA'                 : u'Évora',
    u'FARO'                  : u'Faro',
    u'FELGUEIRAS'            : u'Felgueiras',
    u'FUNCHAL'               : u'Funchal',
    u'GUARDA'                : u'Guarda',
    u'IDANHA-A-NOVA'         : u'Idanha-a-Nova',
    u'LAMEGO'                : u'Lamego',
    u'LAVRADIO'              : u'Lavradio',
    u'LEIRIA'                : u'Leiria',
    u'LISBOA'                : u'Lisboa',
    u'LORDELO VRL'           : u'Lordelo/Vila Real',
    u'MIRANDELA'             : u'Mirandela',
    u'Melgaço'               : u'Melgaço',
    u'OLIVEIRA DE AZEMÉIS'   : u'Oliveira de Azeméis',
    u'OLIVEIRA DO HOSPITAL'  : u'Oliveira do Hospital',
    u'PAÇO DE ARCOS'         : u'Paço de Arcos',
    u'PENICHE'               : u'Peniche',
    u'PONTA DELGADA'         : u'Ponta Delgada',
    u'PONTE DE LIMA'         : u'Ponte de Lima',
    u'PORTALEGRE'            : u'Portalegre',
    u'PORTIMÃO'              : u'Portimão',
    u'PORTO'                 : u'Porto',
    u'PORTO SALVO'           : u'Porto Salvo',
    u'RIO MAIOR'             : u'Rio Maior',
    u'SANTARÉM'              : u'Santarém',
    u'SEIA'                  : u'Seia',
    u'SETÚBAL'               : u'Setúbal',
    u'SÃO MAMEDE DE INFESTA' : u'São Mamede de Infesta',
    u'TOMAR'                 : u'Tomar',
    u'VALENÇA'               : u'Valença',
    u'VIANA DO CASTELO'      : u'Viana do Castelo',
    u'VILA DO CONDE'         : u'Vila do Conde',
    u'VILA NOVA DE GAIA'     : u'Vila Nova de Gaia',
    u'VILA REAL'             : u'Vila Real',
    u'VISEU'                 : u'Viseu' }

class Disciplina:
    def __init__(self, code, name):
        self.code= code
        self.name= name

    def __repr__(self):
        return (u"%s(%s)" % (self.code, self.name))

class Curso:
    def __init__(self):
        self.code   = None
        self.codc   = None
        self.curso  = None
        self.cidade = None
        self.univ   = None
        self.grau   = None
        self.tipo   = None
        self.tempo  = None
        self.ects   = None
        self.vagas  = None
        self.vagas1 = None
        self.vagas2 = None
        self.nota1  = None
        self.provas = None
        self.reqs   = None

    def disciplinas(self):
        return reduce(lambda x,y: x.union(y), [dd.req for dd in self.reqs])

    def md1(self, requirement):
        return ( u"""Por exemplo, em relação ao curso de {curso} com {tempo} anos em {cidade}\n"""
                 u"""(primeira linha da tabela), entraram {vagas1} alunos na primeira fase com nota mínima de {nota1}.\n"""
                 u"""Sobraram {vagas2} vagas para a 2ª fase.\n"""
                 u"""As provas de ingresso de {req} valem {provas}% da média de entrada.\n\n"""
                 u"""| Vagas | Último | Provas | Curso | Anos x Cidade |\n"""
                 u"""| :--: | :--: | :--: | ---- | ---- |\n""").format(
            curso= self.curso, tempo=self.tempo, cidade=cidades.get(self.cidade, u"?"),
            vagas1= self.vagas1 or u"?", nota1= self.nota1 or u"?",
            vagas2= self.vagas2 or u"?", nota2= self.nota2 or u"?",
            vagas= self.vagas, provas= self.provas or u"?",
            req= requirement.title())

    def md(self):
        return ( u"""| {vagas2}/{vagas} | {nota1} | {provas}% | """
                 u"""[{curso}](http://www.dges.mec.pt/guias/detcursopi.asp?codc={codc}&code={code}"""
                 u""" "{univ}") | {tempo}{cidade} |\n""").format(
            nota1= u"{0:.2f}".format(self.nota1) if self.nota1 else u"?",
            vagas2= u"{0:d}".format(self.vagas2) if self.vagas2 else u"",
            tempo= u"{0}x ".format(self.tempo) if self.tempo else u"",
            curso=self.curso, cidade=cidades.get(self.cidade, u"?"),
            code=self.code, codc=self.codc, univ= self.univ,
            vagas= self.vagas, provas= self.provas or u"?")

class Requirement:
    def __init__(self, req):
        self.req= req
        self.cursos= list()

    def __repr__(self):
        return " + ".join([d.name for d in self.req])

    def id(self):
        r= [d.code for d in self.req]
        r.sort()
        return u"-".join(r)

    def title(self):
        r= [d for d in self.req]
        r.sort(key= lambda x: x.code)
        return u" + ".join([d.code+u":"+d.name for d in r])

    def md(self):
        r= ( u"""title: {req} ({cursos2:d} cursos, {vagas2:d} vagas na 2ª fase)\n\n"""
             u"""Na 1ª fase, foram colocados {vagas1} alunos em {cursos:d} cursos"""
             u""" acessíveis com {req}, com notas entre {min:.2f} e {max:.2f}.\n"""
             u"""A tabela abaixo apresenta os cursos (com vagas na 2ª fase) acessíveis com os exames de {req},\n"""
             u"""ordenada pela percentagem de vagas para a 2ª fase.\n\n""").format(
            req= self.title(), cursos=len(self.cursos), cursos2=len([c for c in self.cursos if c.vagas2 > 0]),
            vagas= self.vagas(), vagas1=self.vagas1(), vagas2=self.vagas2(), min=self.min(), max=self.max())
        return r

    def vagas(self):
        return reduce(lambda a, c: a + (c.vagas or 0), self.cursos, 0)

    def vagas1(self):
        return reduce(lambda a, c: a + (c.vagas1 or 0), self.cursos, 0)

    def vagas2(self):
        return reduce(lambda a, c: a + (c.vagas2 or 0), self.cursos, 0)

    def max(self):
        return reduce(lambda a, c: a if not c.nota1 else a if a > c.nota1 else c.nota1,
                      self.cursos, 0)

    def min(self):
        return reduce(lambda a, c: a if not c.nota1 else a if a < c.nota1 else c.nota1,
                      self.cursos, 1000)

    def semantic(self):
        #self.cursos.sort(key= lambda c: c.nota1)
        self.cursos.sort(key= lambda c: float(c.vagas2 or 0) / float(c.vagas))
        #self.cursos.reverse()

class Model:
    def __init__(self):
        self.disciplinas= dict()
        self.cursos= dict()
        self.reqs= dict()

    def disciplina(self, text):
        m= re.match(ur'(\d+)\s+(.*)', text)
        if m:
            if m.group(1) not in self.disciplinas:
                self.disciplinas[m.group(1)]= Disciplina(m.group(1), m.group(2))
            return self.disciplinas[m.group(1)]
        else:
            sys.stderr.write(text)
            sys.stderr.write("\n")

    def requirement(self, disc):
        r= frozenset(disc)
        if r not in self.reqs:
            self.reqs[r]= Requirement(r)
        return self.reqs[r]

    def second_phase(self, code, codc, vagas=None, colocados=None, ultimo=None, vagas2=None):
        c= self.cursos.get(code+codc, None)
        if c:
            c.nota2= None
            if ultimo:    c.nota1 = ultimo
            if vagas2:    c.vagas2= vagas2
            if vagas:     c.vagas = vagas
            if colocados: c.vagas1= colocados
        else:
            sys.stderr.write("Missing {0}:{1}\n".format(code, codc))

    def semantic(self):
        for c in self.cursos.values():
            for r in c.reqs:
                r.cursos.append(c)
        for r in self.reqs.values():
            r.semantic()

def parse_reqs(text, model):
    r= list()
    text= re.sub(ur'^Concurso local.<br>Consulte a institui.*?o.<br>', '', text)
    if text.startswith(u'Uma das seguintes provas:<br>'):
        discs= text.split(u'<br>')[1:]
        for d in discs:
            r.append( model.requirement([model.disciplina(d)]) )
    elif text.startswith(u'Duas das seguintes provas:<br>'):
        discs= text.split(u'<br>')[1:]
        for i in range(len(discs)-1):
            for j in range(i+1, len(discs)):
                r.append( model.requirement( [model.disciplina(discs[i]),
                                              model.disciplina(discs[j])]) )
    elif text.startswith(u'Um dos seguintes conjuntos:<br>'):
        discs= text.split(u'<br>')[1:]
        while discs:
            rr= list()
            while discs and not discs[0].endswith(u'&nbsp;ou'):
                rr.append(model.disciplina( discs.pop(0) ))
            r.append(model.requirement(rr))
            if discs: discs.pop(0)
    elif re.search(ur'&nbsp;e<br>Uma das seguintes provas:<br>', text):
        discs= text.split(u'<br>')
        d= model.disciplina( discs.pop(0) )
        discs.pop(0)
        discs.pop(0)
        while discs:
            r.append( model.requirement([d, model.disciplina(discs.pop(0))]))
    else:
        discs= text.split(u'<br>')
        if discs and discs[0]:
            r.append(model.requirement(
                [model.disciplina(d) for d in discs]))
    return r

def parse_course(fn, course, model):
    r= Curso()
    r.code= fn[-14:-10]
    r.codc= fn[-9:-5]

    m= re.search(ur'<div class="cab1">(.*?)</div>', course)
    r.curso= m.group(1)

    m= re.search(ur'<div class="cab2">(.*?)</div>', course)
    r.univ= m.group(1)

    m= re.search(ur'<br>\d{4}\s*-\s*\d{3}\s+([^0-9<]+)<br>', course)
    r.cidade= m.group(1)

    m= re.search(ur'Grau: (.*?)<br', course)
    r.grau= m.group(1)

    m= re.search(ur'Tipo de Ensino: (.*?)<br', course)
    r.tipo= m.group(1)

    m= re.search(ur'Dura.*?o:\s+(\d+)\s*Semestres<br', course)
    if m: r.tempo= int(m.group(1))/2
    else:
        m= re.search(ur'Dura.*?o:\s+(\d+)\s*Trimestres<br', course)
        if m: r.tempo= int(m.group(1))/4

    m= re.search(ur'>ECTS: (\d+)<br', course)
    if m: r.ects= int(m.group(1))

    m= re.search(ur'<br>Vagas para 2014/2015: (\d+)<br>', course)
    r.vagas= int(m.group(1))

    m= re.search(ur'<br>Provas de ingresso: (\d+)%', course)
    if m: r.provas= int(m.group(1))

    m= re.search(ur'<h2>Provas de Ingresso</h2>(.*?)<[ah]', course)
    ingresso= m.group(1) if m else u''
    r.reqs= parse_reqs(ingresso, model)

    vagas= re.findall(ur'<td class="tvag">(\d+)</td>', course)
    notas= re.findall(ur'<td class="tvag">(\d+,\d)</td>', course)

    model.cursos[r.code + r.codc]= r


model= Model()
second= None
for fn in sys.argv[1:]:
    if fn.endswith(".html"):
        sys.stderr.write(fn)
        with codecs.open(fn, "r", "iso-8859-1") as f:
            parse_course(fn, f.read(), model)
    elif fn.endswith(".csv"):
        second= fn

with codecs.open("2014-cna14_1f_resultados.csv", "r", "utf-8") as f:
    for line in f:
        a= line.split(u"\t")
        # Código da instituição;Código do curso;Nome da instituição;Nome do curso;Grau;Vagas iniciais;Colocados ;Nota de candidatura do último colocado pelo contingente geral;Vagas sobrantes
        if len(a[0])==4:
            model.second_phase(code=a[0], codc=a[1], vagas=int(a[5]), colocados=int(a[6]),
                ultimo=float(a[7].replace(u",", u"."))/10 if a[7] else None, vagas2=None)

# Em 17 de Setembro, as páginas do site tinham as vagas disponíveis em linhas como a seguinte:
#  <a href='col2vagasdet.asp?CodE=0110&CodC=9022'>18</a>
#
# Limitei-me a usar uma "one-liner" em Perl para criar o ficheiro 2014-fase2-vagas.csv:
#   curl http://www.dges.mec.pt/coloc/2014/col2vagasres.asp?CodR=11 > fase2-final-univ.html
#   curl http://www.dges.mec.pt/coloc/2014/col2vagasres.asp?CodR=12 > fase2-final-poli.html
#   perl -ne 'if (m|href=.col2vagasdet.asp[?]CodE=([\dA-Z]+)&CodC=([\dA-Z]+).>(\d+)</a>| && $3 > 0) { print "$1\t$2\t$3\n"; }' fase2-final-* > 2014-fase2-vagas.csv
#
with codecs.open("2014-fase2-vagas.csv", "r", "utf-8") as f:
    for line in f:
        a= line.split(u"\t")
        model.second_phase(code=a[0], codc=a[1], vagas2=int(a[2]))

model.semantic()

for r in model.reqs:
    rr= model.requirement(r)
    cursos2= [c for c in rr.cursos if c.vagas2 > 0]
    if len(cursos2) > 0:
        with codecs.open("2014-09-13-universidade--{0}.md".format(rr.id()), "w", "utf-8") as f:
            f.write(rr.md())
            f.write(cursos2[0].md1(rr))
            for c in cursos2:
                f.write(c.md())

with codecs.open("2014-fase2-escolhas.csv", "w", "utf-8") as f:
    f.write("Requisitos\tVagas\tÚltimo\tProvas\tCuniv\tCcurso\tCurso\tAnos\tCidade\tUniversidade\n")
    for r in model.reqs:
        rr= model.requirement(r)
        cursos2= [c for c in rr.cursos if c.vagas2 > 0]
        if len(cursos2) > 0:
            for c in cursos2:
                f.write(u"{r}\t{c.vagas2}\t{c.vagas}\t{c.nota1}\t{c.provas}%\t{c.code}\t{c.codc}\t{c.curso}\t{c.tempo}\t{cidade}\t{c.univ}\n".format(r=rr.title(), c=c, cidade=cidades[c.cidade]))
