#!/usr/bin/env python3

#----------------------------------------------------------------------------
# 
# Copyright (C) 2015 José Flávio de Souza Dias Júnior
# 
# This file is part of CXXII - http://www.joseflavio.com/cxxii/
# 
# CXXII is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# CXXII is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public License
# along with CXXII. If not, see http://www.gnu.org/licenses/.
# 
#----------------------------------------------------------------------------
# 
# Direitos Autorais Reservados (C) 2015 José Flávio de Souza Dias Júnior
# 
# Este arquivo é parte de CXXII - http://www.joseflavio.com/cxxii/
# 
# CXXII é software livre: você pode redistribuí-lo e/ou modificá-lo
# sob os termos da Licença Pública Menos Geral GNU conforme publicada pela
# Free Software Foundation, tanto a versão 3 da Licença, como
# (a seu critério) qualquer versão posterior.
# 
# CXXII é distribuído na expectativa de que seja útil,
# porém, SEM NENHUMA GARANTIA; nem mesmo a garantia implícita de
# COMERCIABILIDADE ou ADEQUAÇÃO A UMA FINALIDADE ESPECÍ
FICA. Consulte a
# Licença Pública Menos Geral do GNU para mais detalhes.
# 
# Você deve ter recebido uma cópia da Licença Pública Menos Geral do GNU
# junto com CXXII. Se não, veja http://www.gnu.org/licenses/.
# 
#---------------------------------------------
-------------------------------

# "Não vos conformeis com este mundo,
# mas transformai-vos pela renovação do vosso espírito,
# para que possais discernir qual é a vontade de Deus,
# o que é bom, o que lhe agrada e o que é perfeito."
# (Bíblia Sagrada, Romanos 12:2)

# "Do not conform yourselves to this age
# but be transformed by the renewal of your mind,
# that you may discern what is the will of God,
# what is good and pleasing and perfect."
# (Holy Bible, Romans 12:2)

import sys
if sys.version_info[0] < 3:
    print('CXXII exige Python 3 ou mais recente.')
    sys.exit(1)


import os
import time
import datetime
import unicodedata
import urllib.request
import tempfile
import zipfile
import re
from xml.etree.ElementTree import ElementTree as CXXII_XML_Arvore


#----------------------------------------------------------------------------


class CXXII_XML_Arquivo:
    """Classe que representa um arquivo XML."""

    def __init__(self, endereco):
        self.endereco = endereco
        self.nome = NomeDoArquivo(endereco)
        self.arvore = CXXII_XML_Arvore()
        self.arvore.parse(endereco)

    def raiz(self):
        return self.arvore.getroot()


def CXXII_Baixar( url, destino=None, nome=None, forcar=False ):
    """Download de arquivo."""
    if url[-1]     == '/'    : url     = url[0:-1]
    if destino     is None   :
        global CXXII_Diretorio
        destino = CXXII_Diretorio
    if destino[-1] == os.sep : destino = destino[0:-1]
    if nome        is None   : nome    = url.replace('/', '_').replace(':', '_')
    endereco = destino + os.sep + nome
    existe = os.path.exists(endereco)
    baixar = forcar or not existe
    if not baixar:
        global CXXII_Gerador_TempoMaximo
        baixar = ( time.time() - os.path.getmtime(endereco) ) > CXXII_Gerador_TempoMaximo
    if baixar:
        try:
            urllib.request.urlretrieve(url, endereco)
        except:
            raise Exception('Não foi possível baixar o gerador.')
    return endereco


def CXXII_XML_Adicionar( endereco ):
    global CXXII_XML_Arquivos
    CXXII_XML_Arquivos.append( CXXII_XML_Arquivo(endereco) )


def CXXII_Separadores( endereco ):
    """Substitui os separadores "/" pelo separador real do sistema operacional."""
    return endereco.replace('/', os.sep)


def CXXII_Python_Formato( arquivo ):
    """Retorna o valor de "# coding=".

    arquivo -- Arquivo ou endereço de arquivo. Pode-se utilizar "/" como separador.
    """
    if type(arquivo) is str: arquivo = open( CXXII_Separadores(arquivo), 'r', encoding='iso-8859-1' )
    formato = 'utf-8'
    arquivo.seek(0)
    for i in range(2):
        linha = arquivo.readline()
        if linha.startswith('# coding='):
            formato = linha[9:-1]
            break
    arquivo.close()
    return formato


def CXXII_Abrir_Python( endereco ):
    """Abre um arquivo ".py" respeitando a codificação especificada no cabeçalho "# coding=".

    endereco -- Endereço do arquivo. Pode-se utilizar "/" como separador.
    """
    endereco = CXXII_Separadores(endereco)
    return open( endereco, 'r', encoding=CXXII_Python_Formato(endereco) )


def CXXII_Atual( endereco, modo='w', formato='utf-8' ):
    """Determina o arquivo em geração atual.

    endereco -- Endereço do arquivo desejado, relativo ao CXXII_Destino. Pode-se utilizar "/" como separador.
    """
    global CXXII_Saida
    global CXXII_Destino
    endereco = CXXII_Separadores(endereco)
    if endereco[0] != os.sep: endereco = os.sep + endereco
    if CXXII_Saida != None and CXXII_Saida != sys.stdout: CXXII_Saida.close()
    arquivo = CXXII_Texto(CXXII_Destino + endereco)
    diretorio = CXXII_Texto(os.path.dirname(arquivo))
    if not os.path.exists(diretorio): os.makedirs(diretorio)
    CXXII_Saida = open( arquivo, modo, encoding=formato )


def CXXII_Escrever( texto ):
    """Escreve no arquivo em geração atual. Ver CXXII_Atual()."""
    global CXXII_Saida
    if not CXXII_Saida is None:
        CXXII_Saida.write(texto)


def CXXII_ContarIdentacao( linha ):
    comprimento = len(linha)
    if comprimento == 0: return 0
    tamanho = comprimento - len(linha.lstrip())
    if linha[0] == ' ': tamanho /= 4
    return tamanho


def CXXII_Identar( linha, total=1 ):
    espaco = '\t' if len(linha) > 0 and linha[0] == '\t' else '    '
    while total > 0:
        linha = espaco + linha
        total -= 1
    return linha


def CXXII_EscreverArquivo( endereco, inicio=1, fim=None, quebraFinal=True, formato='utf-8', dicGlobal=None, dicLocal=None ):
    """Escreve no arquivo em geração atual (CXXII_Atual()) o conteúdo de um arquivo-modelo.

    Arquivo-modelo é qualquer conteúdo que contenha instruções CXXII embutidas.
    Se o endereço do arquivo for relativo, ele primeiro será buscado em CXXII_Gerador_Diretorio.

    endereco    -- Endereço do arquivo-modelo. Pode-se utilizar "/" como separador.
    inicio      -- Número inicial do intervalo de linhas desejado. Padrão: 1
    fim         -- Número final do intervalo de linhas desejado. Padrão: None (última linha)
    quebraFinal -- Quebra de linha na última linha?
    dicGlobal   -- Ver globals()
    dicLocal    -- Ver locals()
    """
    global CXXII_Saida
    if CXXII_Saida is None: return

    if dicGlobal is None: dicGlobal = globals()
    if dicLocal  is None: dicLocal  = locals()

    endereco = CXXII_Separadores(endereco)
    if endereco[0] != os.sep and os.path.exists(CXXII_Gerador_Diretorio + os.sep + endereco):
        endereco = CXXII_Gerador_Diretorio + os.sep + endereco

    codigo = []
    modelo = open( endereco, 'r', encoding=formato )
    linhas = list(modelo)
    modelo.close()

    total = len(linhas)
    if inicio != 1 or fim != None:
        inicio = inicio - 1 if inicio != None else 0
        fim = fim if fim != None else total
        linhas = linhas[inicio:fim]
        if not quebraFinal and linhas[-1][-1] == '\n': linhas[-1] = linhas[-1][0:-1]
        total = len(linhas)

    identacao = 0
    i = 0

    while i < total:
        linha = linhas[i]
        if linha == '@@@\n':
            i += 1
            if i < total and identacao > 0 and linhas[i] == '@@@\n':
                identacao -= 1
            else:
                while i < total and linhas[i] != '@@@\n':
                    linha = linhas[i]
                    identacao = CXXII_ContarIdentacao(linha)
                    codigo.append(linha)
               