#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net )

This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),

Laborejo2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

"""Use generated music data and build a complete lilypond file from it"""

import logging; logger = logging.getLogger(__name__); logger.info("import")

#Standard Library Modules
import os.path
from datetime import date
import subprocess
from tempfile import gettempdir

#Third Party Modules

#Template Modules
from template.start import PATHS

#Our modules


da = date.fromordinal(730920) # 730920th day after 1. 1. 0001

def saveAsLilypond(score, absoluteFilePath = None):
    #absoluteFilePath = self.absoluteFilePath + ".ly" if (not absoluteFilePath) else absoluteFilePath
    assert absoluteFilePath.endswith(".ly")
    result = score.lilypond()
    if result:
        with open(absoluteFilePath, "w", encoding="utf-8") as f:
            f.write(result)
        return absoluteFilePath

def saveAsLilypondPDF(score, openPDF = False):
    tempfile = os.path.join(gettempdir(), str(id(score)) + ".ly")
    exportedLilypond = saveAsLilypond(score, tempfile)
    ret = subprocess.call("lilypond --output={} {}".format(exportedLilypond, exportedLilypond), shell=True)  #suffix to output added by lilypond
    if ret == 0 and openPDF:
        subprocess.Popen("xdg-open {}.pdf".format(exportedLilypond), shell=True)

def stringToCharsOnlyString(string):
    def num2word(n):
        """Take a number, return a str.
        Lilypond does not allow numbers in variable names"""
        return ''.join(chr(ord(c)+17) for c in str(n))

    def num2wordForIterations(stringOrNum):
        if stringOrNum in [1,2,3,4,5,6,7,8,9,0] or stringOrNum in "1234567890":
            return num2word(int(stringOrNum))
        elif stringOrNum in ("-", "_"):
            return ""
        else:
            return stringOrNum

    string = string.replace(" ", "_")
    return "".join([num2wordForIterations(c) for c in string])

def lilyfy(string):
    return string

def fromTemplate(session, templateFile, data, meta, tempoStaff):
    """Returns a string built from already ly-exported track data and a lilypond template.
    Called by score.lilypond(), which is called by session.lilypond().
    A template is either one of the built-ins "foo.ly" or
    if the templateFile is None/"" the current filename+ly is used: "bar.lbj.ly"

    Laborejo Markers in the template have the syntax %$$DATE$$ . That is pretty unique and additionaly a lilypond
    comment.
    """
    #Load the template
    templatePath = findTemplate(session, templateFile)
    with open(templatePath, 'r') as f:
        templateString = f.read()

    templateString = templateString.replace("%$$DATE$$", da.today().strftime("%A %d. %B %Y")) #The current date
    templateString = templateString.replace("%$$FILENAME$$", session.sessionPrefix)
    templateString = templateString.replace("%$$HEADER$$", processMeta(meta))
    templateString = templateString.replace("%$$SUBTEXT$$", lilyfy(meta["subtext"]))
    voicesString, structureString = processData(data)
    templateString = templateString.replace("%$$VOICES$$", voicesString)
    templateString = templateString.replace("%$$STRUCTURE$$", structureString)
    templateString = templateString.replace("%$$TEMPOSTAFF$$", tempoStaff)

    return templateString

def findTemplate(session, templateFile:str=None)->str:
    """returns a path. checks for existence.
    
    There are two options:
    - Use a standard template in SHARE/lilypondTemplates
    - Use a template that belongs to the save file.
    
    The one that belongs to the save file needs to be created by hand by the user.
    If not we throw a FileNotFoundError"""
    if templateFile:
        path = os.path.join(PATHS["share"], "lilypondTemplates", templateFile)    
    else:
        path = os.path.join(session.sessionPrefix, "template.laborejo2.ly") #the name is always the same because the sessionPrefix is the unqiue part
    assert path.endswith(".ly")
    if os.path.exists(path):
        return path
    else:
        raise FileNotFoundError(templateFile)

def processData(data):
    """returns two strings. the first actual music data, VOICES, the second the structure and order,
    STRUCTURE"""
    def voice(track, lilypondTrack):
        """ tgliHJuGCFAIICICBEHBJABHIJE= { \key c \major <c'>4 <e'>4 <g'>4 <c''>4 \bar "|."} """
        return "{} = {}".format(stringToCharsOnlyString(track.name), "{ " + lilypondTrack + " }")

    def structure(track, lilypondTrack):
        """ \new Staff = "tgliHJuGCFAIICICBEHBJABHIJE_Staff"  <<  \\new Voice = "tgliHJuGCFAIICICBEHBJABHIJE" \tgliHJuGCFAIICICBEHBJABHIJE  >> %EndTrack """
        name = stringToCharsOnlyString(track.name)
        instruments = '\\with {{ \n instrumentName = #"{}" \n shortInstrumentName = #"{}" }}'.format(lilyfy(track.initialInstrumentName), lilyfy(track.initialShortInstrumentName))
        mergeTempo = "\\new Voice = \"Tempo\" \\tempoStaff"
        return  """\\new Staff = "{}_Staff" {} << {} \\new Voice = "{}" \{}  >> %EndTrack""".format(name, instruments, mergeTempo, name, name)

    voices = "\n  ".join(voice(track, lilypondTrack) for track, lilypondTrack in data.items() if lilypondTrack)
    structure = "\n  ".join(structure(track, lilypondTrack) for track, lilypondTrack in data.items() if lilypondTrack)

    return voices, structure


def processMeta(meta):
    """returns a string with lilypond header data like title and composer"""

    def valueMarkup(value):
        value = value.strip()
        if value:
            if value.startswith("\\markup"):
                return value
            else:
                return "\"{}\"".format(value) #results in "value"
        else:
            return "##f"

    metaString = "\n    ".join(key + " = " + valueMarkup(value) for key, value in meta.items())

    return metaString
