# -*- coding: utf-8 -*-
#
#  Copyright (C) 2016 by Igor E. Novikov
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.


import os
import cairo
from copy import deepcopy

from uc2 import libcairo

import core
from core import NONPRINTING_CHARS
from langs import check_maynmar, check_arabic


def cluster_text(text, clusters):
    index = 0
    text_seq = []
    for item in clusters:
        if text[index:item[0]]:
            text_seq += list(text[index:item[0]])
        text_seq += [text[item[0]:item[1]], ]
        index = item[1]
    if text[index:]:
        text_seq += list(text[index:])
    return text_seq


def word_group(text_seq):
    i = 0
    index = None
    while i < len(text_seq):
        if index is not None:
            if not text_seq[i] in NONPRINTING_CHARS:
                text_seq[index] += text_seq[i]
                text_seq[i] = ' '
            else:
                index = None
        else:
            if not text_seq[i] in NONPRINTING_CHARS:
                index = i
        i += 1


def rtl_word_group(text_seq):
    i = 0
    buff = ''
    while i < len(text_seq):
        if not text_seq[i] in NONPRINTING_CHARS:
            buff += text_seq[i]
            text_seq[i] = ' '
        else:
            if buff:
                text_seq[i - 1] = buff
                buff = ''
        i += 1
    if buff:
        text_seq[i - 1] = buff


def rtl_word_group_in_reg(text_seq, reg):
    i = reg[0]
    buff = ''
    while i < reg[1]:
        if not text_seq[i] in NONPRINTING_CHARS:
            buff += text_seq[i]
            text_seq[i] = ' '
        else:
            if buff:
                text_seq[i - 1] = buff
                buff = ''
        i += 1
    if buff:
        text_seq[i - 1] = buff


def find_rtl_regs(layout_data):
    rtl_regs = []
    reg = []
    i = 0
    while i < len(layout_data) - 1:
        if not reg and layout_data[i][5] > layout_data[i + 1][5]:
            reg.append(i)
        elif reg and layout_data[i][5] < layout_data[i + 1][5]:
            reg.append(i + 1)
            rtl_regs.append(reg)
            reg = []
        i += 1
    if reg:
        reg.append(i + 1)
        rtl_regs.append(reg)
    return rtl_regs


def utf8_to_ucs4_dict(text):
    text_dict = {}
    index = 0
    ucs4_index = 0
    for item in text:
        for char in item:
            text_dict[index] = ucs4_index
            index += len(char.encode('utf-8'))
        ucs4_index += 1
    text_dict[index] = -1
    return text_dict


def fix_rlt_clusters(clusters_index, byte_dict):
    clusters = []
    for item in clusters_index:
        start, end = item
        start, end = byte_dict[start], byte_dict[end]
        cluster = (min(start, end), max(start, end) + 1)
        clusters.append(cluster)
    return clusters


def fix_rlt_regs(rtl_regs, layout_data, byte_dict):
    regs = []
    for item in rtl_regs:
        start, end = item
        start, end = layout_data[start][5], layout_data[end - 1][5]
        start, end = byte_dict[start], byte_dict[end]
        reg = (min(start, end), max(start, end) + 1)
        regs.append(reg)
    return regs


def assemble_to_lines(text, rtl_regs):
    if rtl_regs:
        index = 0
        text_seq = ()
        for item in rtl_regs:
            if text[index:item[0]]:
                text_seq += tuple(text[index:item[0]])
            text_seq += (''.join(text[item[0]:item[1]]),)
            text_seq += (len(text[item[0]:item[1]]) - 1) * (' ',)
            index = item[1]
        if text[index:]:
            text_seq += tuple(text[index:])
        text = text_seq
    return text


def get_rtl_layout_data(layout_data, rtl_regs):
    index = 0
    data = []
    for item in rtl_regs:
        if layout_data[index:item[0]]:
            data += layout_data[index:item[0]]
        rtl = layout_data[item[0]:item[1]]
        rtl.reverse()
        i = 0
        while i < len(rtl):
            x, y, w, h, bl, j = rtl[i]
            rtl[i] = (x + w, y, -w, h, bl, j)
            i += 1
        data += rtl
        index = item[1]
    if layout_data[index:]:
        data += layout_data[index:]
    return data


def is_item_in_rtl(index, rtl_regs):
    for item in rtl_regs:
        if index < item[0]:
            return False
        if index >= item[1]:
            continue
        elif item[0] <= index < item[1]:
            return True
    return False


def get_log_layout_data(layout_data, byte_dict, rtl_regs):
    log_layout_data = len(layout_data) * ['', ]
    index = 0
    for item in layout_data:
        if is_item_in_rtl(index, rtl_regs):
            x, y, w, h, bl, j = item
            data = (x + w, y, -w, h, bl, j)
        else:
            data = deepcopy(item)
        log_layout_data[byte_dict[item[5]]] = data
        index += 1
    return log_layout_data


def get_glyphs(ctx, layout_data, text, width, text_style, markup):
    glyphs = []
    i = -1
    for item in text:
        i += 1

        if item in NONPRINTING_CHARS:
            res = False
            if markup and not item == '\n':
                for mrk in markup:
                    if 'u' in mrk[0] or 's' in mrk[0]:
                        if mrk[1][0] <= i < mrk[1][1]:
                            res = True
                            break
            if not res:
                glyphs.append(None)
                continue

        ctx.new_path()
        ctx.move_to(0, 0)
        layout = core.create_layout(ctx)
        text_range = [i, i + len(item)]
        vpos = core.set_glyph_layout(item, width, text_style, markup,
                                     text_range, True, layout)
        if vpos:
            for index in range(*text_range):
                x, y, w, h, base_line, byte_index = layout_data[index]
                dh = (y - base_line) * vpos
                layout_data[index] = (x, y + dh, w, h,
                                      base_line + dh, byte_index)
        core.layout_path(ctx, layout)
        cpath = ctx.copy_path()
        m00 = 1.0
        m11 = -1.0
        if os.name == 'nt':
            m00 *= 0.1
            m11 *= 0.1
        matrix = cairo.Matrix(m00, 0.0, 0.0, m11,
                              layout_data[i][0], layout_data[i][1])
        libcairo.apply_cmatrix(cpath, matrix)
        glyphs.append(cpath)
    return glyphs


def get_rtl_glyphs(ctx, layout_data, log_layout_data, byte_dict, rtl_regs,
                   text, width, text_style, markup):
    glyphs = []
    for item in layout_data:
        try:
            index = byte_dict[item[5]]
            txt = text[index]
            if is_item_in_rtl(index, rtl_regs):
                text_range = [index - len(txt) + 1, index + 1]
            else:
                text_range = [index, index + len(txt)]

        except Exception:
            continue

        if txt in NONPRINTING_CHARS:
            glyphs.append(None)
            continue

        ctx.new_path()
        ctx.move_to(0, 0)
        layout = core.create_layout(ctx)
        vpos = core.set_glyph_layout(txt, width, text_style,
                                     markup, text_range, True, layout)
        if vpos:
            for index in range(*text_range):
                x, y, w, h, base_line, byte_index = log_layout_data[index]
                dh = (y - base_line) * vpos
                log_layout_data[index] = (x, y + dh, w, h,
                                          base_line + dh, byte_index)
        core.layout_path(ctx, layout)
        cpath = ctx.copy_path()
        m00 = 1.0
        m11 = -1.0
        if os.name == 'nt':
            m00 *= 0.1
            m11 *= 0.1
        matrix = cairo.Matrix(m00, 0.0, 0.0, m11, item[0], item[1])
        libcairo.apply_cmatrix(cpath, matrix)
        glyphs.append(cpath)
    return glyphs


def get_text_paths(orig_text, width, text_style, markup):
    if not orig_text:
        orig_text = NONPRINTING_CHARS[0]
        markup = []
    core.set_layout(orig_text, width, text_style, markup)
    w, h = core.get_layout_size()

    surf = cairo.ImageSurface(cairo.FORMAT_RGB24, 100, 100)
    ctx = cairo.Context(surf)
    ctx.set_matrix(libcairo.DIRECT_MATRIX)

    line_points = []
    for item in core.get_line_positions():
        line_points.append([0.0, item])

    text = orig_text
    clusters = []

    # Ligature support
    if text_style[5]:
        data = core.get_cluster_positions(len(orig_text))
        layout_data, clusters, clusters_index, bidi_flag, rtl_flag = data

        if not rtl_flag and not bidi_flag:
            if clusters:
                text = cluster_text(text, clusters)
                if check_maynmar(orig_text):
                    word_group(text)
            log_layout_data = layout_data
            glyphs = get_glyphs(ctx, layout_data, text,
                                width, text_style, markup)
        else:
            byte_dict = utf8_to_ucs4_dict(text)
            clusters = fix_rlt_clusters(clusters_index, byte_dict)
            text = cluster_text(text, clusters)
            byte_dict = utf8_to_ucs4_dict(text)
            rtl_regs = find_rtl_regs(layout_data)
            log_rtl_regs = fix_rlt_regs(rtl_regs, layout_data, byte_dict)
            for item in log_rtl_regs:
                if check_arabic(text[item[0]:item[1]]):
                    rtl_word_group_in_reg(text, item)
            log_layout_data = get_log_layout_data(layout_data, byte_dict,
                                                  rtl_regs)
            glyphs = get_rtl_glyphs(ctx, layout_data, log_layout_data,
                                    byte_dict, log_rtl_regs, text, width,
                                    text_style, markup)

    # Simple char-by-char rendering
    else:
        layout_data = core.get_char_positions(len(orig_text))
        log_layout_data = layout_data
        glyphs = get_glyphs(ctx, layout_data, text,
                            width, text_style, markup)

    layout_bbox = [0.0, layout_data[0][1],
                   float(w), layout_data[0][1] - float(h)]

    return glyphs, line_points, log_layout_data, layout_bbox, clusters
