# This file is part of lyx2lyx
# Copyright (C) 2024 The LyX team
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

"""Convert files to the file format generated by lyx 2.5"""

import re

# Uncomment only what you need to import, please (lyx2lyx_tools):
#    convert_info_insets, get_ert, insert_to_preamble,
#    length_in_bp, lyx2verbatim,
#    revert_flex_inset, revert_flex_inset, revert_font_attrs,
#    str2bool
from lyx2lyx_tools import (
    add_to_preamble,
    hex2ratio,
    latex_length,
    lyx2latex,
    put_cmd_in_ert,
    revert_language
)

# Uncomment only what you need to import, please (parser_tools):
#    check_token, count_pars_in_inset, del_complete_lines, 
#    del_value, find_complete_lines, 
#    find_re, find_token_backwards, find_token_exact,
#    find_tokens,
#    get_option_value,
#    is_in_inset
from parser_tools import (
    del_token,
    find_end_of,
    find_end_of_inset,
    find_end_of_layout,
    find_re,
    find_substring,
    find_token,
    get_containing_inset,
    get_containing_layout,
    get_bool_value,
    get_quoted_value,
    get_value,
    set_bool_value
)

####################################################################
# Private helper functions


###############################################################################
###
### Conversion and reversion routines
###
###############################################################################


def convert_url_escapes(document):
    """Unescape # and % in URLs with hyperref."""

    hyperref = find_token(document.header, "\\use_hyperref true", 0) != -1
    beamer = document.textclass in [
        "beamer",
        "scrarticle-beamer",
        "beamerposter",
        "article-beamer",
    ]

    if not hyperref and not beamer:
        return

    rurl = re.compile(r"^[%#].*")
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Flex URL", i)
        if i == -1:
            return
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Malformed LyX document: Could not find end of URL inset.")
            i += 1
            continue
        while True:
            surl = find_re(document.body, rurl, i, j)
            if surl == -1:
                i = j
                break
            if document.body[surl - 1] == "\\backslash":
                del document.body[surl - 1]
            i = surl


def revert_url_escapes(document):
    """Unescape # and % in URLs with hyperref."""

    hyperref = find_token(document.header, "\\use_hyperref true", 0) != -1
    beamer = document.textclass in [
        "beamer",
        "scrarticle-beamer",
        "beamerposter",
        "article-beamer",
    ]

    if not hyperref and not beamer:
        return

    rurl = re.compile(r"^(.*)([%#].*)")
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Flex URL", i)
        if i == -1:
            return
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Malformed LyX document: Could not find end of URL inset.")
            i += 1
            continue
        while True:
            surl = find_re(document.body, rurl, i, j)
            if surl == -1:
                i = j
                break
            m = rurl.match(document.body[surl])
            if m:
                if m.group(1) == "" and document.body[surl - 1] == "\\backslash":
                    break
                document.body[surl : surl + 1] = [m.group(1), "\\backslash", m.group(2)]
            i = surl


def convert_url_escapes2(document):
    """Unescape backslashes in URLs with hyperref."""

    i = find_token(document.header, "\\use_hyperref true", 0)

    if i == -1 and document.textclass not in [
        "beamer",
        "scrarticle-beamer",
        "beamerposter",
        "article-beamer",
    ]:
        return

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Flex URL", i + 1)
        if i == -1:
            return
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Malformed LyX document: Could not find end of URL inset.")
            i += 1
            continue
        while True:
            bs = find_token(document.body, "\\backslash", i, j)
            if bs == -1:
                break
            if document.body[bs + 2] == "\\backslash":
                del document.body[bs + 2]
            i = bs + 1


def revert_url_escapes2(document):
    """Escape backslashes in URLs with hyperref."""

    i = find_token(document.header, "\\use_hyperref true", 0)

    if i == -1 and document.textclass not in [
        "beamer",
        "scrarticle-beamer",
        "beamerposter",
        "article-beamer",
    ]:
        return

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Flex URL", i + 1)
        if i == -1:
            return
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Malformed LyX document: Could not find end of URL inset.")
            i += 1
            continue
        while True:
            bs = find_token(document.body, "\\backslash", i, j)
            if bs == -1:
                break
            document.body[bs] = "\\backslash\\backslash"
            i = bs + 1


def revert_glue_parskip(document):
    """Revert parskip with glue length to user preamble."""

    i = find_token(document.header, "\\paragraph_separation skip", 0)
    if i == -1:
        return

    j = find_token(document.header, "\\defskip", 0)
    if j == -1:
        document.warning("Malformed LyX document! Missing \\defskip.")
        return

    val = get_value(document.header, "\\defskip", j)

    if val.find("+") == -1 and val.find("-", 1) == -1:
        # not a glue length
        return

    add_to_preamble(document, ["\\usepackage[skip={" + latex_length(val)[1] + "}]{parskip}"])

    document.header[i] = "\\paragraph_separation indent"
    document.header[j] = "\\paragraph_indentation default"


def convert_he_letter(document):
    """Convert hebrew letter to letter document class"""

    if document.textclass == "heb-letter":
        document.textclass = "letter"


chicago_local_layout = [
    "### Inserted by lyx2lyx (biblatex-chicago) ###",
    "Requires biblatex-chicago",
    "### End of insertion by lyx2lyx (biblatex-chicago) ###" "",
]

def convert_biblatex_chicago(document):
    """Convert biblatex-chicago documents"""
    
    chicago = document.del_local_layout(chicago_local_layout)
    if not chicago:
        chicago = document.del_from_header(["Requires biblatex-chicago"])
    if not chicago:
        return

    # 1. Get cite engine
    engine = "basic"
    i = find_token(document.header, "\\cite_engine", 0)
    if i == -1:
        document.warning("Malformed document! Missing \\cite_engine")
    else:
        engine = get_value(document.header, "\\cite_engine", i)

    # 2. If biblatex set to chicago
    biblatex = False
    if engine not in ["biblatex", "biblatex-natbib"]:
        return

    document.header[i] = "\\cite_engine biblatex-chicago"

    i = find_token(document.header, "\\biblio_options", 0)
    bibopts = ""
    if i == -1:
        val = get_value(document.header, "\\biblio_options", i)

    cetype = "authoryear"
    if bibopts.find("authordate") == -1:
        cetype = "notes"

    # 2. Set cite type
    i = find_token(document.header, "\\cite_engine_type", 0)
    if i == -1:
        document.warning("Malformed document! Missing \\cite_engine_type")
    else:
        document.header[i] = "\\cite_engine_type %s" % cetype


def revert_biblatex_chicago(document):
    """Revert biblatex-chicago to ERT where necessary"""

    # 1. Get cite engine
    engine = "basic"
    i = find_token(document.header, "\\cite_engine", 0)
    if i == -1:
        document.warning("Malformed document! Missing \\cite_engine")
    else:
        engine = get_value(document.header, "\\cite_engine", i)

    # 2. Do we use biblatex-chicago?
    if engine != "biblatex-chicago":
        return
    
    # 3. Reset cite engine
    document.header[i] = "\\cite_engine biblatex"

    # 4. Set cite type
    cetype = "authoryear"
    i = find_token(document.header, "\\cite_engine_type", 0)
    if i == -1:
        document.warning("Malformed document! Missing \\cite_engine_type")
    else:
        cetype = get_value(document.header, "\\cite_engine_type", i)
        document.header[i] = "\\cite_engine_type authoryear"

    # 5. Add authordate option if needed
    if cetype == "authoryear":
        i = find_token(document.header, "\\biblio_options", 0)
        if i != -1:
            bibopts = get_value(document.header, "\\biblio_options", i)
            if bibopts.find("authordate") != -1:
                document.header[i] = "\\biblio_options %s" % bibopts + ", authordate"
        else:
            i = find_token(document.header, "\\biblio_style", 0)
            if i == -1:
                document.warning("Malformed document! Missing \\biblio_style")
            else:
                document.header[i+1:i+1] = ["\\biblio_options authordate"]

    # 6. Set local layout
    document.append_local_layout(chicago_local_layout)

    # 7. Handle special citation commands
    # Specific citation insets used in biblatex that need to be reverted to ERT
    new_citations = {
        "atcite": "atcite",
        "atpcite": "atpcite",
        "gentextcite": "gentextcite",
        "Gentextcite": "Gentextcite",
    }
    if cetype == "notes":
        new_citations = {
            "citeyear": "citeyear*",
            "Citetitle": "Citetitle",
            "Citetitle*": "Citetitle*",
            "gentextcite": "gentextcite",
            "Gentextcite": "Gentextcite",
            "shortcite": "shortcite",
            "shortcite*": "shortcite*",
            "shortrefcite": "shortrefcite",
            "shorthandcite": "shorthandcite",
            "shorthandcite*": "shorthandcite*",
            "shorthandrefcite": "shorthandrefcite",
            "citejournal": "citejournal",
            "headlesscite": "headlesscite",
            "Headlesscite": "Headlesscite",
            "headlessfullcite": "headlessfullcite",
            "surnamecite": "surnamecite",
        }

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset citation", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of citation inset at line %d!!" % (i))
            i += 1
            continue
        k = find_token(document.body, "LatexCommand", i, j)
        if k == -1:
            document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
            i = j + 1
            continue
        cmd = get_value(document.body, "LatexCommand", k)
        if cmd in list(new_citations.keys()):
            pre = get_quoted_value(document.body, "before", i, j)
            post = get_quoted_value(document.body, "after", i, j)
            key = get_quoted_value(document.body, "key", i, j)
            if not key:
                document.warning("Citation inset at line %d does not have a key!" % (i))
                key = "???"
            # Replace known new commands with ERT
            res = "\\" + new_citations[cmd]
            if pre:
                res += "[" + pre + "]"
            if post:
                res += "[" + post + "]"
            elif pre:
                res += "[]"
            res += "{" + key + "}"
            document.body[i : j + 1] = put_cmd_in_ert([res])
        i = j + 1


def revert_nptextcite(document):
    """Revert \\nptextcite and MLA's autocite variants to ERT"""

    # 1. Get cite engine
    engine = "basic"
    i = find_token(document.header, "\\cite_engine", 0)
    if i == -1:
        document.warning("Malformed document! Missing \\cite_engine")
    else:
        engine = get_value(document.header, "\\cite_engine", i)

    # 2. Do we use biblatex?
    if engine != "biblatex" and engine != "biblatex-natbib":
        return
    
    # 3. and APA?
    cetype = "authoryear"
    i = find_token(document.header, "\\biblatex_citestyle", 0)
    if i == -1:
        return

    # 4. Convert \nptextcite to ERT
    new_citations = {
        "nptextcite": "nptextcite",
        "mlaautocite": "autocite",
        "Mlaautocite": "Autocite",
        "mlaautocite*": "autocite*",
        "Mlaautocite*": "Autocite*",
    }
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset citation", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of citation inset at line %d!!" % (i))
            i += 1
            continue
        k = find_token(document.body, "LatexCommand", i, j)
        if k == -1:
            document.warning("Can't find LatexCommand for citation inset at line %d!" % (i))
            i = j + 1
            continue
        cmd = get_value(document.body, "LatexCommand", k)
        if cmd in list(new_citations.keys()):
            pre = get_quoted_value(document.body, "before", i, j)
            post = get_quoted_value(document.body, "after", i, j)
            key = get_quoted_value(document.body, "key", i, j)
            if not key:
                document.warning("Citation inset at line %d does not have a key!" % (i))
                key = "???"
            # Replace known new commands with ERT
            res = "\\" + new_citations[cmd]
            if pre:
                res += "[" + pre + "]"
            if post:
                res += "[" + post + "]"
            elif pre:
                res += "[]"
            res += "{" + key + "}"
            document.body[i : j + 1] = put_cmd_in_ert([res])
        i = j + 1


def revert_nomencl_textwidth(document):
    """Revert nomencl textwidth parameter to ERT."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset nomencl_print", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of command inset at line %d" % i
            )
            i += 1
            continue

        sw = get_quoted_value(document.body, "set_width", i, j)
        if sw != "textwidth":
            i += 1
            continue

        # change set_width to "none"
        k = find_token(document.body, "set_width", i, j)
        if k != -1:
            document.body[k] = "set_width \"none\""
        tw = get_quoted_value(document.body, "width", i, j)
        # delete width
        del_token(document.body, "width", i, j)
        # Insert ERT
        res = "\\settowidth{\\nomlabelwidth}{" + tw + "}"
        document.body[i : i] = put_cmd_in_ert([res])
        i = j


def convert_nomencl(document):
    """Convert nomencl inset to collapsible."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset nomenclature", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of command inset at line %d" % i
            )
            i += 1
            continue

        literal = get_quoted_value(document.body, "literal", i, j)
        prefix = get_quoted_value(document.body, "prefix", i, j)
        symbol = get_quoted_value(document.body, "symbol", i, j)
        description = get_quoted_value(document.body, "description", i, j)

        newins = ["\\begin_inset Nomenclature", "status open", "", "\\begin_layout Plain Layout"]
        if prefix:
            newins += ["\\begin_inset Argument 1",
                       "status open",
                       "",
                       "\\begin_layout Plain Layout",
                       prefix,
                       "\\end_layout",
                       "",
                       "\\end_inset",
                       ""]
        if literal == "true":
            newins += put_cmd_in_ert(symbol.replace("\\\\", "\\").replace("\\\"", "\""))
        else:
            newins += [symbol.replace("\\\"", "\"")]
        if description:
            newins += ["\\begin_inset Argument post:1",
                       "status open",
                       "",
                       "\\begin_layout Plain Layout"]
            if literal == "true":
                newins += put_cmd_in_ert(description.replace("\\\\", "\\").replace("\\\"", "\""))
            else:
                newins += [description.replace("\\\"", "\"")]
            newins += ["\\end_layout",
                       "",
                       "\\end_inset",
                       ""]
        newins += ["\\end_layout",
                   "",
                   "\\end_inset"]
        document.body[i : j + 1] = newins


def revert_nomencl(document):
    """Revert nomencl inset to InsetCommand."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Nomenclature", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of command inset at line %d" % i
            )
            i += 1
            continue

        arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
        prefix = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find optarg plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            prefix = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
        description = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find arg 1 plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            description = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
        endPlain = find_end_of_layout(document.body, beginPlain)
        symbol = document.body[beginPlain + 1 : endPlain]
        literal = "false"
        if "\\begin_inset ERT" in symbol or "\\begin_inset ERT" in description:
            literal = "true"

        newins = ["\\begin_inset CommandInset nomenclature", "LatexCommand nomenclature"]
        if prefix:
            newins += ["prefix \"" + lyx2latex(document, prefix) + "\""]
        if symbol:
            if literal == "true":
                newins += ["symbol \"" + lyx2latex(document, symbol).replace("\\", "\\\\").replace("\"", "\\\"") + "\""]
            else:
                newins += ["symbol \"" + lyx2latex(document, symbol).replace("\"", "\\\"") + "\""]
        if description:
            if literal == "true":
                newins += ["description \"" + lyx2latex(document, description).replace("\\", "\\\\").replace("\"", "\\\"") + "\""]
            else:
                newins += ["description \"" + lyx2latex(document, description).replace("\"", "\\\"") + "\""]
        newins += ["literal \"" + literal + "\""]

        j = find_end_of_inset(document.body, i)
        document.body[i : j] = newins

        i += 1


def convert_index_sc(document):
    """Convert index special characters to ERT."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Index", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of index inset at line %d" % i
            )
            i += 1
            continue

        escchars = ["!", "@", "|"]
        for ec in escchars:
            k = i;
            while True:
                j = find_end_of_inset(document.body, i)
                k = find_substring(document.body, ec, k, j)
                if k == -1:
                    break
                inInset = get_containing_inset(document.body, k)
                if inInset and inInset[0] == "ERT":
                    k += 1
                    continue
                    
                line = document.body[k]
                chunks = line.split(ec)
                repl = []
                chunks_len = len(chunks)-1
                for ch in chunks[:-1]:
                    repl += [ch]
                    repl += put_cmd_in_ert(ec)
                repl += chunks[-1:]
                document.body[k:k+1] = repl
        i += 1


def revert_index_sc(document):
    """Escape index special characters."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Index", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of index inset at line %d" % i
            )
            i += 1
            continue

        escchars = ["!", "@", "|"]
        for ec in escchars:
            k = i;
            while True:
                j = find_end_of_inset(document.body, i)
                k = find_substring(document.body, ec, k, j)
                if k == -1:
                    break
                inInset = get_containing_inset(document.body, k)
                if inInset and inInset[0] == "ERT":
                    k += 1
                    continue
                    
                line = document.body[k]
                chunks = line.split(ec)
                repl = []
                chunks_len = len(chunks)-1
                for ch in chunks[:-1]:
                    repl += [ch]
                    repl += put_cmd_in_ert("\"")
                    repl += [ec]
                repl += chunks[-1:]
                document.body[k:k+1] = repl
                k += len(repl)
        i += 1


def revert_nomentbl(document):
    """Revert nomentbl inset to ERT."""

    i = find_token(document.header, "\\nomencl_options", 0)
    if i == -1:
        # nothing to do
        return

    opts = get_value(document.header, "\\nomencl_options", i)
    # remove header
    del document.header[i]

    # store options
    document.append_local_layout([r"### Inserted by lyx2lyx (nomencl) ###",
                                  r"PackageOptions nomencl %s" % opts])

    if opts.find("nomentbl") == -1:
        return
    
    # revert insets to ERT
    have_nomencl = False
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Nomenclature", i)
        if i == -1:
            break

        have_nomencl = True
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning(
                "Malformed LyX document: Can't find end of command inset at line %d" % i
            )
            i += 1
            continue

        arg = find_token(document.body, "\\begin_inset Argument 1", i, j)
        prefix = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find optarg plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            prefix = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        j = find_end_of_inset(document.body, i)
        arg = find_token(document.body, "\\begin_inset Argument post:1", i, j)
        description = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find arg post:1 plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            description = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        j = find_end_of_inset(document.body, i)
        arg = find_token(document.body, "\\begin_inset Argument post:2", i, j)
        unit = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find arg post:2 plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            unit = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        j = find_end_of_inset(document.body, i)
        arg = find_token(document.body, "\\begin_inset Argument post:3", i, j)
        note = []
        if arg != -1:
            endarg = find_end_of_inset(document.body, arg)
            argbeginPlain = find_token(
                document.body, "\\begin_layout Plain Layout", arg, endarg
            )
            if argbeginPlain == -1:
                document.warning("Malformed LyX document: Can't find arg post:3 plain Layout")
                continue
            argendPlain = find_end_of_inset(document.body, argbeginPlain)
            note = document.body[argbeginPlain + 1 : argendPlain - 2]

            # remove Arg insets and paragraph, if it only contains this inset
            if (
                document.body[arg - 1] == "\\begin_layout Plain Layout"
                and find_end_of_layout(document.body, arg - 1) == endarg + 3
            ):
                del document.body[arg - 1 : endarg + 4]
            else:
                del document.body[arg : endarg + 1]

        beginPlain = find_token(document.body, "\\begin_layout Plain Layout", i)
        endPlain = find_end_of_layout(document.body, beginPlain)
        symbol = document.body[beginPlain + 1 : endPlain]

        # Replace command with ERT
        res = put_cmd_in_ert(["\\nomenclature"])
        if prefix:
            res += put_cmd_in_ert(["["]) + prefix + put_cmd_in_ert(["]"])
        res += put_cmd_in_ert(["{"]) + symbol + put_cmd_in_ert(["}{"]) \
            + description + put_cmd_in_ert(["}{"]) + unit + put_cmd_in_ert(["}{"]) \
            + note + put_cmd_in_ert(["}"])

        j = find_end_of_inset(document.body, i)
        document.body[i : j + 1] = res

        i += 1


def revert_langopts(document):
    """Remove language options header."""

    i = 0
    while True:
        i = find_token(document.header, "\\language_options_", 0)
        if i == -1:
            # nothing to do
            return

        # remove header
        del document.header[i]


def revert_new_polyglossia_languages(document):
    """Emulate support for Simplified Chinese, Traditional Chinese, Japanese, Sorani Kurdish,
       Classic, Ecclesiastic and Medieval Latin, N'ko, Odia, Punjabi, and Uyghur with polyglossia."""

    # Does the document use polyglossia?
    use_polyglossia = False
    if get_bool_value(document.header, "\\use_non_tex_fonts"):
        i = find_token(document.header, "\\language_package")
        if i == -1:
            document.warning("Malformed document! Missing \\language_package")
        else:
            pack = get_value(document.header, "\\language_package", i)
            if pack in ("default", "auto"):
                use_polyglossia = True

    if not use_polyglossia:
        return

    #    lyxname:    (babelname, polyglossianame, polyglossiaopts)
    new_languages = {
        "chinese-simplified": ("", "chinese", "variant=simplified"),
        "chinese-traditional": ("", "chinese", "variant=traditional"),
        "japanese": ("", "japanese", ""),
        "sorani": ("", "kurdish", "variant=sorani"),
        "latin-classic": ("", "latin", "variant=classic"),
        "latin-ecclesiastic": ("", "latin", "variant=ecclesiastic"),
        "latin-medieval": ("", "latin", "variant=medieval"),
        "nko": ("", "nko", ""),
        "odia": ("", "odia", ""),
        "punjabi": ("", "punjabi", ""),
        "uyghur": ("", "uyghur", ""),
    }
    if document.language in new_languages:
        used_languages = {document.language}
    else:
        used_languages = set()
    i = 0
    while True:
        i = find_token(document.body, "\\lang", i + 1)
        if i == -1:
            break
        val = get_value(document.body, "\\lang", i)
        if val in new_languages:
            used_languages.add(val)

    for lang in used_languages:
        revert_language(document, lang, *new_languages[lang])
 

def revert_new_babel_languages(document):
    """Emulate support for Amharic, Armenian, Asturian, Bengali, Church Slavonic,
    Coptic, Divehi, Kannada, Kazakh, Khmer, Kurdish (Sorani), Lao, Latin (Classic),
    Latin (Ecclesiastic), Latin (Medieval), Malayalam, Marathi, N'ko, Occitan, Odia,
    Punjabi, Russian (Petrine orthography), Sanskrit, Syriac, Tamil, Telugu, Tibetan,
    Urdu, and Uyghur with babel."""

    # Does the document use polyglossia?
    use_polyglossia = False
    if get_bool_value(document.header, "\\use_non_tex_fonts"):
        i = find_token(document.header, "\\language_package")
        if i == -1:
            document.warning("Malformed document! Missing \\language_package")
        else:
            pack = get_value(document.header, "\\language_package", i)
            if pack in ("default", "auto"):
                use_polyglossia = True

    if use_polyglossia:
        return

    #    lyxname:    (babelname, polyglossianame, polyglossiaopts, babelprovide)
    new_languages = {
        "amharic": ("amharic", "", "", True),
        "armenian": ("armenian", "", "", True),
        "asturian": ("asturian", "", "", True),
        "bengali": ("bengali", "", "", True),
        "churchslavonic": ("churchslavic", "", "", True),
        "coptic": ("coptic", "", "", True),
        "divehi": ("divehi", "", "", True),
        "hindi": ("hindi", "", "", True),
        "kannada": ("kannada", "", "", True),
        "kazakh": ("kazakh", "", "", True),
        "khmer": ("khmer", "", "", True),
        "lao": ("lao", "", "", True),
        "latin-classic": ("classiclatin", "", "", False),
        "latin-ecclesiastic": ("ecclesiasticlatin", "", "", False),
        "latin-medieval": ("medievallatin", "", "", False),
        "malayalam": ("malayalam", "", "", True),
        "marathi": ("marathi", "", "", True),
        "nko": ("nko", "", "", True),
        "occitan": ("occitan", "", "", False),
        "odia": ("odia", "", "", True),
        "punjabi": ("punjabi", "", "", True),
        "sanskrit": ("sanskrit", "", "", True),
        "sorani": ("sorani", "", "", True),
        "syriac": ("syriac", "", "", True),
        "tamil": ("tamil", "", "", True),
        "telugu": ("telugu", "", "", True),
        "tibetan": ("tibetan", "", "", True),
        "urdu": ("urdu", "", "", True),
        "uyghur": ("uyghur", "", "", True),
    }
    if document.language in new_languages:
        used_languages = {document.language}
    else:
        used_languages = set()
    i = 0
    while True:
        i = find_token(document.body, "\\lang", i + 1)
        if i == -1:
            break
        val = get_value(document.body, "\\lang", i)
        if val in new_languages:
            used_languages.add(val)

    for lang in used_languages:
        revert_language(document, lang, *new_languages[lang])

    # revert oldrussian to russian
    have_oldrussian = False
    if document.language == "oldrussian":
        document.language = "russian"
        have_oldrussian = True

    i = 0
    while True:
        i = find_token(document.body, "\\lang oldrussian", i + 1)
        if i == -1:
            break
        have_oldrussian = True
        document.body[i] = "\\lang russian"

    if have_oldrussian:
        add_to_preamble(document, ["\\AddToHook{package/babel/after}{\\languageattribute{russian}{ancient}}"])

    # Some babel languages require special treatment with unicode engines
    if get_bool_value(document.header, "\\use_non_tex_fonts"):
        if document.language == "hebrew" or find_token(document.body, "\\lang oldrussian", 0) != -1:
            add_to_preamble(document, ["\\PassOptionsToPackage{provide*=*}{babel}"])


def convert_mathml_version(document):
    """Add MathML version header for DocBook to use MathML 3 preferably.

    For cleanliness, add this header close to other DocBook headers if present.

    Leave XHTML alone, as the default value is still probably what the user wants (MathML Core)."""

    i = find_token(document.header, "\\docbook", 0)
    if i == -1:
        document.header.insert(-1, "\\docbook_mathml_version 0")
    else:
        document.header.insert(i + 1, "\\docbook_mathml_version 0")


def revert_mathml_version(document):
    """Remove MathML version header.

    For XHTML, only remove the value 4 for \\html_math_output (MathML 3) and replace it with 0
    (MathML Core with format 631+, MathML for 630-).

    For DocBook, totally remove the header (the default with 630- is MathML)."""

    while True:
        i = find_token(document.header, "\\html_math_output", 0)
        if i == -1:
            # nothing to do
            break

        # reset XHTML header if using the new value, leave alone otherwise.
        if "4" in document.header[i]:
            document.header[i] = "\\html_math_output 0"
        break

    while True:
        i = find_token(document.header, "\\docbook_mathml_version", 0)
        if i == -1:
            # nothing to do
            return

        # remove header
        del document.header[i]

xcolor_names = [
    "DVIPS:Apricot",
    "DVIPS:Aquamarine",
    "DVIPS:Bittersweet",
    "DVIPS:Black",
    "DVIPS:Blue",
    "DVIPS:BlueGreen",
    "DVIPS:BlueViolet",
    "DVIPS:BrickRed",
    "DVIPS:Brown",
    "DVIPS:BurntOrange",
    "DVIPS:CadetBlue",
    "DVIPS:CarnationPink",
    "DVIPS:Cerulean",
    "DVIPS:CornflowerBlue",
    "DVIPS:Cyan",
    "DVIPS:Dandelion",
    "DVIPS:DarkOrchid",
    "DVIPS:Emerald",
    "DVIPS:ForestGreen",
    "DVIPS:Fuchsia",
    "DVIPS:Goldenrod",
    "DVIPS:Gray",
    "DVIPS:Green",
    "DVIPS:GreenYellow",
    "DVIPS:JungleGreen",
    "DVIPS:Lavender",
    "DVIPS:LimeGreen",
    "DVIPS:Magenta",
    "DVIPS:Mahogany",
    "DVIPS:Maroon",
    "DVIPS:Melon",
    "DVIPS:MidnightBlue",
    "DVIPS:Mulberry",
    "DVIPS:NavyBlue",
    "DVIPS:OliveGreen",
    "DVIPS:Orange",
    "DVIPS:OrangeRed",
    "DVIPS:Orchid",
    "DVIPS:Peach",
    "DVIPS:Periwinkle",
    "DVIPS:PineGreen",
    "DVIPS:Plum",
    "DVIPS:ProcessBlue",
    "DVIPS:Purple",
    "DVIPS:RawSienna",
    "DVIPS:Red",
    "DVIPS:RedOrange",
    "DVIPS:RedViolet",
    "DVIPS:Rhodamine",
    "DVIPS:RoyalBlue",
    "DVIPS:RoyalPurple",
    "DVIPS:RubineRed",
    "DVIPS:Salmon",
    "DVIPS:SeaGreen",
    "DVIPS:Sepia",
    "DVIPS:SkyBlue",
    "DVIPS:SpringGreen",
    "DVIPS:Tan",
    "DVIPS:TealBlue",
    "DVIPS:Thistle",
    "DVIPS:Turquoise",
    "DVIPS:Violet",
    "DVIPS:VioletRed",
    "DVIPS:White",
    "DVIPS:WildStrawberry",
    "DVIPS:Yellow",
    "DVIPS:YellowGreen",
    "DVIPS:YellowOrange",
    "SVG:AliceBlue",
    "SVG:AntiqueWhite",
    "SVG:Aqua",
    "SVG:Aquamarine",
    "SVG:Azure",
    "SVG:Beige",
    "SVG:Bisque",
    "SVG:Black",
    "SVG:BlanchedAlmond",
    "SVG:Blue",
    "SVG:BlueViolet",
    "SVG:Brown",
    "SVG:BurlyWood",
    "SVG:CadetBlue",
    "SVG:Chartreuse",
    "SVG:Chocolate",
    "SVG:Coral",
    "SVG:CornflowerBlue",
    "SVG:Cornsilk",
    "SVG:Crimson",
    "SVG:Cyan",
    "SVG:DarkBlue",
    "SVG:DarkCyan",
    "SVG:DarkGoldenrod",
    "SVG:DarkGray",
    "SVG:DarkGreen",
    "SVG:DarkKhaki",
    "SVG:DarkMagenta",
    "SVG:DarkOliveGreen",
    "SVG:DarkOrange",
    "SVG:DarkOrchid",
    "SVG:DarkRed",
    "SVG:DarkSalmon",
    "SVG:DarkSeaGreen",
    "SVG:DarkSlateBlue",
    "SVG:DarkSlateGray",
    "SVG:DarkTurquoise",
    "SVG:DarkViolet",
    "SVG:DeepPink",
    "SVG:DeepSkyBlue",
    "SVG:DimGray",
    "SVG:DodgerBlue",
    "SVG:FireBrick",
    "SVG:FloralWhite",
    "SVG:ForestGreen",
    "SVG:Fuchsia",
    "SVG:Gainsboro",
    "SVG:GhostWhite",
    "SVG:Gold",
    "SVG:Goldenrod",
    "SVG:Gray",
    "SVG:Green",
    "SVG:GreenYellow",
    "SVG:Honeydew",
    "SVG:HotPink",
    "SVG:IndianRed",
    "SVG:Indigo",
    "SVG:Ivory",
    "SVG:Khaki",
    "SVG:Lavender",
    "SVG:LavenderBlush",
    "SVG:LawnGreen",
    "SVG:LemonChiffon",
    "SVG:LightBlue",
    "SVG:LightCoral",
    "SVG:LightCyan",
    "SVG:LightGoldenrod",
    "SVG:LightGoldenrodYellow",
    "SVG:LightGray",
    "SVG:LightGreen",
    "SVG:LightPink",
    "SVG:LightSalmon",
    "SVG:LightSeaGreen",
    "SVG:LightSkyBlue",
    "SVG:LightSlateBlue",
    "SVG:LightSlateGray",
    "SVG:LightSteelBlue",
    "SVG:LightYellow",
    "SVG:Lime",
    "SVG:LimeGreen",
    "SVG:Linen",
    "SVG:Magenta",
    "SVG:Maroon",
    "SVG:MediumAquamarine",
    "SVG:MediumBlue",
    "SVG:MediumOrchid",
    "SVG:MediumPurple",
    "SVG:MediumSeaGreen",
    "SVG:MediumSlateBlue",
    "SVG:MediumSpringGreen",
    "SVG:MediumTurquoise",
    "SVG:MediumVioletRed",
    "SVG:MidnightBlue",
    "SVG:MintCream",
    "SVG:MistyRose",
    "SVG:Moccasin",
    "SVG:NavajoWhite",
    "SVG:NavyBlue",
    "SVG:OldLace",
    "SVG:Olive",
    "SVG:OliveDrab",
    "SVG:Orange",
    "SVG:OrangeRed",
    "SVG:Orchid",
    "SVG:PaleGoldenrod",
    "SVG:PaleGreen",
    "SVG:PaleTurquoise",
    "SVG:PaleVioletRed",
    "SVG:PapayaWhip",
    "SVG:PeachPuff",
    "SVG:Peru",
    "SVG:Pink",
    "SVG:Plum",
    "SVG:PowderBlue",
    "SVG:Purple",
    "SVG:Red",
    "SVG:RosyBrown",
    "SVG:RoyalBlue",
    "SVG:SaddleBrown",
    "SVG:Salmon",
    "SVG:SandyBrown",
    "SVG:SeaGreen",
    "SVG:Seashell",
    "SVG:Sienna",
    "SVG:Silver",
    "SVG:SkyBlue",
    "SVG:SlateBlue",
    "SVG:SlateGray",
    "SVG:Snow",
    "SVG:SpringGreen",
    "SVG:SteelBlue",
    "SVG:Tan",
    "SVG:Teal",
    "SVG:Thistle",
    "SVG:Tomato",
    "SVG:Turquoise",
    "SVG:Violet",
    "SVG:VioletRed",
    "SVG:Wheat",
    "SVG:White",
    "SVG:WhiteSmoke",
    "SVG:Yellow",
    "SVG:YellowGreen",
    "X11:AntiqueWhite1",
    "X11:AntiqueWhite2",
    "X11:AntiqueWhite3",
    "X11:AntiqueWhite4",
    "X11:Aquamarine1",
    "X11:Aquamarine2",
    "X11:Aquamarine3",
    "X11:Aquamarine4",
    "X11:Azure1",
    "X11:Azure2",
    "X11:Azure3",
    "X11:Azure4",
    "X11:Bisque1",
    "X11:Bisque2",
    "X11:Bisque3",
    "X11:Bisque4",
    "X11:Blue1",
    "X11:Blue2",
    "X11:Blue3",
    "X11:Blue4",
    "X11:Brown1",
    "X11:Brown2",
    "X11:Brown3",
    "X11:Brown4",
    "X11:Burlywood1",
    "X11:Burlywood2",
    "X11:Burlywood3",
    "X11:Burlywood4",
    "X11:CadetBlue1",
    "X11:CadetBlue2",
    "X11:CadetBlue3",
    "X11:CadetBlue4",
    "X11:Chartreuse1",
    "X11:Chartreuse2",
    "X11:Chartreuse3",
    "X11:Chartreuse4",
    "X11:Chocolate1",
    "X11:Chocolate2",
    "X11:Chocolate3",
    "X11:Chocolate4",
    "X11:Coral1",
    "X11:Coral2",
    "X11:Coral3",
    "X11:Coral4",
    "X11:Cornsilk1",
    "X11:Cornsilk2",
    "X11:Cornsilk3",
    "X11:Cornsilk4",
    "X11:Cyan1",
    "X11:Cyan2",
    "X11:Cyan3",
    "X11:Cyan4",
    "X11:DarkGoldenrod1",
    "X11:DarkGoldenrod2",
    "X11:DarkGoldenrod3",
    "X11:DarkGoldenrod4",
    "X11:DarkOliveGreen1",
    "X11:DarkOliveGreen2",
    "X11:DarkOliveGreen3",
    "X11:DarkOliveGreen4",
    "X11:DarkOrange1",
    "X11:DarkOrange2",
    "X11:DarkOrange3",
    "X11:DarkOrange4",
    "X11:DarkOrchid1",
    "X11:DarkOrchid2",
    "X11:DarkOrchid3",
    "X11:DarkOrchid4",
    "X11:DarkSeaGreen1",
    "X11:DarkSeaGreen2",
    "X11:DarkSeaGreen3",
    "X11:DarkSeaGreen4",
    "X11:DarkSlateGray1",
    "X11:DarkSlateGray2",
    "X11:DarkSlateGray3",
    "X11:DarkSlateGray4",
    "X11:DeepPink1",
    "X11:DeepPink2",
    "X11:DeepPink3",
    "X11:DeepPink4",
    "X11:DeepSkyBlue1",
    "X11:DeepSkyBlue2",
    "X11:DeepSkyBlue3",
    "X11:DeepSkyBlue4",
    "X11:DodgerBlue1",
    "X11:DodgerBlue2",
    "X11:DodgerBlue3",
    "X11:DodgerBlue4",
    "X11:Firebrick1",
    "X11:Firebrick2",
    "X11:Firebrick3",
    "X11:Firebrick4",
    "X11:Gold1",
    "X11:Gold2",
    "X11:Gold3",
    "X11:Gold4",
    "X11:Goldenrod1",
    "X11:Goldenrod2",
    "X11:Goldenrod3",
    "X11:Goldenrod4",
    "X11:Green1",
    "X11:Green2",
    "X11:Green3",
    "X11:Green4",
    "X11:Honeydew1",
    "X11:Honeydew2",
    "X11:Honeydew3",
    "X11:Honeydew4",
    "X11:HotPink1",
    "X11:HotPink2",
    "X11:HotPink3",
    "X11:HotPink4",
    "X11:IndianRed1",
    "X11:IndianRed2",
    "X11:IndianRed3",
    "X11:IndianRed4",
    "X11:Ivory1",
    "X11:Ivory2",
    "X11:Ivory3",
    "X11:Ivory4",
    "X11:Khaki1",
    "X11:Khaki2",
    "X11:Khaki3",
    "X11:Khaki4",
    "X11:LavenderBlush1",
    "X11:LavenderBlush2",
    "X11:LavenderBlush3",
    "X11:LavenderBlush4",
    "X11:LemonChiffon1",
    "X11:LemonChiffon2",
    "X11:LemonChiffon3",
    "X11:LemonChiffon4",
    "X11:LightBlue1",
    "X11:LightBlue2",
    "X11:LightBlue3",
    "X11:LightBlue4",
    "X11:LightCyan1",
    "X11:LightCyan2",
    "X11:LightCyan3",
    "X11:LightCyan4",
    "X11:LightGoldenrod1",
    "X11:LightGoldenrod2",
    "X11:LightGoldenrod3",
    "X11:LightGoldenrod4",
    "X11:LightPink1",
    "X11:LightPink2",
    "X11:LightPink3",
    "X11:LightPink4",
    "X11:LightSalmon1",
    "X11:LightSalmon2",
    "X11:LightSalmon3",
    "X11:LightSalmon4",
    "X11:LightSkyBlue1",
    "X11:LightSkyBlue2",
    "X11:LightSkyBlue3",
    "X11:LightSkyBlue4",
    "X11:LightSteelBlue1",
    "X11:LightSteelBlue2",
    "X11:LightSteelBlue3",
    "X11:LightSteelBlue4",
    "X11:LightYellow1",
    "X11:LightYellow2",
    "X11:LightYellow3",
    "X11:LightYellow4",
    "X11:Magenta1",
    "X11:Magenta2",
    "X11:Magenta3",
    "X11:Magenta4",
    "X11:Maroon1",
    "X11:Maroon2",
    "X11:Maroon3",
    "X11:Maroon4",
    "X11:MediumOrchid1",
    "X11:MediumOrchid2",
    "X11:MediumOrchid3",
    "X11:MediumOrchid4",
    "X11:MediumPurple1",
    "X11:MediumPurple2",
    "X11:MediumPurple3",
    "X11:MediumPurple4",
    "X11:MistyRose1",
    "X11:MistyRose2",
    "X11:MistyRose3",
    "X11:MistyRose4",
    "X11:NavajoWhite1",
    "X11:NavajoWhite2",
    "X11:NavajoWhite3",
    "X11:NavajoWhite4",
    "X11:OliveDrab1",
    "X11:OliveDrab2",
    "X11:OliveDrab3",
    "X11:OliveDrab4",
    "X11:Orange1",
    "X11:Orange2",
    "X11:Orange3",
    "X11:Orange4",
    "X11:OrangeRed1",
    "X11:OrangeRed2",
    "X11:OrangeRed3",
    "X11:OrangeRed4",
    "X11:Orchid1",
    "X11:Orchid2",
    "X11:Orchid3",
    "X11:Orchid4",
    "X11:PaleGreen1",
    "X11:PaleGreen2",
    "X11:PaleGreen3",
    "X11:PaleGreen4",
    "X11:PaleTurquoise1",
    "X11:PaleTurquoise2",
    "X11:PaleTurquoise3",
    "X11:PaleTurquoise4",
    "X11:PaleVioletRed1",
    "X11:PaleVioletRed2",
    "X11:PaleVioletRed3",
    "X11:PaleVioletRed4",
    "X11:PeachPuff1",
    "X11:PeachPuff2",
    "X11:PeachPuff3",
    "X11:PeachPuff4",
    "X11:Pink1",
    "X11:Pink2",
    "X11:Pink3",
    "X11:Pink4",
    "X11:Plum1",
    "X11:Plum2",
    "X11:Plum3",
    "X11:Plum4",
    "X11:Purple1",
    "X11:Purple2",
    "X11:Purple3",
    "X11:Purple4",
    "X11:Red1",
    "X11:Red2",
    "X11:Red3",
    "X11:Red4",
    "X11:RosyBrown1",
    "X11:RosyBrown2",
    "X11:RosyBrown3",
    "X11:RosyBrown4",
    "X11:RoyalBlue1",
    "X11:RoyalBlue2",
    "X11:RoyalBlue3",
    "X11:RoyalBlue4",
    "X11:Salmon1",
    "X11:Salmon2",
    "X11:Salmon3",
    "X11:Salmon4",
    "X11:SeaGreen1",
    "X11:SeaGreen2",
    "X11:SeaGreen3",
    "X11:SeaGreen4",
    "X11:Seashell1",
    "X11:Seashell2",
    "X11:Seashell3",
    "X11:Seashell4",
    "X11:Sienna1",
    "X11:Sienna2",
    "X11:Sienna3",
    "X11:Sienna4",
    "X11:SkyBlue1",
    "X11:SkyBlue2",
    "X11:SkyBlue3",
    "X11:SkyBlue4",
    "X11:SlateBlue1",
    "X11:SlateBlue2",
    "X11:SlateBlue3",
    "X11:SlateBlue4",
    "X11:SlateGray1",
    "X11:SlateGray2",
    "X11:SlateGray3",
    "X11:SlateGray4",
    "X11:Snow1",
    "X11:Snow2",
    "X11:Snow3",
    "X11:Snow4",
    "X11:SpringGreen1",
    "X11:SpringGreen2",
    "X11:SpringGreen3",
    "X11:SpringGreen4",
    "X11:SteelBlue1",
    "X11:SteelBlue2",
    "X11:SteelBlue3",
    "X11:SteelBlue4",
    "X11:Tan1",
    "X11:Tan2",
    "X11:Tan3",
    "X11:Tan4",
    "X11:Thistle1",
    "X11:Thistle2",
    "X11:Thistle3",
    "X11:Thistle4",
    "X11:Tomato1",
    "X11:Tomato2",
    "X11:Tomato3",
    "X11:Tomato4",
    "X11:Turquoise1",
    "X11:Turquoise2",
    "X11:Turquoise3",
    "X11:Turquoise4",
    "X11:VioletRed1",
    "X11:VioletRed2",
    "X11:VioletRed3",
    "X11:VioletRed4",
    "X11:Wheat1",
    "X11:Wheat2",
    "X11:Wheat3",
    "X11:Wheat4",
    "X11:Yellow1",
    "X11:Yellow2",
    "X11:Yellow3",
    "X11:Yellow4",
    "X11:Gray0",
    "X11:Green0",
    "X11:Maroon0",
    "X11:Purple0"
]


def revert_textcolor(document):
    "revert new \\textcolor colors to TeX code"

    i = 0
    j = 0
    xcolor = False
    x11 = False
    svg = False
    dvips = False
    while True:
        i = find_token(document.body, "\\color ", i)
        if i == -1:
            break
        else:
            for color in list(xcolor_names):
                if document.body[i] == "\\color " + color.lower():
                    parts = color.split(":")
                    # register that xcolor must be loaded in the preamble
                    xcolor = True
                    if parts[0] == "X11":
                        x11 = True
                    elif parts[0] == "SVG":
                        svg = True
                    elif parts[0] == "DVIPS":
                        dvips = True
                    # find the next \\color and/or the next \\end_layout
                    j = find_token(document.body, "\\color", i + 1)
                    k = find_token(document.body, "\\end_layout", i + 1)
                    if j == -1 and k != -1:
                        j = k + 1
                    # output TeX code
                    # first output the closing brace
                    if k < j:
                        document.body[k:k] = put_cmd_in_ert("}")
                    else:
                        document.body[j:j] = put_cmd_in_ert("}")
                    # now output the \textcolor command
                    document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + parts[1] + "}{")
        i = i + 1

    # For math, we simply check whether a color of a model
    # that needs to be loaded is used.
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Formula", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of inset at line %d of body!" % i)
            i += 1
            continue
        while i < j:
            m = re.search("\\\\color{([^}]+)}", document.body[i])
            if m:
                for color in list(xcolor_names):
                    if color == "SVG:" + m.group(1):
                        svg = True
                    elif color == "X11:" + m.group(1):
                        x11 = True
                    elif color == "DVIPS:" + m.group(1):
                        dvips = True
            i += 1

    if xcolor == True:
        opts = []
        if x11 == True:
            opts.append("x11names")
        if svg == True:
            opts.append("svgnames")
        if dvips == True:
            opts.append("dvipsnames")
        options = "\\SetKeys[xcolor]{" + ",".join(opts) + "}"
        add_to_preamble(
            document,
            ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}",
             options]
        )

def revert_custom_colors(document):
    "reverts custom colors to TeX code"

    cols = []
    defs = []
    while True:
        i = find_token(document.header, "\\customcolor", 0)
        if i == -1:
            break
        value = get_value(document.header, "\\customcolor", i).split()
        del document.header[i]
        colorname = value[0]
        cols.append(colorname)
        colorcode = "#" + value[1]
        red = hex2ratio(colorcode[1:3])
        green = hex2ratio(colorcode[3:5])
        blue = hex2ratio(colorcode[5:7])
        defs.append("\\definecolor{" + colorname + "}{rgb}{" + red + "," + green + "," + blue + "}")
        i += 1

    if len(defs) == 0:
        return

    df = "\n".join(defs)
    add_to_preamble(
        document,
        [
            "% To set the background color",
            "\\@ifundefined{definecolor}{\\usepackage{color}}{}",
            df
        ],
    )
    i = 0
    j = 0
    while True:
        i = find_token(document.body, "\\color ", i)
        if i == -1:
            break
        else:
            for color in list(cols):
                if document.body[i] == "\\color " + color:
                    # find the next \\color and/or the next \\end_layout
                    j = find_token(document.body, "\\color", i + 1)
                    k = find_token(document.body, "\\end_layout", i + 1)
                    if j == -1 and k != -1:
                        j = k + 1
                    # output TeX code
                    # first output the closing brace
                    if k < j:
                        document.body[k:k] = put_cmd_in_ert("}")
                    else:
                        document.body[j:j] = put_cmd_in_ert("}")
                    # now output the \textcolor command
                    document.body[i : i + 1] = put_cmd_in_ert("\\textcolor{" + color + "}{")
        i = i + 1


def convert_doc_colors(document):
    "Converts background, main font, shaded box and greyedout colors to new format"

    i = find_token(document.header, "\\fontcolor", 0)
    if i != -1:
        value = get_value(document.header, "\\fontcolor", i)
        document.header[i] = "\\customcolor lyxfontcolor " + value[1:]
        document.header.insert(i + 1, "\\fontcolor lyxfontcolor")

    i = find_token(document.header, "\\backgroundcolor", 0)
    if i != -1:
        value = get_value(document.header, "\\backgroundcolor", i)
        document.header[i] = "\\customcolor lyxbackgroundcolor " + value[1:]
        document.header.insert(i + 1, "\\backgroundcolor lyxbackgroundcolor")

    i = find_token(document.header, "\\notefontcolor", 0)
    if i != -1:
        value = get_value(document.header, "\\notefontcolor", i)
        document.header[i] = "\\customcolor lyxnotefontcolor " + value[1:]
        document.header.insert(i + 1, "\\notefontcolor lyxnotefontcolor")

    i = find_token(document.header, "\\boxbgcolor", 0)
    if i != -1:
        value = get_value(document.header, "\\boxbgcolor", i)
        document.header[i] = "\\customcolor lyxboxbgcolor " + value[1:]
        document.header.insert(i + 1, "\\boxbgcolor lyxboxbgcolor")


# helper function for revert_doc_colors
def revert_doc_col(document, color, default_value, xcolor, x11, svg, dvips, nonedefault):
    "Reverts a given document color"

    i = find_token(document.header, color, 0)
    if i == -1:
        document.warning("Can't find %s header!" % color)
    else:
        value = get_value(document.header, color, i)
        if value == default_value or (nonedefault and value == "none"):
            # default; nothing more to do
            del document.header[i]
            return
        # check whether it is a color also otherwise used
        color_used = find_token(document.body, "\\color " + value) != -1 \
        or find_token(document.body, "framecolor \"" + value) != -1 \
        or find_token(document.body, "backgroundcolor \"" + value) != -1
        # check whether it is a known latexcolor
        if value in list(xcolor_names):
            if color_used == False and value.find(":") != -1:
                xcolor = True
                x11 = value.find("X11:") != -1
                svg = value.find("SVG:") != -1
                dvips = value.find("DVIPS:") != -1
                del document.header[i]
        # if not it must be a custom color
        else:
            # find custom color definition
            j = find_token(document.header, "\\customcolor " + value, 0)
            if j != -1:
                # get its value
                ccval = get_value(document.header, "\\customcolor", j).split()
                # and use its hex
                document.header[i] = color + " #" + ccval[1]
                if color_used == False:
                    # and delete custom color definition if not otherwise used
                    del document.header[j]
            else:
               # custom color not found. We can only warn
               document.warning("%s %s not found, we will set back to default!" % (color, value))
               del document.header[i]


def revert_doc_colors(document):
    "Reverts background, main font, shaded box and greyedout colors to old format or TeX code"

    xcolor = False
    x11 = False
    svg = False
    dvips = False
    revert_doc_col(document, "\\fontcolor", "none", xcolor, x11, svg, dvips, False)
    revert_doc_col(document, "\\backgroundcolor", "none", xcolor, x11, svg, dvips, False)
    revert_doc_col(document, "\\notefontcolor", "lightgray", xcolor, x11, svg, dvips, True)
    revert_doc_col(document, "\\boxbgcolor", "red", xcolor, x11, svg, dvips, True)
    
    # Preamble stuff if needed
    if xcolor == True:
        opts = []
        if x11 == True:
            opts.append("x11names")
        if svg == True:
            opts.append("svgnames")
        if dvips == True:
            opts.append("dvipsnames")
        options = "\\SetKeys[xcolor]{" + ",".join(opts) + "}"
        add_to_preamble(
            document,
            ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}",
             options]
        )


basic_box_colors = [
    "default",
    "none",
    "black",
    "white",
    "blue",
    "brown",
    "cyan",
    "darkgray",
    "gray",
    "green",
    "lightgray",
    "lime",
    "magenta",
    "orange",
    "olive",
    "pink",
    "purple",
    "red",
    "teal",
    "violet",
    "white",
    "yellow"
]


def revert_colorbox(document):
    """Change box color settings to LaTeX code for new colors."""

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Box", i)
        if i == -1:
            return

        j = find_end_of_inset(document.body, i)
        k = find_token(document.body, "\\begin_layout", i, j)
        if k == -1:
            document.warning("Malformed LyX document: no layout in Box inset!")
            i += 1
            continue

        # Get colour settings:
        framecolor = get_quoted_value(document.body, "framecolor", i, k)
        backcolor = get_quoted_value(document.body, "backgroundcolor", i, k + 1)
        if not framecolor or not backcolor:
            document.warning("Malformed LyX document: color options not found in Box inset!")
            i += 1
            continue
        if framecolor in list(basic_box_colors) and backcolor in list(basic_box_colors):
            i += 1
            continue

        # Set params to default
        p1 = find_token(document.body, "framecolor", i, j)
        if p1 != -1:
            document.body[p1] = "framecolor \"default\""
        p2 = find_token(document.body, "backgroundcolor", i, j)
        if p2 != -1:
            document.body[p2] = "backgroundcolor \"none\""
        
        if "Box Boxed" not in document.body[i]:
            i += 1
            continue

        # framed box, use \fcolorbox
        # Emulate new colors with LaTeX code
        einset = find_end_of_inset(document.body, i)
        if einset == -1:
            document.warning("Malformed LyX document: Can't find end of box inset!")
            i += 1
            continue

        bothcolors = framecolor + backcolor
        opts = []
        if bothcolors.find("X11:") != -1:
            opts.append("x11names")
        if bothcolors.find("SVG:") != -1:
            opts.append("svgnames")
        if bothcolors.find("DVIPS:") != -1:
            opts.append("dvipsnames")
        options = "\\SetKeys[xcolor]{" + ",".join(opts) + "}"
        for color in list(xcolor_names):
            if framecolor == color.lower():
               framecolor = color.split(":")[1]
            if backcolor == color.lower():
               backcolor = color.split(":")[1]
        
        add_to_preamble(document, ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}", options])
        # insert the closing brace first (keeps indices 'i' and 'einset' valid)
        document.body[einset + 1 : einset + 1] = put_cmd_in_ert("}")
        # now insert the (f)color box command
        # change the box type (frame added by \fcolorbox)
        document.body[i] = "\\begin_inset Box Frameless"
        # ensure an inner box:
        try:
            if not set_bool_value(document.body, "has_inner_box", True, i + 3, i + 4):
                set_bool_value(document.body, "use_makebox", True, i + 6, i + 7)
        except ValueError:
            document.warning(
                "Malformed LyX document: 'has_inner_box' or "
                "'use_makebox' option not found in box inset!"
            )
        ertinset = put_cmd_in_ert(f"\\fcolorbox{{{framecolor}}}{{{backcolor}}}{{")
        document.body[i:i] = ertinset + [""]
        i += 13


def revert_colortbl(document):
    """Revert colortbl features to TeX code."""

    # save some variables
    handledcolors = []
    xcolor = False
    colortbl = False
    x11 = False
    svg = False
    dvips = False
    tables = False
    preamble = []

    # Start with the global settings
    gbordercol = ""
    goddrowcol = ""
    gevenrowcol = ""
    galtrowstart = ""
    bordercolor_tex = "black"
    i = find_token(document.header, "\\table_border_color", 0)
    if i == -1:
        gbordercol == "default"
    else:
        gbordercol = get_value(document.header, "\\table_border_color", i)
        del document.header[i]
        # check whether it is a color also otherwise used
        color_used = find_token(document.body, "\\color " + gbordercol) != -1 \
        or find_token(document.body, "framecolor \"" + gbordercol) \
        or find_token(document.body, "backgroundcolor \"" + gbordercol)
        if color_used == True:
            handledcolors.append(gbordercol)
        # check whether it is a known latexcolor only used here
        for color in list(xcolor_names):
            if gbordercol == color.lower():
                if color_used == False and color.find(":") != -1:
                    xcolor = True
                    x11 = color.find("X11:") != -1
                    svg = color.find("SVG:") != -1
                    dvips = color.find("DVIPS:") != -1
        # if not there is nothing more to do (customcolors will
        # be reverted later on)
        else:
            # No need to check again for this later
            handledcolors.append(gbordercol)

        # Add preamble code
        if gbordercol != "default":
            bordercolor_tex = gbordercol
            for color in list(xcolor_names):
                if gbordercol == color.lower():
                   bordercolor_tex = color.split(":")[1]
            preamble.append("\\arrayrulecolor{%s}" % bordercolor_tex)
            colortbl = True

    goddrowcolor_tex = ""
    i = find_token(document.header, "\\table_odd_row_color", 0)
    if i == -1:
        goddrowcol == "default"
    else:
        goddrowcol = get_value(document.header, "\\table_odd_row_color", i)
        del document.header[i]
        if goddrowcol not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + goddrowcol) != -1 \
            or find_token(document.body, "framecolor \"" + goddrowcol) != -1 \
            or find_token(document.body, "backgroundcolor \"" + goddrowcol) != -1
            if color_used == True:
                handledcolors.append(goddrowcol)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if goddrowcol == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do (customcolors will
            # be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(goddrowcol)

        # get LaTeX color name
        if goddrowcol != "default":
            goddrowcolor_tex = goddrowcol
            for color in list(xcolor_names):
                if goddrowcol == color.lower():
                    goddrowcolor_tex = color.split(":")[1]

    gevenrowcolor_tex = ""
    i = find_token(document.header, "\\table_even_row_color", 0)
    if i == -1:
        gevenrowcol == "default"
    else:
        gevenrowcol = get_value(document.header, "\\table_even_row_color", i)
        del document.header[i]
        if gevenrowcol not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + gevenrowcol) != -1 \
            or find_token(document.body, "framecolor \"" + gevenrowcol) != -1 \
            or find_token(document.body, "backgroundcolor \"" + gevenrowcol) != -1
            if color_used == True:
                handledcolors.append(gevenrowcol)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if gevenrowcol == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do (customcolors will
            # be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(gevenrowcol)

        # get LaTeX color name
        if gevenrowcol != "default":
            gevenrowcolor_tex = gevenrowcol
            for color in list(xcolor_names):
                if gevenrowcol == color.lower():
                    gevenrowcolor_tex = color.split(":")[1]

    i = find_token(document.header, "\\table_alt_row_colors_start", 0)
    if i == -1:
        galtrowstart == "1"
    else:
        galtrowstart = get_value(document.header, "\\table_alt_row_colors_start", i)
        del document.header[i]


    # Add preamble code
    if goddrowcolor_tex != "" or gevenrowcolor_tex != "":
        preamble.append("\\rowcolors{%s}{%s}{%s}" % (galtrowstart, goddrowcolor_tex, gevenrowcolor_tex))
        colortbl = True
        tables = True

    # now that we have the global settings, handle the tables
    # in the body that use them
    i = 0
    re1 = re.compile(r"^<features borderColor=\"([^\"]+)\".*$", re.IGNORECASE)
    while True:
        i = find_re(document.body, re1, i)
        if i == -1:
            break

        m = re1.match(document.body[i])
        if not m:
            document.warning("malformed table header: %s" % document.body[i])
            continue
        bcval = m.group(1)
        # remove
        document.body[i] = document.body[i].replace(' borderColor="' + bcval + '"', "")
        if bcval == "default" or bcval == gbordercol:
            # default or global value; nothing more to do
            continue

        if bcval not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + bcval) != -1 \
            or find_token(document.body, "framecolor \"" + bcval) != -1 \
            or find_token(document.body, "backgroundcolor \"" + bcval) != -1
            if color_used == True:
                handledcolors.append(bcval)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if bcval == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do
            # (customcolors will be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(bcval)

        # Add ERT
        lay = get_containing_layout(document.body, i)
        if lay == False:
            document.warning("Table has no layout!")
            i += 1
            continue
        beglay = lay[3]
        endlay = find_end_of_layout(document.body, beglay)
        if endlay == False:
            document.warning("Table has no endlayout!")
            i += 1
            continue      
        # get LaTeX color name
        bcval_tex = bcval
        for color in list(xcolor_names):
            if bcval == color.lower():
               bcval_tex = color.split(":")[1]
        begcmd = put_cmd_in_ert("\\arrayrulecolor{%s}" % bcval_tex)
        endcmd = put_cmd_in_ert("\\arrayrulecolor{%s}" % bordercolor_tex)
        document.body[endlay : endlay] = endcmd
        document.body[beglay : beglay] = begcmd
        colortbl = True

    # rowcolors
    i = 0
    re1 = re.compile(r"^<features .*startAltRowColors=\"([^\"]+)\".*$", re.IGNORECASE)
    re2 = re.compile(r"^<features .*oddRowsColor=\"([^\"]+)\".*$", re.IGNORECASE)
    re3 = re.compile(r"^<features .*evenRowsColor=\"([^\"]+)\".*$", re.IGNORECASE)
    while True:
        startarc = "1"
        orc = "default"
        erc = "default"
        have_startarc = False
        have_orc = False
        have_erc = False
        l = find_re(document.body, re1, i)
        if l != -1:
            i = l
            have_startarc = True
            m = re1.match(document.body[l])
            if not m:
                document.warning("malformed table header: %s" % document.body[l])
            else:
                startarc = m.group(1)
            # remove from table header
            document.body[l] = document.body[l].replace(' startAltRowColors="' + startarc + '"', "")
        j = -1
        if l != -1:
            j = find_re(document.body, re2, i, i)
        else:
            j = find_re(document.body, re2, i)
        if j != -1:
            i = j
            l = j
            have_orc = True
            m = re2.match(document.body[j])
            if not m:
                document.warning("malformed table header: %s" % document.body[j])
            else:
                orc = m.group(1)
            # remove from table header
            document.body[j] = document.body[j].replace(' oddRowsColor="' + orc + '"', "")
        k = -1
        if l != -1:
            k = find_re(document.body, re3, i, i)
        else:
            k = find_re(document.body, re3, i)
        if k != -1:
            i = k
            have_erc = True
            m = re3.match(document.body[k])
            if not m:
                document.warning("malformed table header: %s" % document.body[k])
            else:
                erc = m.group(1)
            # remove from table header
            document.body[k] = document.body[k].replace(' evenRowsColor="' + erc + '"', "")
        if have_startarc == False and have_orc == False and have_erc == False:
            # nothing (left)
            break

        if startarc == galtrowstart and (erc == "default" or erc == goddrowcol) \
        and (orc == "default" or orc == gevenrowcol):
            # default or global value; nothing more to do
            i += 1
            continue

        altrowfonts = [erc, orc]
        for arf in altrowfonts:
            if arf not in handledcolors:
                # check whether it is a color also otherwise used
                color_used = find_token(document.body, "\\color " + arf) != -1 \
                or find_token(document.body, "framecolor \"" + arf) != -1 \
                or find_token(document.body, "backgroundcolor \"" + arf) != -1
                if color_used == True:
                    handledcolors.append(arf)
                # check whether it is a known latexcolor only used here
                for color in list(xcolor_names):
                    if arf == color.lower():
                        if color_used == False and color.find(":") != -1:
                            xcolor = True
                            x11 |= color.find("X11:") != -1
                            svg |= color.find("SVG:") != -1
                            dvips |= color.find("DVIPS:") != -1
                # if not there is nothing more to do
                # (customcolors will be reverted later on)
                else:
                    # No need to check again for this later
                    handledcolors.append(arf)

        # Add ERT
        lay = get_containing_layout(document.body, i)
        if lay == False:
            document.warning("Table has not layout!")
            i += 1
            continue
        beglay = lay[3]
        endlay = find_end_of_layout(document.body, beglay)
        if endlay == False:
            document.warning("Table has not endlayout!")
            i += 1
            continue      
        # get LaTeX color names
        orc_tex = orc
        erc_tex = erc
        for color in list(xcolor_names):
            if orc == color.lower():
               orc_tex = color.split(":")[1]
            elif erc== color.lower():
               erc_tex = color.split(":")[1]
        if orc_tex == "default":
            orc_tex = ""
        if erc_tex == "default":
            erc_tex = ""
        begcmd = put_cmd_in_ert("\\rowcolors{%s}{%s}{%s}" % (startarc, orc_tex, erc_tex))
        endcmd = put_cmd_in_ert("\\rowcolors{%s}{%s}{%s}" % (galtrowstart, goddrowcolor_tex, gevenrowcolor_tex))
        document.body[endlay : endlay] = endcmd
        document.body[beglay : beglay] = begcmd
        colortbl = True

    # Next, we handle cellcolors in table
    i = 0
    re1 = re.compile(r"^<cell .*color=\"([^\"]+)\".*$", re.IGNORECASE)
    while True:
        i = find_re(document.body, re1, i)
        if i == -1:
            break

        m = re1.match(document.body[i])
        if not m:
            document.warning("malformed cell header: %s" % document.body[i])
            continue
        ccval = m.group(1)
        # remove
        document.body[i] = document.body[i].replace(' color="' + ccval + '"', "")
        if ccval == "default":
            # default; nothing more to do
            continue

        if ccval not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + ccval) != -1 \
            or find_token(document.body, "framecolor \"" + ccval) != -1 \
            or find_token(document.body, "backgroundcolor \"" + ccval) != -1
            if color_used == True:
                handledcolors.append(ccval)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if ccval == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do
            # (customcolors will be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(ccval)

        # Add ERT
        lay = find_token(document.body, "\\begin_layout Plain Layout", i)
        if lay == False:
            document.warning("Table cell has not layout!")
            i += 1
            continue
        # get LaTeX color name
        ccval_tex = ccval
        for color in list(xcolor_names):
            if ccval == color.lower():
               ccval_tex = color.split(":")[1]
        cmd = put_cmd_in_ert("\\cellcolor{%s}" % ccval_tex)
        document.body[lay+1 : lay+1] = cmd
        colortbl = True

    # rowcolor in tables
    i = 0
    re1 = re.compile(r"^<row .*color=\"([^\"]+)\".*$", re.IGNORECASE)
    re_lo = re.compile(r"^<row .*colorleftoverhang=\"([^\"]+)\".*$", re.IGNORECASE)
    re_ro = re.compile(r"^<row .*colorrightoverhang=\"([^\"]+)\".*$", re.IGNORECASE)
    while True:
        i = find_re(document.body, re1, i)
        if i == -1:
            break

        m = re1.match(document.body[i])
        if not m:
            document.warning("malformed row header: %s" % document.body[i])
            continue
        rcval = m.group(1)
        # left overhang value
        clo = ""
        j = find_re(document.body, re_lo, i, i)
        if j != -1:
            m = re_lo.match(document.body[j])
            if m:
                clo = "[" + m.group(1) + "]"
                # remove
                document.body[j] = document.body[j].replace(' colorleftoverhang="' + m.group(1) + '"', "")
        # right overhang value
        cro = ""
        k = find_re(document.body, re_ro, i, i)
        if k != -1:
            m = re_ro.match(document.body[k])
            if m:
                cro = "[" + m.group(1) + "]"
                if clo == "":
                    clo = "[]"
                # remove
                document.body[j] = document.body[j].replace(' colorrightoverhang="' + m.group(1) + '"', "")
        # remove
        document.body[i] = document.body[i].replace(' color="' + rcval + '"', "")
        if rcval == "default":
            # default; nothing more to do
            continue

        if rcval not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + rcval) != -1 \
            or find_token(document.body, "framecolor \"" + rcval) != -1 \
            or find_token(document.body, "backgroundcolor \"" + rcval) != -1
            if color_used == True:
                handledcolors.append(rcval)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if rcval == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do
            # (customcolors will be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(rcval)

        # Add ERT
        lay = find_token(document.body, "\\begin_layout Plain Layout", i)
        if lay == -1:
            document.warning("Table row has no layout!")
            i += 1
            continue
        # get LaTeX color name
        rcval_tex = rcval
        for color in list(xcolor_names):
            if rcval == color.lower():
               rcval_tex = color.split(":")[1]
        cmd = put_cmd_in_ert("\\rowcolor{%s}%s%s" % (rcval_tex, clo, cro))
        document.body[lay+1 : lay+1] = cmd
        colortbl = True

    # columncolor
    i = 0
    re1 = re.compile(r"^<column .*color=\"([^\"]+)\".*$", re.IGNORECASE)
    re2 = re.compile(r"^<column .*special=\"([^\"]+)\".*$", re.IGNORECASE)
    re3 = re.compile(r"^<column .*decimal_point=\"([^\"]+)\".*$", re.IGNORECASE)
    re_lo = re.compile(r"^<column .*colorleftoverhang=\"([^\"]+)\".*$", re.IGNORECASE)
    re_ro = re.compile(r"^<column .*colorrightoverhang=\"([^\"]+)\".*$", re.IGNORECASE)
    while True:
        i = find_re(document.body, re1, i)
        if i == -1:
            break

        m = re1.match(document.body[i])
        if not m:
            document.warning("malformed column header: %s" % document.body[i])
            continue
        ccval = m.group(1)
        # remove
        document.body[i] = document.body[i].replace(' color="' + ccval + '"', "")
        # left overhang value
        clo = ""
        j = find_re(document.body, re_lo, i, i)
        if j != -1:
            m = re_lo.match(document.body[j])
            if m:
                clo = "[" + m.group(1) + "]"
                # remove
                document.body[j] = document.body[j].replace(' colorleftoverhang="' + m.group(1) + '"', "")
        # right overhang value
        cro = ""
        k = find_re(document.body, re_ro, i, i)
        if k != -1:
            m = re_ro.match(document.body[k])
            if m:
                cro = "[" + m.group(1) + "]"
                if clo == "":
                    clo = "[]"
                # remove
                document.body[j] = document.body[j].replace(' colorrightoverhang="' + m.group(1) + '"', "")

        if ccval == "default" or find_re(document.body, re2, i, i) != -1:
            # default or special entry; nothing more to do
            continue

        if ccval not in handledcolors:
            # check whether it is a color also otherwise used
            color_used = find_token(document.body, "\\color " + ccval) != -1 \
            or find_token(document.body, "framecolor \"" + ccval) != -1 \
            or find_token(document.body, "backgroundcolor \"" + ccval) != -1
            if color_used == True:
                handledcolors.append(ccval)
            # check whether it is a known latexcolor only used here
            for color in list(xcolor_names):
                if ccval == color.lower():
                    if color_used == False and color.find(":") != -1:
                        xcolor = True
                        x11 |= color.find("X11:") != -1
                        svg |= color.find("SVG:") != -1
                        dvips |= color.find("DVIPS:") != -1
            # if not there is nothing more to do
            # (customcolors will be reverted later on)
            else:
                # No need to check again for this later
                handledcolors.append(ccval)

        # Add value to special header
        # get LaTeX color name
        ccval_tex = ccval
        for color in list(xcolor_names):
            if ccval == color.lower():
               ccval_tex = color.split(":")[1]
        cmd = ""
        if find_substring(document.body, 'alignment="decimal"', i, i):
            decpoint = "."
            dp = find_re(document.body, re2, i, i)
            if dp != -1:
                m = re2.match(document.body[dp])
                if m:
                    decpoint = m.group(1)
            scmd = ">{\\columncolor{%s}%s%s}" % (ccval_tex, clo, cro)
            scmd2 = ">{\\columncolor{%s}[0pt][\\tabcolsep]}" % ccval_tex
            cmd = " special=\"" + scmd + "r@{\\extracolsep{0pt}" + decpoint + "}" + scmd2 + "l\">"
        else:
            cmd = " special=\">{\\columncolor{%s}%s%s}\">" % (ccval_tex, clo, cro)
        document.body[i] = document.body[i][:-1] + cmd
        colortbl = True

    # and finally, href colors
    i = find_token(document.header, "\\pdf_quoted_options", 0)
    if i != -1:
        pdfopts = get_quoted_value(document.header, "\\pdf_quoted_options", i).split()
        rex = re.compile(r"^.*color=([^,]+).*$", re.IGNORECASE)
        for pdfopt in pdfopts:
            m = rex.match(pdfopt)
            if not m:
                continue
            ccval = m.group(1)
            if ccval not in handledcolors:
                # check whether it is a known latexcolor only used here
                for color in list(xcolor_names):
                    x11 |= "X11:" + ccval == color
                    svg |= "SVG:" + ccval == color
                    dvips |= "DVIPS:" + ccval == color
                    xcolor |= "X11:" + ccval == color or "SVG:" + ccval == color or "DVIPS:" + ccval == color

    # To conclude, the preamble stuff
    if xcolor == True:
        opts = []
        if x11 == True:
            opts.append("x11names")
        if svg == True:
            opts.append("svgnames")
        if dvips == True:
            opts.append("dvipsnames")
        if tables == True:
            opts.append("table")
        options = "\\SetKeys[xcolor]{" + ",".join(opts) + "}"
        add_to_preamble(
            document,
            ["\\@ifundefined{rangeHsb}{\\usepackage{xcolor}}{}",
             options]
        )
    if colortbl == True:
        add_to_preamble(
            document,
            ["\\usepackage{colortbl}"]
        )
    if len(preamble) > 0:
        add_to_preamble(document, preamble)
 

def convert_crossref_package(document):
    "Converts \\use_refstyle to \\crossref_package"

    i = find_token(document.header, "\\use_refstyle", 0)
    if i != -1:
        value = get_value(document.header, "\\use_refstyle", i)
        if value == "1":
            document.header[i] = "\\crossref_package refstyle"
        else:
            document.header[i] = "\\crossref_package prettyref"


def revert_crossref_package(document):
    "Reverts \\crossref_package to \\use_refstyle"

    i = find_token(document.header, "\\crossref_package", 0)
    if i != -1:
        value = get_value(document.header, "\\crossref_package", i)
        if value == "prettyref":
            document.header[i] = "\\use_refstyle 0"
        else:
            document.header[i] = "\\use_refstyle 1"


def revert_cleveref(document):
    "Reverts cleveref commands to ERT"

    i = find_token(document.header, "\\crossref_package cleveref", 0)
    if i == -1:
        return

    # Reset header
    document.header[i] = "\\crossref_package prettyref"

    # Check and revert insets
    i = 0
    need_cleveref = False
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset ref", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        plural = get_bool_value(document.body, "plural", i, j, False)
        caps = get_bool_value(document.body, "caps", i, j, False)
        label = get_quoted_value(document.body, "reference", i, j)

        cmd = "\\"
        if caps:
            cmd += "C"
        else:
            cmd += "c"
        cmd += "ref"
        if plural:
            cmd += "s"
        cmd += "{" + label + "}"
        document.body[i : j + 1] = put_cmd_in_ert([cmd])
        need_cleveref = True
        i += 1

    # preamble
    if need_cleveref:
        add_to_preamble(
            document,
            ["\\usepackage{cleveref}"]
        )


def revert_zref(document):
    "Reverts zref-clever and zref-vario commands to ERT"

    use_zref = True
    i = find_token(document.header, "\\crossref_package zref", 0)
    if i == -1:
        use_zref = False

    if use_zref:
        # Reset header
        document.header[i] = "\\crossref_package prettyref"

    # Check and revert insets
    i = 0
    need_zref_clever = False
    need_zref_vario = False
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset ref", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        if not use_zref:
            # only need to remove the options
            del_token(document.body, "options", i, j)
            i += 1
            continue

        vref = False
        vpageref = False
        k = find_token(document.body, "LatexCommand formatted", i, j)
        if k == -1:
            k = find_token(document.body, "LatexCommand vref", i, j)
            if k == -1:
                k = find_token(document.body, "LatexCommand vpageref", i, j)
                if k == -1:
                    i += 1
                    continue
                vpageref = True
            else:
                vref = True

        caps = get_bool_value(document.body, "caps", i, j, False)
        nolink = get_bool_value(document.body, "nolink", i, j, False)
        label = get_quoted_value(document.body, "reference", i, j)
        options = get_quoted_value(document.body, "options", i, j)

        cmd = ""
        if vref:
            cmd = "\\zvref"
        elif vpageref:
            cmd = "\\zvpageref"
        else:
            cmd = "\\zcref"
        if nolink:
            cmd += "*"
        opts = ""
        if caps:
            opts = "S"
            if options != "":
                opts += "," + options
        elif options != "":
            opts = options
        if opts != "":
            cmd += "[" + opts + "]"
        cmd += "{" + label + "}"
        document.body[i : j + 1] = put_cmd_in_ert([cmd])
        if vref or vpageref:
            need_zref_vario = True
        else:
            need_zref_clever = True
        i += 1

    # preamble
    if need_zref_clever:
        add_to_preamble(
            document,
            ["\\usepackage{zref-clever}"]
        )
    if need_zref_vario:
        add_to_preamble(
            document,
            ["\\usepackage{zref-vario}"]
        )


def convert_reflists(document):
    "Remove commas from labels and references to work with lists"

    i = 0
    regexp = re.compile(r"(name|reference) \".*\,.*\"")
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset", i)
        if i == -1:
            break
        if document.body[i] != "\\begin_inset CommandInset label" \
        and document.body[i] != "\\begin_inset CommandInset ref":
            i += 1
            continue
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        k = find_re(document.body, regexp, i, j)
        if k != -1:
            # replace comma by dot
            document.body[k] = document.body[k].replace(",", ".")
        i += 1

    # Same for mathed
    i = 0
    regexp = re.compile(r"(.*\\)(label{|[a-zA-Z]*ref{)([^}]+\,[^}]+)(}.*)")
    while True:
        i = find_token(document.body, "\\begin_inset Formula", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of Formula inset at line %d!!" % (i))
            i += 1
            continue

        k = find_re(document.body, regexp, i, j)
        if k != -1:
            while True:
                m = regexp.match(document.body[k])
                if not m:
                    break
                # replace comma by dot
                document.body[k] = m.group(1) + m.group(2) + m.group(3).replace(",", ".") + m.group(4)
        i += 1


def revert_reflists(document):
    "Reverts crossref lists and ranges to ERT"

    package = "refstyle"
    i = find_token(document.header, "\\crossref_package", 0)
    if i == -1:
        document.warning("Missing \\crossref_package header!")
    else:
        package = get_value(document.header, "\\crossref_package", i)

    # Check and revert insets
    need_zref_clever = False
    need_zref_vario = False
    need_cleveref = False
    need_refstyle = False
    need_varioref = False
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset ref", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        reftype = get_value(document.body, "LatexCommand", i, j, "ref")
        label = get_quoted_value(document.body, "reference", i, j)
        if label.find(",") == -1 and reftype != "cpageref":
            # only need to remove the tuple param
            del_token(document.body, "tuple", i, j)
            i += 1
            continue

        caps = get_bool_value(document.body, "caps", i, j, False)
        plural = get_bool_value(document.body, "plural", i, j, False)
        nolink = get_bool_value(document.body, "nolink", i, j, False)
        options = get_quoted_value(document.body, "options", i, j)
        tupl = get_quoted_value(document.body, "tuple", i, j)
        labels = label.split(",")
        isRange = len(labels) == 2 and tupl == "range"

        cmd = "\\" + reftype
        cmd_options = []
        if reftype == "ref":
            if package == "cleveref":
                cmd = "\\labelcref"
                need_cleveref = True
            elif package == "zref":
                cmd = "\\zcref"
                if nolink:
                    cmd += "*"
                cmd_options.append("noname")
                need_zref_clever = True
        elif reftype == "pageref":
            if package == "cleveref":
                cmd = "\\labelcpageref"
                need_cleveref = True
            elif package == "zref":
                need_zref_clever = True
                cmd = "\\zcref"
                if nolink:
                    cmd += "*"
                cmd_options.append("noname")
                cmd_options.append("page")
        elif reftype == "cpageref":
            if package == "cleveref":
                need_cleveref = True
                if caps:
                    cmd = "\\Cpageref"
                if isRange:
                    cmd += "range"
            elif package == "zref":
                need_zref_clever = True
                cmd = "\\zcpageref"
                if isRange:
                    cmd_options.append("range")
            else:
                cmd = "\\pageref"
        elif reftype == "formatted":
            if package == "cleveref":
                need_cleveref = True
                if caps:
                    cmd = "\\Cref"
                else:
                    cmd = "\\cref"
                if isRange:
                    cmd += "range"
                if nolink:
                    cmd += "*"
            elif package == "zref":
                need_zref_clever = True
                cmd = "\\zcpageref"
                if nolink:
                    cmd += "*"
                if caps:
                    cmd_options.append("S")
            elif package == "refstyle":
                need_refstyle = True
                prefix = ""
                nlabels = []
                for l in labels:
                    if l.find(":") != -1:
                        ll = l.split(":")
                        if len(ll) > 1:
                            prefix = ll[0]
                            nlabels.append(ll[1])
                        else:
                            nlabels.append(ll[0])
                labels = nlabels
                if caps:
                    prefix = prefix[0].title() + prefix[1:]
                cmd = "\\" + prefix
                if isRange:
                    cmd += "range"
                cmd += "ref"
                if plural:
                    cmd_options.append("s")
        elif reftype == "vref":
            if package == "zref":
                need_zref_vario = True
                cmd = "\\zvref"
                if caps:
                    cmd_options.append("S")
            else:
                need_varioref = True
            if isRange:
                    cmd += "range"
            if nolink:
                cmd += "*"
        elif reftype == "vpageref":
            if package == "zref":
                cmd = "\\zvpageref"
                if caps:
                    cmd_options.append("S")
            else:
                need_varioref = True
            if isRange:
                    cmd += "range"
            if nolink:
                cmd += "*"

        if len(cmd_options) > 0:
            cmd += "[" + ", ".join(cmd_options) + "]"
        
        if isRange and (reftype == "vref" or reftype == "vpageref" or reftype == "cpageref" or (reftype == "formatted" and package != "prettyref")):
            cmd += "{" + labels[0] + "}{" + labels[1] + "}"
        elif package == "prettyref" or (package == "refstyle" and (reftype == "ref" or reftype == "pageref" or reftype == "cpageref")):
            cmds = []
            for l in labels:
                cmds.append(cmd + "{" + l + "}")
            cmd = ", ".join(cmds)
        else:
            cmd += "{" + ','.join(labels) + "}"
        document.body[i : j + 1] = put_cmd_in_ert([cmd])
        i += 1

    # check math refs
    if package == "zref" or package == "cleveref":
        regexp = re.compile(r".*(\\cpageref{).*")
        i = 0
        while True:
            i = find_token(document.body, "\\begin_inset Formula", i)
            if i == -1:
                break
            j = find_end_of_inset(document.body, i)
            if j == -1:
                document.warning("Can't find end of inset at line %d of body!" % i)
                i += 1
                continue
            k = find_re(document.body, regexp, i, j)
            if k != -1:
                if package == "zref":
                    need_zref_clever = True
                    document.body[k] = document.body[k].replace("\\cpageref", "\\zcpageref")
                else:
                    need_cleveref = True
                break
            i = j

    # preamble
    if need_zref_clever:
        add_to_preamble(
            document,
            ["\\usepackage{zref-clever}"]
        )
    if need_zref_vario:
        add_to_preamble(
            document,
            ["\\usepackage{zref-vario}"]
        )
    if need_cleveref:
        add_to_preamble(
            document,
            ["\\usepackage{cleveref}"]
        )
    if need_varioref:
        add_to_preamble(
            document,
            ["\\usepackage{varioref}"]
        )
    if need_refstyle:
        add_to_preamble(
            document,
            ["\\usepackage{refstyle}"]
        )


def revert_xr(document):
    "Removes filenames params from InsetRef"

    # Since LyX < 2.5 is fundamentally unable to support xr,
    # we can only remove the param
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset ref", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        del_token(document.body, "filenames", i, j)
        i += 1
        continue

ack_theorem_def_old = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended module to be loaded',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \thetheorem."',
    r'  Preamble',
    r'    \theoremstyle{remark}',
    r'    \newtheorem{acknowledgement}[thm]{\protect\acknowledgementname}',
    r'  EndPreamble',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ack_theorem_def_new = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended module to be loaded',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \thetheorem."',
    r'  TheoremName           "acknowledgement"',
    r'  TheoremLaTeXName      "acknowledgementname"',
    r'  TheoremCounter        "thm"',
    r'  TheoremStyle          "remark"',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ackStar_theorem_def_old = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires a theorems-ams-extended-* module to be loaded',
    r'Style Acknowledgement*',
    r'  CopyStyle             Remark*',
    r'  LatexName             acknowledgement*',
    r'  LabelString           "Acknowledgement."',
    r'  Preamble',
    r'    \theoremstyle{remark}',
    r'    \newtheorem*{acknowledgement*}{\protect\acknowledgementname}',
    r'  EndPreamble',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ackStar_theorem_def_new = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires a theorems-ams-extended-* module to be loaded',
    r'Style Acknowledgement*',
    r'  CopyStyle             Remark*',
    r'  LatexName             acknowledgement*',
    r'  LabelString           "Acknowledgement."',
    r'  TheoremName           "acknowledgement*"',
    r'  TheoremLaTeXName      "acknowledgementname"',
    r'  TheoremCounter        "none"',
    r'  TheoremStyle          "remark"',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ack_bytype_theorem_def_old = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended-bytype module to be loaded',
    r'Counter acknowledgement',
    r'  GuiName Acknowledgment',
    r'End',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \theacknowledgement."',
    r'  Preamble',
    r'    \theoremstyle{remark}',
    r'    \newtheorem{acknowledgement}{\protect\acknowledgementname}',
    r'  EndPreamble',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ack_bytype_theorem_def_new = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended-bytype module to be loaded',
    r'Counter acknowledgement',
    r'  GuiName Acknowledgment',
    r'End',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \theacknowledgement."',
    r'  TheoremName           "acknowledgement"',
    r'  TheoremLaTeXName      "acknowledgementname"',
    r'  TheoremStyle          "remark"',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ack_chap_bytype_theorem_def_old = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended-chap-bytype module to be loaded',
    r'Counter acknowledgement',
    r'  GuiName Acknowledgment',
    r'  Within chapter',
    r'End',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \theacknowledgement."',
    r'  Preamble',
    r'    \theoremstyle{remark}',
    r'    \ifx\thechapter\undefined',
    r'      \newtheorem{acknowledgement}{\protect\acknowledgementname}',
    r'    \else',
    r'      \newtheorem{acknowledgement}{\protect\acknowledgementname}[chapter]',
    r'    \fi',
    r'  EndPreamble',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]

ack_chap_bytype_theorem_def_new = [
    r'### Inserted by lyx2lyx (ams extended theorems) ###',
    r'### This requires theorems-ams-extended-chap-bytype module to be loaded',
    r'Counter acknowledgement',
    r'  GuiName Acknowledgment',
    r'  Within chapter',
    r'End',
    r'Style Acknowledgement',
    r'  CopyStyle             Remark',
    r'  LatexName             acknowledgement',
    r'  LabelString           "Acknowledgement \theacknowledgement."',
    r'  TheoremName           "acknowledgement"',
    r'  TheoremLaTeXName      "acknowledgementname"',
    r'  TheoremParentCounter  "chapter"',
    r'  TheoremStyle          "remark"',
    r'  LangPreamble',
    r'    \providecommand{\acknowledgementname}{_(Acknowledgement)}',
    r'  EndLangPreamble',
    r'  BabelPreamble',
    r'    \addto\captions$$lang{\renewcommand{\acknowledgementname}{_(Acknowledgement)}}',
    r'  EndBabelPreamble',
    r'  DocBookTag            para',
    r'  DocBookAttr           role="acknowledgement"',
    r'  DocBookItemTag        ""',
    r'End',
]


def convert_theorem_local_def(document):
    """Adapt acknowledgement theorems from local layout to new scheme"""
    if "theorems-ams-extended-bytype" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_old):
            document.append_local_layout(ackStar_theorem_def_new)
        if document.del_local_layout(ack_bytype_theorem_def_old):
            document.append_local_layout(ack_bytype_theorem_def_new)
    elif "theorems-ams-extended-chap-bytype" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_old):
            document.append_local_layout(ackStar_theorem_def_new)
        if document.del_local_layout(ack_chap_bytype_theorem_def_old):
            document.append_local_layout(ack_chap_bytype_theorem_def_new)
    elif "theorems-ams-extended" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_old):
            document.append_local_layout(ackStar_theorem_def_new)
        if document.del_local_layout(ack_theorem_def_old):
            document.append_local_layout(ack_theorem_def_new)


def revert_theorem_local_def(document):
    """Adapt acknowledgement theorems from local layout to old scheme"""
    if "theorems-ams-extended-bytype" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_new):
            document.append_local_layout(ackStar_theorem_def_old)
        if document.del_local_layout(ack_bytype_theorem_def_new):
            document.append_local_layout(ack_bytype_theorem_def_old)
    elif "theorems-ams-extended-chap-bytype" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_new):
            document.append_local_layout(ackStar_theorem_def_old)
        if document.del_local_layout(ack_chap_bytype_theorem_def_new):
            document.append_local_layout(ack_chap_bytype_theorem_def_old)
    elif "theorems-ams-extended" in document.get_module_list():
        if document.del_local_layout(ackStar_theorem_def_new):
            document.append_local_layout(ackStar_theorem_def_old)
        if document.del_local_layout(ack_theorem_def_new):
            document.append_local_layout(ack_theorem_def_old)


def revert_prettyref_l7n(document):
    """Revert prettyref-l7n crossref format to prettyref"""
    i = find_token(document.header, "\\crossref_package prettyref-l7n", 0)
    if i != -1:
        document.header[i] = "\\crossref_package prettyref"

def convert_refstyle_unsupported(document):
    """Convert previously unsupported refstyle refs (enu: and alg:) to simple ref"""
    if find_token(document.header, "\\crossref_package refstyle", 0) == -1:
        return

    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset CommandInset ref", i)
        if i == -1:
            break
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Can't find end of reference inset at line %d!!" % (i))
            i += 1
            continue

        k = find_token(document.body, "LatexCommand formatted", i, j)
        if k == -1:
            i += 1
            continue

        label = get_quoted_value(document.body, "reference", i, j)
        if label.startswith("enu:") or label.startswith("alg:"):
            document.body[k] = "LatexCommand ref"

        i += 1

def revert_justification_pref(document):
    """Revert justification pref setting"""
    i = find_token(document.header, "\\justification default", 0)
    if i != -1:
        document.header[i] = "\\justification true"


def convert_justification_pref(document):
    """Convert justification pref setting"""
    i = find_token(document.header, "\\justification true", 0)
    if i != -1:
        document.header[i] = "\\justification default"


def revert_plimsoll(document):
    """Add plimsoll package when that is used"""
    i = 0
    while True:
        i = find_token(document.body, "\\begin_inset Formula", i)
        if i == -1:
            return
        j = find_end_of_inset(document.body, i)
        if j == -1:
            document.warning("Malformed LyX document: Could not find end of Formula inset.")
            i += 1
            continue
        k = find_substring(document.body, "\\plimsoll", i, j)
        if k == -1:
            i = j + 1
            continue
        add_to_preamble(document, ["\\usepackage{plimsoll}"])
        return


def revert_html_preamble(document):
    """Revert html preamble to local layout"""
    i = find_token(document.header, "\\begin_preamble_html", 0)
    if i == -1:
        return

    j = find_end_of(document.header, i, "\\begin_preamble_html", "\\end_preamble_html")
    if j == -1:
        # this should not happen
        document.warning("Malformed LyX document: Could not find end of html preamble.")
        return

    html_def = document.header[i + 1 : j]
    document.header[i : j + 1] = []
    if len(html_def) > 0:
        html_def.insert(0, "HTMLPreamble")
        html_def.append("EndPreamble")
        document.append_local_layout(html_def)


##
# Conversion hub
#

supported_versions = ["2.5.0", "2.5"]
convert = [
    [621, [convert_url_escapes, convert_url_escapes2]],
    [622, []],
    [623, [convert_he_letter]],
    [624, [convert_biblatex_chicago]],
    [625, []],
    [626, []],
    [627, [convert_nomencl, convert_index_sc]],
    [628, []],
    [629, []],
    [630, []],
    [631, [convert_mathml_version]],
    [632, []],
    [633, [convert_doc_colors]],
    [634, []],
    [635, [convert_crossref_package]],
    [636, []],
    [637, [convert_reflists]],
    [638, []],
    [639, [convert_theorem_local_def]],
    [640, []],
    [641, [convert_justification_pref]],
    [642, []],
    [643, [convert_refstyle_unsupported]]
]


revert = [
    [642, [revert_html_preamble]],
    [641, [revert_plimsoll]],
    [640, [revert_justification_pref]],
    [639, [revert_prettyref_l7n]],
    [638, [revert_theorem_local_def]],
    [637, [revert_xr]],
    [636, [revert_reflists]],
    [635, [revert_cleveref, revert_zref]],
    [634, [revert_crossref_package]],
    [633, [revert_colortbl]],
    [632, [revert_doc_colors, revert_colorbox]],
    [631, [revert_textcolor, revert_custom_colors]],
    [630, [revert_mathml_version]],
    [629, [revert_new_polyglossia_languages, revert_new_babel_languages]],
    [628, [revert_langopts]],
    [627, [revert_nomentbl]],
    [626, [revert_nomencl, revert_index_sc]],
    [625, [revert_nomencl_textwidth]],
    [624, [revert_nptextcite]],
    [623, [revert_biblatex_chicago]],
    [622, []],
    [621, [revert_glue_parskip]],
    [620, [revert_url_escapes2, revert_url_escapes]],
]


if __name__ == "__main__":
    pass
