# duckcharset - auto-detect and convert character encoding, by using chardet
#
# Copyright 2010 Yuya Nishihara <yuya@tcha.org>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.

"""auto-convert character encoding when displaying

This depends on Universal Encoding Detector, aka. chardet, library:
http://chardet.feedparser.org/

TODO
"""

from mercurial import extensions
from mercurial import encoding, util, commands, context
from mercurial.hgweb import webcommands

import sys, os, re

_disabled = True  # set to False to enable convertenc temporarily

def runenabled(orig, *args, **kwargs):
    """Runs the given func under convertenc-enabled environment"""
    global _disabled

    if not _disabled:
        return orig(*args, **kwargs)  # already enabled

    _disabled = False
    try:
        return orig(*args, **kwargs)
    finally:
        _disabled = True

def runiterenabled(orig, *args, **kwargs):
    """Runs the given func and generated iter under convertenc-enabled env"""
    global _disabled

    if not _disabled:
        return orig(*args, **kwargs)

    _disabled = False
    try:
        return iter(list(orig(*args, **kwargs)))
    finally:
        _disabled = True

def runwebenabled(orig, web, req, tmpl):
    """Runs the given webcommand under convertenc-enabled environment"""
    mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
    if 'html' in mt:
        return runiterenabled(orig, web, req, tmpl)
    else:
        return orig(web, req, tmpl)

def detectenc(text):
    """Detects character encoding by first 8 lines containing multi-byte char"""
    try:
        from chardet.universaldetector import UniversalDetector
    except ImportError:
        # FIXME: use ui obj
        sys.stderr.write('*** failed to import chardet module ***\n')
        return

    MB = re.compile(r'[\x80-\xff]')
    detector = UniversalDetector()
    for i, line in enumerate(l for l in text.splitlines() if MB.search(l)):
        detector.feed(line)
        # TODO: by minimal confidence, e.g. >= 0.8
        if detector.done or i >= 7:
            break

    detector.close()
    return detector.result['encoding']

def convertenc(text, textenc):
    """Converts the given text to local encoding"""
    # TODO: stricter way to test encoding
    if _disabled or encoding.encoding == textenc or not textenc:
        return text

    # TODO: keep unconvertable characters as possible
    return text.decode(textenc, 'replace').encode(encoding.encoding, 'replace')

def convertencdata(data):
    """Detects and converts character encoding of the given data"""
    if _disabled or util.binary(data):
        return data

    return convertenc(data, detectenc(data))


def duckfctxdata(orig, self):
    return convertencdata(orig(self))


def hgtksetup():
    from tortoisehg.hgtk import changeset
    extensions.wrapfunction(changeset.ChangeSet, 'append_diff', runenabled)
    # TODO: not fully supported yet

def hgviewsetup():
    global _disabled
    _disabled = False  # safe because hgview does nothing to modify repo

def uisetup(ui):
    for fctx in (context.filectx, context.workingfilectx):
        extensions.wrapfunction(fctx, 'data', duckfctxdata)

def extsetup():
    if 'hgtk' in os.path.basename(sys.argv[0]):  # TODO: ugly
        hgtksetup()
    if 'hgview' in os.path.basename(sys.argv[0]):  # TODO: also ugly
        hgviewsetup()

    for cmd in ('annotate', 'diff', 'incoming', 'log', 'outgoing'):
        extensions.wrapcommand(commands.table, cmd, runenabled)

    # TODO: grep, ...

    for cmd in ('annotate', 'diff', 'file', 'rev'):
        extensions.wrapfunction(webcommands, cmd, runwebenabled)

    try:
        mq = extensions.find('mq')
        extensions.wrapcommand(mq.cmdtable, 'qdiff', runenabled)
    except KeyError:
        pass
