# -*- coding: utf-8 -*-

##########################################################################
# OpenLP - Open Source Lyrics Projection                                 #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2024 OpenLP Developers                              #
# ---------------------------------------------------------------------- #
# This program 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 <https://www.gnu.org/licenses/>. #
##########################################################################
"""
The :mod:`singingthefaith` module provides the functionality for importing songs which are
exported from Singing The Faith - an Authorised songbook for the Methodist Church of
Great Britain."""

import logging
import re
from pathlib import Path

from openlp.core.common.i18n import translate
from openlp.plugins.songs.lib.importers.songimport import SongImport
from openlp.core.common.applocation import AppLocation


log = logging.getLogger(__name__)


class SingingTheFaithImport(SongImport):
    """
    Import songs exported from SingingTheFaith
    """

    def __init__(self, manager, **kwargs):
        """
        Initialise the class.
        """
        super(SingingTheFaithImport, self).__init__(manager, **kwargs)
        self.hints_available = False
        self.checks_needed = True
        self.hint_line = {}
        self.hint_file_version = '0'
        self.hint_verse_order = ''
        self.hint_song_title = ''
        self.hint_comments = ''
        self.hint_ccli = ''
        self.hint_ignore_indent = False
        self.hint_songbook_number_in_title = False

    def do_import(self):
        """
        Receive a single file or a list of files to import.
        """
        if not isinstance(self.import_source, list):
            return
        self.import_wizard.progress_bar.setMaximum(len(self.import_source))
        for file_path in self.import_source:
            if self.stop_import_flag:
                return
            # If this is backported to version 2.4 then do_import is called with a filename
            #  rather than a path object if called from the development version.
            # Check here to minimise differences between versions.
            if isinstance(file_path, str):
                song_file = open(file_path, 'rt', encoding='cp1251')
                self.do_import_file(song_file)
                song_file.close()
            else:
                with file_path.open('rt', encoding='cp1251') as song_file:
                    self.do_import_file(song_file)

    def do_import_file(self, file):
        """
        Process the SingingTheFaith file - pass in a file-like object, not a file path.
        """
        hints_file_name = 'singingthefaith-hints.tag'
        singing_the_faith_version = 1
        self.set_defaults()
        # Setup variables
        line_number = 0
        old_indent = 0
        # The chorus indent is how many spaces the chorus is indented - it might be 6,
        # but we test for >= and I do not know how consistent to formatting of the
        # exported songs is.
        chorus_indent = 5
        # Initialise the song title - the format of the title finally produced can be affected
        #  by the SongbookNumberInTitle option in the hints file
        song_title = 'STF000 -'
        song_number = '0'
        ccli = '0'
        current_verse = ''
        current_verse_type = 'v'
        current_verse_number = 1
        # Potentially we could try to track current chorus number to automatically handle
        # more than 1 chorus, currently unused.
        # current_chorus_number = 1
        has_chorus = False
        chorus_written = False
        auto_verse_order_ok = False
        copyright = ''
        # the check_flag is prepended to the title, removed if the import should be OK
        # all the songs which need manual editing should sort below all the OK songs
        check_flag = 'z'

        self.add_comment(
            'Imported with Singing The Faith Importer  v{no}'.format(no=singing_the_faith_version))

        # Get the file_song_number - so we can use it for hints
        filename = Path(file.name)
        song_number_file = filename.stem
        song_number_match = re.search(r'\d+', song_number_file)
        if song_number_match:
            song_number_file = song_number_match.group()

        # See if there is a hints file in the same location as the file
        dir_path = filename.parent
        hints_file_path = dir_path / hints_file_name
        try:
            with hints_file_path.open('r') as hints_file:
                hints_available = self.read_hints(hints_file, song_number_file)
        except FileNotFoundError:
            # Look for the hints file in the Plugins directory
            hints_file_path = AppLocation.get_directory(AppLocation.PluginsDir) / 'songs' / 'lib' / \
                'importers' / hints_file_name
            try:
                with hints_file_path.open('r') as hints_file:
                    hints_available = self.read_hints(hints_file, song_number_file)
            except FileNotFoundError:
                hints_available = False

        try:
            for line in file:
                line_number += 1
                # Strip out leftover formatting (\i and \b)
                line = line.replace('\\i', '')
                line = line.replace('\\b', '')
                if hints_available and str(line_number) in self.hint_line:
                    hint = self.hint_line[str(line_number)]
                    # Set to false if this hint does not replace the line
                    line_replaced = True
                    if hint == 'Comment':
                        line.strip()
                        self.add_comment(line)
                        continue
                    elif hint == 'Ignore':
                        continue
                    elif hint == 'Author':
                        # add as a raw author - do not split
                        line.strip()
                        self.add_author(line)
                        line_number += 1
                        next(file)
                        continue
                    elif hint.startswith('VariantVerse'):
                        vv, hintverse, replace = hint.split(' ', 2)
                        this_verse = self.verses[int(hintverse) - 1]
                        this_verse_str = this_verse[1]
                        new_verse = this_verse_str
                        # There might be multiple replace pairs separated by |
                        replaces = replace.split('|')
                        for rep in replaces:
                            source_str, dest_str = rep.split('/')
                            new_verse = new_verse.replace(source_str, dest_str)
                        self.add_verse(new_verse, 'v')
                        self.verse_order_list.append('v{}'.format(str(current_verse_number)))
                        current_verse_number += 1
                        line_number += 1
                        next(file)
                        continue
                    elif hint == 'AddSpaceAfterSemi':
                        line = line.replace(';', '; ')
                        line_replaced = False
                        # note - do not use contine here as the line should now be processed as normal.
                    elif hint == 'AddSpaceAfterColon':
                        line = line.replace(':', ': ')
                        line_replaced = False
                    elif hint == 'BlankLine':
                        line = ' *Blank*'
                        line_replaced = False
                    elif hint == 'BoldLine':
                        # processing of the hint is deferred, but pick it up as a known hint here
                        line_replaced = False
                    else:
                        self.log_error(translate('SongsPlugin.SingingTheFaithImport',
                                       'File {file})'.format(file=file.name)),
                                       translate('SongsPlugin.SingingTheFaithImport',
                                       'Unknown hint {hint}').format(hint=hint))
                    if line_replaced:
                        return
                # STF exported lines have a leading verse number at the start of each verse.
                #  remove them - note that we want to track the indent as that shows a chorus
                # so will deal with that before stripping all leading spaces.
                indent = 0
                if line.strip():
                    # One hymn has one line which starts '* 6' at the start of a verse
                    # Strip this out
                    if line.startswith('* 6'):
                        line = line.lstrip('* ')
                    verse_num_match = re.search(r'^\d+', line)
                    if verse_num_match:
                        # Could extract the verse number and check it against the calculated
                        # verse number - TODO
                        # verse_num = verse_num_match.group()
                        line = line.lstrip('0123456789')
                    indent_match = re.search(r'^\s+', line)
                    if indent_match:
                        indent = len(indent_match.group())
                # Assuming we have sorted out what is verse and what is chorus, strip lines,
                # unless ignoreIndent
                if self.hint_ignore_indent:
                    line = line.rstrip()
                else:
                    line = line.strip()
                if line_number == 2:
                    # note that songs seem to start with a blank line so the title is line 2
                    # Also we strip blanks from the title, even if ignoring indent.
                    song_title = line.strip()
                # Process possible line formatting hints after the verse number has been removed
                if hints_available and str(line_number) in self.hint_line and hint == 'BoldLine':
                    line = '{{st}}{0}{{/st}}'.format(line)
                # Detect the 'Reproduced from Singing the Faith Electronic Words Edition' line
                if line.startswith('Reproduced from Singing the Faith Electronic Words Edition'):
                    song_number_match = re.search(r'\d+', line)
                    if song_number_match:
                        song_number = song_number_match.group()
                        continue
                elif indent == 0:
                    # If the indent is 0 and it contains '(c)' then it is a Copyright line
                    if '(c)' in line:
                        copyright = line
                        continue
                    elif (line.startswith('Liturgical ') or line.startswith('From The ') or
                          line.startswith('From Common ') or line.startswith('Based on Psalm ')):
                        self.add_comment(line)
                        continue
                    # If indent is 0 it may be the author, unless it was one of the cases covered above
                    elif len(line) > 0:
                        # May have more than one author, separated by ' and '
                        authors = line.split(' and ')
                        for a in authors:
                            self.parse_author(a)
                        continue
                # If a blank line has bee replaced by *Blank* then put it back to being
                # a simple space since this is past stripping blanks
                if '*Blank*' in line:
                    line = ' '
                if line == '':
                    if current_verse != '':
                        self.add_verse(current_verse, current_verse_type)
                        self.verse_order_list.append(current_verse_type + str(current_verse_number))
                        if current_verse_type == 'c':
                            chorus_written = True
                        else:
                            current_verse_number += 1
                    current_verse = ''
                    if chorus_written:
                        current_verse_type = 'v'
                else:
                    # If the line is indented more than or equal chorus_indent then assume it is a chorus
                    # If the indent has just changed then start a new verse just like hitting a blank line
                    if not self.hint_ignore_indent and ((indent >= chorus_indent) and (old_indent < indent)):
                        if current_verse != '':
                            self.add_verse(current_verse, current_verse_type)
                            self.verse_order_list.append(current_verse_type + str(current_verse_number))
                            if current_verse_type == 'v':
                                current_verse_number += 1
                        current_verse = line
                        current_verse_type = 'c'
                        old_indent = indent
                        chorus_written = False
                        has_chorus = True
                        continue
                    if current_verse == '':
                        current_verse += line
                    else:
                        current_verse += '\n' + line
                old_indent = indent
        except Exception as e:
            self.log_error(translate('SongsPlugin.SingingTheFaithImport', 'File {file}').format(file=file.name),
                           translate('SongsPlugin.SingingTheFaithImport', 'Error: {error}').format(error=e))
            return

        if self.hint_song_title:
            song_title = self.hint_song_title
        self.title = '{}STF{} - {title}'.format(check_flag, song_number.zfill(3), title=song_title)
        self.song_book_name = 'Singing The Faith'
        self.song_number = song_number
        self.ccli_number = ccli
        self.add_copyright(copyright)
        # If we have a chorus then the generated Verse order can not be used directly, but we can generate
        #  one for two special cases - Verse followed by one chorus (to be repeated after every verse)
        #  of Chorus, followed by verses. If hints for ManualCheck or VerseOrder are supplied ignore this
        if has_chorus and not self.hint_verse_order and not self.checks_needed:
            auto_verse_order_ok = False
            # Popular case V1 C2 V2 ...
            if self.verse_order_list:         # protect against odd cases
                if self.verse_order_list[0] == 'v1' and self.verse_order_list[1] == 'c2':
                    new_verse_order_list = ['v1', 'c1']
                    i = 2
                    auto_verse_order_ok = True
                elif self.verse_order_list[0] == 'c1' and self.verse_order_list[1] == 'v1':
                    new_verse_order_list = ['c1', 'v1', 'c1']
                    i = 2
                    auto_verse_order_ok = True
                # if we are in a case we can deal with
                if auto_verse_order_ok:
                    while i < len(self.verse_order_list):
                        if self.verse_order_list[i].startswith('v'):
                            new_verse_order_list.append(self.verse_order_list[i])
                            new_verse_order_list.append('c1')
                        else:
                            auto_verse_order_ok = False
                            self.add_comment('Importer detected unexpected verse order entry {}'.format(
                                self.verse_order_list[i]))
                        i += 1
                    self.verse_order_list = new_verse_order_list
            else:
                if not auto_verse_order_ok:
                    self.verse_order_list = []
        if self.hint_verse_order:
            self.verse_order_list = self.hint_verse_order.split(',')
        if self.hint_comments:
            self.add_comment(self.hint_comments)
        if self.hint_ccli:
            self.ccli_number = self.hint_ccli
        # Write the title last as by now we will know if we need checks
        if hints_available and not self.checks_needed:
            check_flag = ''
        elif not hints_available and not has_chorus:
            check_flag = ''
        elif not hints_available and has_chorus and auto_verse_order_ok:
            check_flag = ''
        if self.hint_songbook_number_in_title:
            self.title = '{}STF{} - {title}'.format(check_flag, song_number.zfill(3), title=song_title)
        else:
            self.title = '{}{title}'.format(check_flag, title=song_title)
        if not self.finish():
            self.log_error(file.name)

    def read_hints(self, file, song_number):
        """
        Read the hints used to transform a particular song into version which can be projected,
        or improve the transformation process beyond the standard heuristics. Not every song will
        have, or need, hints.
        """
        hintfound = False
        self.hint_verse_order = ''
        self.hint_line.clear()
        self.hint_comments = ''
        self.hint_song_title = ''
        self.hint_ignore_indent = False
        self.hint_ccli = ''
        for tl in file:
            if not tl.strip():
                return hintfound
            tagval = tl.split(':')
            tag = tagval[0].strip()
            val = tagval[1].strip()
            if tag == 'Version':
                self.hint_file_version = val
                continue
            elif tag == 'SongbookNumberInTitle':
                if val == 'False':
                    self.hint_songbook_number_in_title = False
                else:
                    self.hint_songbook_number_in_title = True
                continue
            elif tag == 'Comment':
                continue
            if (tag == 'Hymn') and (val == song_number):
                self.add_comment('Using hints version {}'.format(str(self.hint_file_version)))
                hintfound = True
                # Assume, unless the hints has ManualCheck that if hinted all will be OK
                self.checks_needed = False
                for tl in file:
                    tagval = tl.split(':')
                    tag = tagval[0].strip()
                    val = tagval[1].strip()
                    if tag == 'End':
                        return hintfound
                    elif tag == 'CommentsLine':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'Comment'
                    elif tag == 'IgnoreLine':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'Ignore'
                    elif tag == 'AuthorLine':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'Author'
                    elif tag == 'AddSpaceAfterSemi':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'AddSpaceAfterSemi'
                    elif tag == 'AddSpaceAfterColon':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'AddSpaceAfterColon'
                    elif tag == 'BlankLine':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'BlankLine'
                    elif tag == 'BoldLine':
                        vals = val.split(',')
                        for v in vals:
                            self.hint_line[v] = 'BoldLine'
                    elif tag == 'VerseOrder':
                        self.hint_verse_order = val
                    elif tag == 'ManualCheck':
                        self.checks_needed = True
                    elif tag == 'IgnoreIndent':
                        self.hint_ignore_indent = True
                    elif tag == 'VariantVerse':
                        vvline = val.split(' ', 1)
                        self.hint_line[vvline[0].strip()] = 'VariantVerse {}'.format(vvline[1].strip())
                    elif tag == 'SongTitle':
                        self.hint_song_title = val
                    elif tag == 'AddComment':
                        self.hint_comments += '\n' + val
                    elif tag == 'CCLI':
                        self.hint_ccli = val
                    elif tag == 'Hymn':
                        self.log_error(file.name, 'Missing End tag in hint for Hymn: {}'.format(song_number))
                    else:
                        self.log_error(file.name, 'Unknown tag {} value {}'.format(tag, val))
        return hintfound
