from __future__ import print_function

import os
import sys

HERE = os.path.dirname(__file__)
PLAY_MAP = {}

def load_play_map():
    m = open(os.path.join(HERE, '../mapping.txt'))

    for line in m:
        fc_play, os_play = line.split('\t', 1)
        PLAY_MAP[fc_play.strip()] = os_play.strip()

def xptr_fc_to_offset_fc(fc_play, xpath, offset):
    """

    Convert a FinalsClub XPointer into a FinalsClub global offset using the
    files generated by `bin/xpath_offsets`.

    """
    offset_fn = os.path.join(HERE, '../out/fc', fc_play + '.offset.txt')

    with open(offset_fn) as fp:
        # walk through the lines in the file, and find the first XPath that
        # starts with the given XPath. FinalsClub XPaths are of the form
        # "/span[42]" even if the DOM has additional structure (e.g.
        # "/span[42]/p[1]/blockquote[1]"...

        possible = None

        for line in fp:
            l_offset, l_xpath = line.strip().split(' ', 1)
            l_offset = int(l_offset)

            if possible is None:
                if l_xpath.startswith(xpath):
                    possible = l_offset
                continue

            # If we can walk past another whole text node, then do so,
            # "adding" one to the input offset but subtracting one less than
            # the length of the next text node.
            offset_subtract = l_offset - possible - 1

            if offset - offset_subtract > 0:
                possible = l_offset
                offset -= offset_subtract
            else:
                return possible + offset

        return possible + offset

def offset_fc_to_offset_os(os_play, offset_fc):
    """

    Convert a FinalsClub global offset into an OpenShakespeare global offset
    using the files generated by `bin/offset_mapping`.

    """
    mapping_fn = os.path.join(HERE, '../out', os_play + '.mapping.txt')

    last_offset_fc = 0
    last_offset_os = 0

    with open(mapping_fn) as fp:
        for line in fp:
            l_offset_fc, l_offset_os = map(int, line.strip().split(' ', 1))

            if l_offset_fc > offset_fc:
                return last_offset_os + (offset_fc - last_offset_fc)

            last_offset_fc = l_offset_fc
            last_offset_os = l_offset_os

        # fell through: return last offset
        return last_offset_os + (offset_fc - last_offset_fc)

def offset_os_to_xptr_os(os_play, offset_os):
    """

    Convert an OpenShakespeare global offset into an OpenShakespeare XPointer
    files generated by `bin/xpath_offsets`.

    This is made substantially more complicated because of the add-a-space
    procedure, necessary to avoid conjoined words in the plain text rendition
    of the play.

    """
    offset_fn = os.path.join(HERE, '../out/os', os_play + '.offset.txt')

    last_offset = 0
    last_xpath = None

    lines = [line for line in open(offset_fn)]

    for line in lines:
        l_offset, l_xpath = line.strip().split(' ', 1)
        l_offset = int(l_offset)

        if l_offset > offset_os:
            # This is horrible, and makes this O(N^2), but I can't see an easy
            # way around it.
            return (last_xpath, offset_os - find_first_match_offset(lines, last_xpath, last_offset))

        # store previous line
        last_xpath, last_offset = l_xpath, l_offset

    # fell through: return last xpath/offset
    return (last_xpath, offset_os - find_first_match_offset(lines, last_xpath, last_offset))


def find_first_match_offset(lines, xpath, offset):
    start_offset = None
    correction = 0

    for line in lines:
        l_offset, l_xpath = line.strip().split(' ', 1)
        l_offset = int(l_offset)

        if start_offset is None:
            if l_xpath == xpath:
                start_offset = l_offset
        else:
            correction += 1

        if l_xpath == xpath and l_offset == offset:
            return start_offset + correction

def xptr_fc_to_offset_os(fc_play, xpath, offset):
    os_play = PLAY_MAP[fc_play]
    offset_fc = xptr_fc_to_offset_fc(fc_play, xpath, offset)
    offset_os = offset_fc_to_offset_os(os_play, offset_fc)
    return offset_os

def xptr_fc_to_xptr_os(fc_play, xpath, offset):
    os_play = PLAY_MAP[fc_play]
    offset_fc = xptr_fc_to_offset_fc(fc_play, xpath, offset)
    offset_os = offset_fc_to_offset_os(os_play, offset_fc)
    xptr_os = offset_os_to_xptr_os(os_play, offset_os)
    return xptr_os
