#!/usr/bin/env python3
# -*- coding: utf-8 -*-

#==============================================================================
# Copyright (C) 2011 by Loïc Grobol
#
# This file is part of CTESLO.
# 
#    CTESLO is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
# 
#    CTESLO 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 CTESLO.  If not, see <http://www.gnu.org/licenses/>.
#==============================================================================

"""
Created on 20 juin 2011

@author: Loïc Grobol <loic.grobol@gmail.com>
"""

import tempfile, shutil
import subprocess, os, sys
import argparse
import pkgutil

import formats, taggers

from libcteslo.dflib import columns_handling as dfch, tokenization as dft, abstractions as abst
from libcteslo.tasks.utils import textify, formatify, columnize, \
     dynamic_import, copy, temporary_files_collection
from libcteslo.tasks.externals import apply_external_tools

_copy = shutil.copy2
DEFAULT_TOOLS = ["cem", "lglex_lefff"]
ROOT = sys.path[0]


def tag(in_stream, out_stream,
            external_tools=DEFAULT_TOOLS,
            tagger=taggers.DEFAULT_TAGGER,
            file_format=formats.DEFAULT_INPUT_FORMAT):

    with tempfile.TemporaryDirectory(prefix=".cteslo_") as temp:
        # Temp files
        in_file = os.path.join(temp, "in")
        t = temporary_files_collection(in_file)

        copy(in_stream, in_file) # Safety copy
        textify(in_file, t.textified, file_format)
        _tag(t.textified, t.tagged, external_tools, tagger)
        formatify(t.tagged, t.final, file_format, "-s", in_file)
        copy(t.final, out_stream)


def _tag(in_stream, out_stream,
            external_tools=DEFAULT_TOOLS,
            tagger=taggers.DEFAULT_TAGGER):
    """Tokenize a text file, apply external tools, then tag."""
    # Load tagger's-specific utilities
    tagger = dynamic_import("taggers." + tagger)

    with tempfile.TemporaryDirectory(prefix=".cteslo_") as temp:
        # Temp files
        in_file = os.path.join(temp, "in")
        t = temporary_files_collection(in_file)
        # Utilities
        tags_tagger = os.path.join(ROOT, "taggers", tagger.NAME, tagger.LABELER)
        lex_tagger = os.path.join(ROOT, "taggers", tagger.NAME, tagger.LABELER)

        copy(in_stream, in_file)

        _make_taggable(in_file, t.external_tools_ed)

        # Lexeme-tag
        subprocess.call(" ".join([lex_tagger, "-i", t.external_tools_ed, "-o", t.lex_tagged, "-m", "lexemes"]), shell=True)
        dft.stream_lexemize(t.lex_tagged, t.taggable)

        # Label-tag
        subprocess.call(" ".join([tags_tagger, "-i", t.taggable, "-o", t.tagged, "-m", "tags"]), shell=True)
        dfch.stream_select(t.tagged, t.final, [0, -1])

        copy(t.final, out_stream)


def _make_taggable(in_stream, out_stream,
            external_tools=DEFAULT_TOOLS):
    """Columnize a clean text file and add external tools' data."""
    with tempfile.TemporaryDirectory(prefix=".cteslo_") as temp:
        # Temp files
        in_file = os.path.join(temp, "in")
        t = temporary_files_collection(in_file)

        copy(in_stream, in_file)
        columnize(in_file, t.columnized, file_format=None)
        apply_external_tools(in_file, t.external_tools_res, external_tools)
        dfch.stream_insert(t.external_tools_res, t.columnized, t.final)
        copy(t.final, out_stream)


def make_train(in_stream, lex_output, tags_output,
               external_tools=DEFAULT_TOOLS,
               file_format="tagged_csv"):
    with tempfile.TemporaryDirectory(prefix=".cteslo_") as temp:
        # Temp files
        in_file = os.path.join(temp, "in")
        t = temporary_files_collection(in_file)
        # Output the input stream content inside a temp file
        copy(in_stream, in_file)

        textify(in_file, t.textified, file_format)

        dft.stream_tokenize(in_file, t.columnized)
        _make_taggable(t.textified, t.external_data, external_tools)

        dfch.stream_insert(t.columnized, t.external_data, t.lex_tagged, [-1], [-1])
        copy(t.lex_tagged, lex_output)

        dft.stream_lexemize(t.lex_tagged, t.lexemized)
        dfch.stream_insert(in_file, t.lexemized, t.tags_tagged, [-1], [-1])
        copy(t.tags_tagged, tags_output)


def make_eval(in_stream, out_stream,
            external_tools=DEFAULT_TOOLS,
            tagger=taggers.DEFAULT_TAGGER,
            nolabels=False,
            file_format="tagged_csv"):
    """Make evaluation files for crotal's ceval."""

    with tempfile.TemporaryDirectory(prefix=".cteslo_") as temp:
        # Temp files
        in_file = os.path.join(temp, "in")
        t = temporary_files_collection(in_file)

        copy(in_stream, in_file)  # Safety copy
        tag(in_file, t.tagged, external_tools, tagger, file_format)  # get the tags
        dft.stream_tokenize(t.tagged, t.tokres)

        def foo(line):
            if line.is_blank:
                return abst.BLANK_LINE
            scheme = "{0}-{1}".format(line[-1], line[-2]) if not nolabels else "{0}-segment".format(line[-1])
            return abst.DataLine((line[0], scheme))

        abst.compose(t.tokres, t.res, foo)

        dft.stream_tokenize(in_file, t.tokref)
        abst.compose(t.tokref, t.ref, foo)
        dfch.stream_insert(t.ref, t.res, out_stream, [-1], [-1])


def main(args=sys.argv[1:]):
    parser = argparse.ArgumentParser(description="CRF morphosyntactic tagging tool.")
    subparsers = parser.add_subparsers(help="sub-commands")

    # Commons
    args_format = argparse.ArgumentParser(add_help=False)
    # Find all the available formats (subpackages of format)
    formats_choices = [name
                      for (module_loader, name, ispkg) in pkgutil.iter_modules(["formats"])
                      if ispkg]
    args_format.add_argument("--file-format", "-f",
                     choices=formats_choices,
                     default=formats.DEFAULT_INPUT_FORMAT,
                     help="The format of the input file. (default: %(default)s).")
    # Filters : output > command > output
    args_filters_io = argparse.ArgumentParser(add_help=False)
    args_filters_io.add_argument("--in-stream", "-i",
                         type=argparse.FileType('r'),
                         default=sys.stdin,
                         help="Input file to be processed. Standard input by default.")
    args_filters_io.add_argument("--out-stream", "-o",
                         type=argparse.FileType('w'),
                         default=sys.stdout,
                         help="Output file. Standard output by default.")

    # Tag a file : the regular use of this software
    tag_parser = subparsers.add_parser("tag",
                        parents=[args_filters_io, args_format],
                        description="Tokenize and tag a text file")
    tag_parser.set_defaults(func=tag)

    #===========================================================================
    # # Add external tools columns but don't tag
    # add_externals_parser = subparsers.add_parser("add_data",
    #                parents=[args_filters_io, args_format],
    #                description="Add data from external tools to a text file")
    # add_externals_parser.set_defaults(func=add_externals)
    #===========================================================================

    # make_train : input > command > output1, output2
    make_train_parser = subparsers.add_parser("make_train",
                    description="Make training corpus from a tagged corpus.")
    make_train_parser.add_argument("--in-stream", "-i",
                         type=argparse.FileType('r'),
                         default=sys.stdin,
                         help="Tagged corpus. Standard input by default.")

    make_train_parser.add_argument("--lex-output", "-l",
                         type=argparse.FileType('w'),
                         help="Training corpus for segmentation.")
    make_train_parser.add_argument("--tags-output", "-t",
                         type=argparse.FileType('w'),
                         help="Training corpus for morphosyntactic tags.")
    make_train_parser.set_defaults(func=make_train, file_format="tagged_csv")


    # Make ceval-evaluable file from tagged corpus
    make_eval_parser = subparsers.add_parser("make_eval",
                        parents=[args_filters_io],
                        description="Make evaluation file for ceval.")
    make_eval_parser.add_argument("--nolabels",
                        action="store_true",
                        help="Make evaluation for segmentation only")
    make_eval_parser.set_defaults(func=make_eval, file_format="tagged_csv")

    args = parser.parse_args(args)
    if args.in_stream.isatty():  # If invoked from terminal without any piping
        parser.print_help()
    else:
        # Forgive me
        try:
            return args.func(**{arg:val for arg, val in vars(args).items() if arg != "func"})
        except UnicodeDecodeError:
            sys.stderr.write("The input file's encoding is not proper utf-8. Please make sure the input files are all encoded in utf-8.\n")

if __name__ == "__main__":
    sys.exit(main())
