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

# ====================================================
# Copyright 2017 Christopher Simpkins
# MIT License
# ====================================================

import os
import sys

from commandlines import Command
from fontTools import ttLib
from git import Repo

from fontv import settings
from fontv.utilities import file_exists, dir_exists


def main():
    c = Command()

    if c.does_not_validate_missing_args():
        sys.stderr.write("[font-v] ERROR: Please include the appropriate arguments with your command." + os.linesep)
        sys.exit(1)

    if c.is_help_request():
        print(settings.HELP)
        sys.exit(0)
    elif c.is_version_request():
        print(settings.VERSION)
        sys.exit(0)
    elif c.is_usage_request():
        print(settings.USAGE)
        sys.exit(0)

    if c.subcmd == "report":
        for arg in c.argv[1:]:
            if arg[-4:].lower() == ".ttf" or arg[-4:].lower() == ".otf":
                font_path = arg
                if file_exists(font_path):
                    tt = ttLib.TTFont(font_path)
                    namerecord_list = tt['name'].names
                    for record in namerecord_list:
                        if record.nameID == 5:
                            fobj = FontVersionObj(font_path, record)
                            if "--dev" in c.argv:   # --dev flag prints every version string in name records
                                print(fobj.get_dev_print_string())
                            else:
                                print(fobj.get_print_string())
                                break  # print a single version string if there are multiple name records by default
                else:
                    print("[font-v] ERROR: " + font_path + " does not appear to be a valid ttf or otf font file path")
                    sys.exit(1)
    elif c.subcmd == "write":
        # argument parsing flags
        add_sha1 = False
        add_release_string = False
        add_dev_string = False
        add_new_version = False
        fontpath_list = []            # list of font paths that user submits on command line
        version = ""

        # test for mutually exclusive arguments
        # do not refactor this below the level of the argument tests that follow
        if "--rel" in c.argv and "--dev" in c.argv:
            sys.stderr.write("[font-v] ERROR: Please use either --dev or --rel argument, not both" + os.linesep)
            sys.exit(1)

        # test arguments
        for arg in c.argv[1:]:
            if arg == "--sha1":
                add_sha1 = True
            elif arg == "--rel":
                add_release_string = True
            elif arg == "--dev":
                add_dev_string = True
            elif arg[0:6] == "--ver=":
                add_new_version = True
                version_list = arg.split("=")  # split on the = symbol and use second part as definition
                if len(version_list) < 2:
                    sys.stderr.write("[font-v] ERROR: --arg=version argument is not properly specified")
                    sys.exit(1)
                version_pre = version_list[1]
                version_pre = version_pre.replace("-", ".")   # specified on command line as 1-000
                version_final = version_pre.replace("_", ".")   # or as 1_000
                version = "Version " + version_final
            elif len(arg) > 4 and (arg[-4:].lower() == ".ttf" or arg[-4:].lower() == ".otf"):
                if file_exists(arg):
                    fontpath_list.append(arg)
                else:
                    sys.stderr.write("[font-v] ERROR: " + arg + " does not appear to be a valid font file path." + os.linesep)
                    sys.exit(1)

        if add_sha1 is False and add_release_string is False and add_dev_string is False and add_new_version is False:
            print("[font-v]  No changes specified.  Nothing to do.")
            sys.exit(0)

        for fontpath in fontpath_list:
            tt = ttLib.TTFont(fontpath)
            namerecord_list = tt['name'].names
            for record in namerecord_list:
                if record.nameID == 5:
                    fvo = FontVersionObj(fontpath, record)

                    if add_new_version is True:
                        version_string = version
                    else:
                        version_string = fvo.version_parts_list[0]

                    if add_sha1 is True:
                        sha_string = "; " + fvo.get_repo_commit()
                        version_string += sha_string

                    if add_release_string is True:
                        if add_sha1 is True:
                            version_string += "-release"
                        else:
                            version_string += "; RELEASE"
                    elif add_dev_string is True:
                        if add_sha1 is True:
                            version_string += "-dev"
                        else:
                            version_string += "; DEV"

                    post_string = fvo.get_post_string()
                    if len(post_string) > 0:
                        post_string_add = ";" + post_string
                        version_string += post_string_add

                    if record.platformID == 0:
                        record.string = version_string.encode('utf_16_be')  # Unicode platform ID gets UTF16
                    elif record.platformID == 1:
                        record.string = version_string                      # Mac platform ID
                    elif record.platformID == 3:
                        record.string = version_string.encode('utf_16_be')  # Windows platform ID gets UTF16
            # write out the modifications to the font name table
            tt.save(fontpath)
            print(fontpath + " version string successfully updated.")


class FontVersionObj(object):
    def __init__(self, font_path, name_id5_record):
        self.fontpath = font_path
        self.platformID = name_id5_record.platformID
        self.platEncID = name_id5_record.platEncID
        self.langID = name_id5_record.langID
        self.nameID = name_id5_record.nameID
        self.version_string = name_id5_record.string
        self.version_parts_list = []

        self._make_version_parts_list()

    def _make_version_parts_list(self):
        if ";" in self.version_string:
            self.version_parts_list = self.version_string.split(";")
        else:
            self.version_parts_list = [self.version_string]

    def get_repo_commit(self):
        repo = Repo(get_git_root_path())
        gitpy = repo.git
        full_sha_string = gitpy.rev_parse("HEAD")
        return full_sha_string[0:7]  # return first 7 characters of the sha1 string (=short sha1 string definition)

    def get_print_string(self):
        return self.fontpath + ":" + os.linesep + self.version_string

    def get_dev_print_string(self):
        recordtype_list = [str(self.platformID), str(self.platEncID), str(self.langID), str(self.nameID)]
        recordtype_string = "/".join(recordtype_list)
        print_string = self.fontpath + " [" + recordtype_string + "]:" + os.linesep + self.version_string
        return print_string

    def get_version_string(self):
        return self.version_parts_list[0]   # anything before first ';' in version string name record

    def get_post_string(self):
        if len(self.version_parts_list) > 2:
            return ";".join(self.version_parts_list[1:])  # return ; delimited list if there were multiple parts
        elif len(self.version_parts_list) == 2:
            return self.version_parts_list[1]            # return just the post string if only one post part
        else:
            return ""                                   # return empty list if there was no string after version string


def get_git_root_path():
    """
    Recursively searches for git root path over 4 directory levels above working directory
    :return: validated git root path as string OR raises SystemExit if not found
    """
    try:
        # begin by defining current working directory as root of git repository
        unverified_gitroot_path = os.path.abspath('.')

        # check to see if this assumption is correct
        if dir_exists(os.path.join(unverified_gitroot_path, '.git')):
            verified_gitroot_path = os.path.join(unverified_gitroot_path, '.git')
        else:  # if not, recursive search up to three directories above for the git repo root
            one_level_up = os.path.abspath(os.path.join(unverified_gitroot_path, os.pardir))
            two_levels_up = os.path.dirname(one_level_up)
            three_levels_up = os.path.dirname(two_levels_up)

            one_level_up_path = os.path.join(one_level_up, '.git')
            two_levels_up_path = os.path.join(two_levels_up, '.git')
            three_levels_up_path = os.path.join(three_levels_up, '.git')

            if dir_exists(one_level_up_path):  # check one directory level up
                verified_gitroot_path = os.path.dirname(one_level_up_path)
            elif dir_exists(two_levels_up_path):  # check two directory levels up
                verified_gitroot_path = os.path.dirname(two_levels_up_path)
            elif dir_exists(three_levels_up_path):  # check three directory levels up
                verified_gitroot_path = os.path.dirname(three_levels_up_path)
            else:
                sys.stderr.write("[font-v] ERROR: Unable to identify the root of your git repository."
                                 " Please try again from the root of your repository" + os.linesep)
                sys.exit(1)
    except Exception as e:
        sys.stderr.write("[font-v] ERROR: Unable to identify the root of your git repository. Please try again from "
                         "the root of your repository. " + str(e) + os.linesep)
        sys.exit(1)

    return verified_gitroot_path


if __name__ == '__main__':
    main()
