#!/usr/bin/env ruby
# encoding: UTF-8
# coding: UTF-8

require 'fileutils'
require 'json'
require 'yaml'
require 'roo'

Encoding.default_internal = Encoding::UTF_8

# Dependencias externas #
# - roo (gema)
# - pecas
# - pandoc
# - kindlegen
# - pdflatex
# - cordova

# Funciones #

def format_date
    f = $date.to_s.split(' ')[0].split('-')

    def dia d
        return d.to_i.to_s
    end

    def mes m
        m = m.to_i

        if    m == 1
            'enero'
        elsif m == 2
            'febrero'
        elsif m == 3
            'marzo'
        elsif m == 4
            'abril'
        elsif m == 5
            'mayo'
        elsif m == 6 
            'junio'
        elsif m == 7
            'julio'
        elsif m == 8
            'agosto'
        elsif m == 9
            'septiembre'
        elsif m == 10
            'octubre'
        elsif m == 11
            'noviembre'
        else  m == 12
            'diciembre'
        end
    end

    return dia(f[2]) + ' de ' + mes(f[1]) + ' del ' + f[0]
end

# Obtiene las rutas de los proyectos
def locate project = false

    # Iteración de los repositorios hermanos
    Dir.glob('../*') do |repo|
        # Si se busca un proyecto en específico
        if project
            if File.basename(repo) == project.strip
                $projects.push(repo)
            end
        # Si se quieren todos los proyectos
        else
            # Se consideran proyectos si el repositorio empieza con «aml-» y no es «aml-general»
            if File.basename(repo) =~ /aml-\w+/ && File.basename(repo) !~ /general/
                $projects.push(repo)
            end
        end
    end

    # Aborta si no encontró ningún project
    if $projects.length == 0
        puts "ERROR: no se localizaron repositorios de la AML."
        abort
    end
end

# Translitera el nombre de los archivos para evitar errores
def transliterar texto, oracion = true
	# Elementos particulares a cambiar
	elementos1 = "ñáàâäéèêëíìîïóòôöúùûü"
	elementos2 = "naaaaeeeeiiiioooouuuu"
	
	# Pone el texto en bajas
	texto = texto.downcase
	
	# Limita el nombre a cinco palabras
    if oracion
	    texto = texto.split(/\s+/)
	    texto = texto[0..4].join("_")
    end
	
	# Cambia los elementos particulares
    texto = texto.tr(elementos1, elementos2)
	
	# Todo lo que son etiquetas viejas o nuevas de Pecas o caracteres no alfanuméricos se eliminan
	return texto.gsub(/ºº\w+?ºº/,"").gsub(/--\w+?--/,"").gsub(/\W/,"")
end

# Convierte los archivos originales a un hash
def convert_to_hash project
    hash = {}
    hash['owner'] = 'Academia Mexicana de la Lengua'
    hash['repository'] = File.basename(project)
    hash['date'] = $date

    # Si es el diccionario escolar
    if project == '../aml-escolar'
        # Extrae el contenido
        puts "Obteniendo datos del XLSX…"
        xlsx = Roo::Spreadsheet.open(project + '/src/xlsx/diccionario.xlsx')

        # Datos del hash
        puts "Creando notación de objetos…"
        hash['project'] = "Diccionario escolar"
        hash['collaborators'] = ["Moreno de Alba, José G.", "Garrido, Felipe", "Mandujano Servín, Rocío"]

        xlsx.sheet(0).each_with_index do |row, i|
            if i == 0
                hash['dictionary'] = []
            else
                hash['dictionary'].push({'word' => row[1], 'definition' => row[3].gsub('<html>', '').gsub('</html>', '')})
            end
        end
    # Si es el diccionario de mexicanismos
    elsif project == '../aml-mexicanismos'
        def docx_extract path
            # Convierte el DOCX a HTML
            puts "  Obteniendo datos de «#{File.basename(path)}»…"
            html_path = Dir.pwd + '/tmp/' + File.basename(path, '.docx') + '.html'
            system("pandoc #{path} -o #{html_path}")

            # Extrae el contenido del HTML
            archivo_abierto = File.open(html_path, 'r:UTF-8')
            archivo_abierto.each do |line|
                $docx.push(line)
            end
            archivo_abierto.close

            # Elimina el HTML porque ya no es necesario
            FileUtils.rm_rf(html_path)
        end

        # Obtiene en orden alfabético cada ruta a los archivos DOCX
        docx_paths = []
        Dir.glob(project + '/src/docx/*.docx') do |path|
            docx_paths.push(path)
        end
        docx_paths = docx_paths.sort_by{|s| transliterar(s, false)}

        # Extrae el contenido
        puts "Obteniendo datos de los DOCX…"
        $docx = []
        docx_paths.each do |docx_path|
            docx_extract(docx_path)
        end

        # Datos del hash
        puts "Creando notación de objetos…"
        hash['project'] = "Diccionario de mexicanismos"
        hash['collaborators'] = ["Company, Concepción"]
        hash['dictionary'] = []

        $docx.each_with_index do |line, i|
            if i == 0 || i.even?
                term = {'word' => line.gsub(/<.*?>/, '').strip, 'definition' => $docx[i + 1].gsub('<p>', '').gsub('</p>', '').strip}
                hash['dictionary'].push(term)
            end
        end
    end

    return hash
end

# Crea los EPUB y el MOBI
def create_epub_mobi hash, project
    letter = ''
    appear_ñ = false

    # Crea el MD que se usará para pc-automata
	archivo = File.new("tmp/#{File.basename(project)}.md", 'w:UTF-8')
    archivo.puts "# #{$warning[0]} {.centrado}\n\n#{$warning[1]} {.centrado}\n\nÚltima actualización {.espacio-arriba1 .centrado}\n\n#{$date_with_format} {.centrado}"
    hash['dictionary'].each do |term|

        # Agrega la letra del alfabeto si inicia una nueva letra
        if (transliterar(letter, false).upcase != transliterar(term['word'][0], false).upcase) || (appear_ñ == false && term['word'][0].downcase == 'ñ')
            # Necesario para localizar cuándo inicia la «ñ»
            if appear_ñ == false && term['word'][0].downcase == 'ñ'
                letter = term['word'][0].upcase
                appear_ñ = true
            else
                letter = transliterar(term['word'][0], false).upcase
            end

            archivo.puts "\n# #{letter}"
        end

        # Agrega la palabra y su definición
        archivo.puts "\n**#{term['word']}**. #{term['definition']} {.frances}"
    end
	archivo.close

    # Crea el EPUB, el MOBI y más (véase la documentación de pc-automata)
    puts "Creando proyecto EPUB…"
    system("pc-automata --directory tmp --init")
    system("pc-automata -d tmp/epub-automata -y #{project}/src/yaml/meta-data.yaml -f tmp/#{File.basename(project)}.md -c #{project}/src/img/portada.jpg -i #{project}/src/img -x #{project}/src/xhtml -s #{project}/src/css/styles.css --no-analytics --no-legacy --no-ace --overwrite")

    # Saca los EPUB y el MOBI del proyecto, los renombra y borra el resto del proyecto
    Dir.glob('tmp/epub-automata/*') do |file|
        if File.extname(file) == '.epub' || File.extname(file) == '.mobi'
            file_name = File.basename(project) + File.extname(file)
            puts "Renombrando «#{File.basename(file)}» por «#{file_name}»…"
            FileUtils.mv(file, Dir.pwd + '/tmp/' + file_name)
        end
    end
    FileUtils.rm_rf('tmp/epub-automata')

    FileUtils.rm_rf("tmp/#{File.basename(project)}.md")
end

# Crea el PDF
def create_pdf hash, project
    letter = ''
    appear_ñ = false

    Dir.mkdir('tmp/tex')
    Dir.chdir('tmp/tex')

    # Crea el MD que se usará para pc-automata
	archivo = File.new("#{File.basename(project)}.md", 'w:UTF-8')
    archivo.puts "\\newpage\n\n\\vspace*{5em}\n\n\\thispagestyle{empty}\n\n\\begin{center}\n\n\\textbf{#{$warning[0]}}\n\n\\vskip 1em #{$warning[1]}\n\n\\vskip 1em Última actualización\\linebreak#{$date_with_format}\n\n\\end{center}\n\n\\clearpage\n\n"
    hash['dictionary'].each do |term|

        def to_md text
            text.gsub("□", '$\Box$').gsub(/<\s*i\s*>/, '*').gsub(/<\/\s*i\s*>/, '*').gsub(/<\s*b\s*>/, '**').gsub(/<\/\s*b\s*>/, '**')
        end

        # Agrega la letra del alfabeto si inicia una nueva letra
        if (transliterar(letter, false).upcase != transliterar(term['word'][0], false).upcase) || (appear_ñ == false && term['word'][0].downcase == 'ñ')
            # Necesario para localizar cuándo inicia la «ñ»
            if appear_ñ == false && term['word'][0].downcase == 'ñ'
                letter = term['word'][0].upcase
                appear_ñ = true
            else
                letter = transliterar(term['word'][0], false).upcase
            end

            archivo.puts "\n\n# #{letter}"
        end

        # Agrega la palabra y su definición
        archivo.puts "\n**#{term['word']}**. #{to_md(term['definition'])}"
    end
	archivo.close

    # Copia la portada
    FileUtils.cp('../../' + project + '/src/img/portada.jpg', '.')

    # Crea el PDF
    puts "Creando PDF…"
    system("pandoc #{File.basename(project)}.md --pdf-engine=xelatex --template=../../src/latex/template.latex --toc -V lang:es -V documentclass:book -V papersize:a5 -V classoption:openany -V geometry:margin=1in -V title:\"#{hash['project']}\" -V author:\"Academia Mexicana de la Lengua\" -o #{File.basename(project)}.tex")
    system("pdflatex -synctex=1 -interaction=batchmode #{File.basename(project)}.tex")
    system("pdflatex -synctex=1 -interaction=batchmode #{File.basename(project)}.tex")
    system("pdflatex -synctex=1 -interaction=batchmode #{File.basename(project)}.tex")

    # Saca el PDF del proyecto TeX ya que se elimina
    Dir.chdir('../..')
    FileUtils.mv("tmp/tex/#{File.basename(project)}.pdf", Dir.pwd + "/tmp/#{File.basename(project)}.pdf")
    FileUtils.rm_rf('tmp/tex')
end

# Crea versión JSON y WEB
def create_json_web hash, project
    # Crea el archivo JSON
    puts "Creando JSON…"
	archivo = File.new("tmp/#{File.basename(project)}.json", 'w:UTF-8')
	archivo.puts JSON.pretty_generate(hash)
	archivo.close

    # Añade fecha de actualización al HTML
    puts "Modificando HTML…"
    html = []
    archivo_abierto = File.open(project + '/docs/index.html', 'r:UTF-8')
    archivo_abierto.each do |line|
        if line =~ /id="fecha"/
            line = line.gsub(/<span id=\"fecha\">.*?<\/span>/, '<span id="fecha">' + $date_with_format + '</span>')
        end

        html.push(line)
    end
    archivo_abierto.close

	archivo_abierto = File.new(project + '/docs/index.html', 'w:UTF-8')
	archivo_abierto.puts html
	archivo_abierto.close
end

# Crea versión APK
def create_apk project
    # Cambia la versión a una superior
    puts "Creando APK…"
    def change_version text
        version = text.split('"')[1].split('.')
        subversion = version.last.to_i + 1

        return 'version="' + version[0] + '.' + version[1] + '.' + subversion.to_s + '"'
    end

    # Copia la carpeta de la versión web a la carpeta temporal y la renombra como «www»
    FileUtils.cp_r(project + '/docs', Dir.pwd + '/tmp')
    FileUtils.mv(Dir.pwd + '/tmp/docs', Dir.pwd + '/tmp/www')

    # Elimina soportes finales innecesarios
    Dir.glob(Dir.pwd + '/tmp/www/files/*.*') do |file|
        if File.extname(file) != '.json'
            FileUtils.rm_rf(file)
        end
    end

    # Modifica pie del HTML
    puts "Modificando HTML para el APK…"
    html = []
    archivo_abierto = File.open(Dir.pwd + '/tmp/www/index.html', 'r:UTF-8')
    archivo_abierto.each do |line|
        if line !~ /no-apk/
            html.push(line)
        end
    end
    archivo_abierto.close

	archivo_abierto = File.new(Dir.pwd + '/tmp/www/index.html', 'w:UTF-8')
	archivo_abierto.puts html
	archivo_abierto.close

    # Añade los archivos al proyecto de cordova
    puts "Añadiendo archivos a cordova…"
    FileUtils.rm_rf(project + '/cordova/www')
    FileUtils.mv(Dir.pwd + '/tmp/www', project + '/cordova')

    # Modifica la versión en el config.xml si se desea
    if $version_increase == true
        puts "Añadiendo número de versión…"
        config = []
        archivo_abierto = File.open(project + '/cordova/config.xml', 'r:UTF-8')
        archivo_abierto.each do |line|
            if line =~ /<widget/
                line = line.gsub(/version=".*?"/, change_version(line.scan(/version=".*?"/)[0]))
            end

            config.push(line)
        end
        archivo_abierto.close

	    archivo_abierto = File.new(project + '/cordova/config.xml', 'w:UTF-8')
	    archivo_abierto.puts config
	    archivo_abierto.close
    end

    # Crea el APK
    puts "Compilando APK…"
    Dir.chdir(project + '/cordova')
    system("cordova build android")
    Dir.chdir('../../aml-general')

    # Copia el APK a la carpeta temporal y lo renombra
    FileUtils.cp_r(project + '/cordova/platforms/android/app/build/outputs/apk/debug/app-debug.apk', Dir.pwd + '/tmp')
    FileUtils.mv(Dir.pwd + '/tmp/app-debug.apk', Dir.pwd + '/tmp/' + File.basename(project) + '.apk')
end

# Elimina archivos previos, manda soportes finales a su destino y firma estos archivos
def finish project
    # Elimina los archivos previos de la carpeta «/docs/files» de cada repo
    puts "Eliminando archivos previos de «#{File.basename(project)}/docs/files»…"
    Dir.glob(project + '/docs/files/*') do |file|
        FileUtils.rm_rf(file)
    end

    # Manda todos los archivos a la carpeta «/docs/files» de cada repo
    puts "Moviendo archivos finales a «#{File.basename(project)}/docs/files»…"
    Dir.glob('tmp/*') do |file|
        FileUtils.mv(file, project + '/docs/files')
    end

    # Firma los archivos
    puts "Firmando archivos…"
    Dir.glob(project + '/docs/files/*.*').each do |file|
        system("gpg --output #{file}.sig --detach-sig #{file}")
    end

    # Elimina la carpeta temporal
    FileUtils.rm_rf('tmp')
end

def push project
    if $push == true
        puts "Guardando cambios en el repositorio…"
        Dir.chdir(project)
        system("git add . && git commit -m \"Guardado automático de aml-general: #{$date}\" && git push origin master && git-push-all")
        Dir.chdir('../aml-general')
    end
end

# Variables #

$projects = []
$date = Time.now
$date_with_format = format_date
$warning = ["Advertencia", "La edición de la presente obra solo es preliminar. Todavía falta su adaptación a las normas de publicación de la lengua española."]
$version_increase = true
$push = false

# Va a la carpeta del script
Dir.chdir(File.dirname(__FILE__))

# Localiza los proyectos y las configuraciones
if ARGF.argv.length == 0
    locate
else
    project_path = ''

    # Busca el tipo de argumento presente
    ARGF.argv.each do |argument|
        if argument == '--no-version-increase'
            $version_increase = false
        elsif argument == '--push'
            $push = true
        elsif argument == '-h' || argument == '--help'
            puts "Genera formatos APK, EPUB, JSON, MOBI, PDF y WEB del «Diccionario escolar» y del «Diccionario de mexicanismos» de la Academia Mexicana de la Lengua.\n\nNota: requiere los repositorios «aml-escolar» o «aml-mexicanismos» como directorios hermanos a este repositorio.\n\nUso:\n  ruby crear.rb\n\nParámetros opcionales:\n  proyecto: Nombre del proyecto en específico para no generar todos (p. ej. «aml-escolar» o «aml-mexicanismos»).\n  --no-version-increase: Evita incrementar el número de versión del APK.\n  --push: Hace push a los repositorios de cada obra.\n  -h || --help: Muestra esta ayuda."
            abort
        else
            project_path = argument
        end
    end 

    # Determina si se crearán todos o solo un proyecto
    if project_path == ''
        locate
    else
        locate(project_path)
    end
end

# Inicia la creación de cada formato de cada proyecto
$projects.each do |project|
    puts "\n========================================\n\n"
    puts "Iniciando automatización del repositorio «#{File.basename(project)}»…"

    # Crea una carpeta temporal donde se estarán poniendo los archivos preliminares
    FileUtils.rm_rf('tmp')
    Dir.mkdir('tmp')

    # Obtiene la información
    hash = convert_to_hash(project)

    # Crea los archivos EPUB y MOBI
    create_epub_mobi(hash, project)

    # Crea el archivo PDF
    create_pdf(hash, project)

    # Crea el archivo JSON y modifica la versión web
    create_json_web(hash, project)

    # Crea el archivo APK
    create_apk(project)

    # Manda los archivos a su destino y los firma
    finish(project)

    # Hace push al repositorio si se desea
    push(project)
end