#!/usr/bin/env python2.7
# -*- coding: utf-8 -*-
#
# cmap.py
#
# Copyright (c) 2013,
# Виталий Волков <hash.3g@gmail.com>
# Dave Crossland <dave@understandinglimited.com>
#
# Released under the GNU General Public License version 3 or later.
# See accompanying LICENSE.txt file for details.
from __future__ import print_function
import argparse
import codecs
import os
import re
import sys

path = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(os.path.realpath(path))

sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

import fontaine
from fontaine import VERSION
from fontaine.cmap import library
from fontaine.builder import Builder, Director
from importlib import import_module
from operator import itemgetter


class FontaineArgParser(argparse.ArgumentParser):

    def error(self, message):
        if '--collections' not in message:
            sys.stderr.write('error: %s\n' % message)
        else:
            result = get_collections()
            max_length = 0
            for t in result:
                max_length = max(len(t[0]), max_length)
            err = '{0:<%s}{1}' % (max_length + 2)
            sys.stderr.write('%s\n' % message)
            string = '\n'.join(map(lambda x: err.format(x[0], x[1]), result))
            print(string)
        sys.exit(2)


def get_collections():
    result = []
    for ext in fontaine.charmaps.internals.__all__:
        try:
            module = import_module('fontaine.charmaps.internals.%s' % ext)
            try:
                shortname = module.Charmap.short_name
            except AttributeError:
                shortname = ext
            longname = module.Charmap.common_name or ext

            result.append((shortname, longname))
        except (ImportError, AttributeError):
            continue
    return sorted(result, key=itemgetter(0))


def collections(s):
    if not s:
        return
    result = get_collections()

    shorts = map(lambda x: x[0], result)
    invalid = []
    for r in s.split(','):
        if r in shorts:
            return s
        invalid.append(r)

    error = 'invalid collections "%s"' % ' '.join(invalid)
    raise argparse.ArgumentTypeError(error)


def usage():
    description = """
    PyFontaine is a tool to analize your fonts or help to see glyphs analytics.
    """

    parser = FontaineArgParser(description=description)
    parser.add_argument('font', metavar='font', type=str, nargs='+')
    if len(sys.argv) == 1:
        print('pyFontaine %s (https://github.com/davelab6/pyfontaine)' % VERSION)
        parser.print_help()
        sys.exit(1)
    parser.add_argument('--flush-cache', action='store_true',
                        help='Clean application cache')
    parser.add_argument('--disable-unames', action='store_true',
                        help='This will prevent using Unicode names data')
    parser.add_argument('--missing', action='store_true',
                        help='Print additionally a list of each unicode value'
                             ' that is missing from a character set')
    parser.add_argument('--text', action='store_true',
                        help='Output information in plain text')
    parser.add_argument('--xml', action='store_true',
                        help='Output information into XML')
    parser.add_argument('--json', action='store_true',
                        help='Output information in JSON')
    parser.add_argument('--wiki', action='store_true',
                        help='Print information in wikipedia format')
    parser.add_argument('--csv', action='store_true',
                        help=('Output font coverage information in CSV.'
                              ' If glyphs have been passed to script then'
                              ' it shows rows with character set and values'
                              ' True if glyph exists in collections'))
    parser.add_argument('--show-hilbert', action='store_true',
                        help='Create PNG files represented coveragin of font'
                             ' charset in coverage_pngs directory')
    parser.add_argument('--set', type=str, default='',
                        help='Specify the sets based on either common'
                             ' or native name')
    parser.add_argument('--collections', type=collections, default='',
                        help='Specify collections of charmaps to print')

    url = 'https://github.com/davelab6/pyfontaine'
    param = '%(version)s (%(url)s)' % {'url': url, 'version': VERSION}
    parser.add_argument('-V', '--version', action='version',
                        version="%(prog)s " + param)
    return parser.parse_args()


def check_for_unicodechar(char):

    # char = char.encode('ascii').decode('utf8')
    char = char.decode('utf8')

    if len(char) == 1:
        return ord(char)

    if re.match('0x[0-9A-Fa-f]+', char):
        return eval(char)

    unimatch = re.match(r'U\+([0-9A-Fa-f]+)', char)
    if unimatch:
        return eval('0x%s' % unimatch.group(1))

    return None


def main(*argv):
    args = usage()

    if args.flush_cache:
        import shutil
        from fontaine.ext.update import get_data_directory
        shutil.rmtree(get_data_directory())

    glyphs = []
    fonts = []

    for filename in args.font:
        unicodecode = check_for_unicodechar(filename)

        if unicodecode:
            glyphs.append(unicodecode)
            continue

        if not os.path.exists(filename):
            print("'%s' is not found" % (filename))
            sys.exit(1)
        fonts.append(filename)

    if not fonts and not glyphs:
        sys.exit(0)

    if args.collections:
        library.collections = args.collections.split(',')

    if fonts:
        if args.disable_unames:
            os.environ['DISABLE_UNAMES'] = 'disable'

        director = Director(charmaps=args.set.split(','),
                            missing=args.missing)
        if args.xml:
            tree = director.construct_tree(fonts)
            Builder.xml_(tree).display()
        elif args.csv:
            sys.stdout.write(Builder.csv_(fonts))
        elif args.json:
            tree = director.construct_tree(fonts)
            Builder.json_(tree)
        elif args.wiki:
            Builder.wiki(fonts)
        else:
            tree = director.construct_tree(fonts)
            Builder.text_(tree).display()

    if glyphs:
        rows = [['Character Set'] + map(lambda x: '%s (%s)' % (unichr(x), hex(x)), glyphs)]
        max_length = 0
        for charmap in library.charmaps:
            charset_glyphs = charmap.glyphs
            if callable(charset_glyphs):
                charset_glyphs = charset_glyphs()

            max_length = max(max_length, len(charmap.common_name))
            row = [charmap.common_name]
            has_true = False
            for glyph in glyphs:
                value = glyph in charset_glyphs
                if value:
                    has_true = True
                row.append(str(value or ''))

            if has_true:
                rows.append(row)

        if args.csv:
            import cStringIO
            data = cStringIO.StringIO()
            doc = UnicodeWriter(data, delimiter=';')
            doc.writerows(rows)
            data.seek(0)
            print(data.read().decode('utf-8'))
        else:
            for row in rows:
                string = ''
                first = True
                for g in row:
                    if first:
                        string += (u'{:<%s}' % (max_length + 4)).format(g)
                        first = False
                    else:
                        string += u'{:<15}'.format(g)
                print(string)


import csv


class UnicodeWriter:

    def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
        # Redirect output to a queue
        import cStringIO
        self.queue = cStringIO.StringIO()
        self.writer = csv.writer(self.queue, dialect=dialect, **kwds)
        self.stream = f
        self.encoder = codecs.getincrementalencoder(encoding)()

    def writerow(self, row):
        self.writer.writerow([s.encode("utf-8") for s in row])
        # Fetch UTF-8 output from the queue ...
        data = self.queue.getvalue()
        data = data.decode("utf-8")
        # ... and reencode it into the target encoding
        data = self.encoder.encode(data)
        # write to the target stream
        self.stream.write(data)
        # empty queue
        self.queue.truncate(0)

    def writerows(self, rows):
        for row in rows:
            self.writerow(row)

if __name__ == '__main__':
    main()
