#!/usr/bin/python3
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
#
# Copyright (C) 2010-2012 Bryce Harrington <bryce@canonical.com>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from __future__ import absolute_import, print_function, unicode_literals

import re
import os
import sys
import shutil
import fnmatch

# Add project root directory (enable symlink, and trunk execution).
PROJECT_ROOT_DIRECTORY = os.path.abspath(
    os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))))

if (os.path.exists(os.path.join(PROJECT_ROOT_DIRECTORY, 'xdiagnose'))
    and PROJECT_ROOT_DIRECTORY not in sys.path):
    sys.path.insert(0, PROJECT_ROOT_DIRECTORY)
    os.putenv('PYTHONPATH', PROJECT_ROOT_DIRECTORY) # for subprocesses

from xdiagnose.edid import Edid

## NVIDIA ##
#
# In Precise, -nvidia does not support writable EDID via KMS.
# However, it does provide a CustomEDID option in xorg.conf which
# servces essentially the same purpose.  To use it, you need
# a section like the following in your xorg.conf.  (I believe the
# nvidia-config GUI tool has buttons to add this for you.)
#
#Section "Device"
#    Identifier    "Device0"
#    Driver        "nvidia" #Choose the driver used for this monitor
#    Option "UseEDID" "FALSE"
#    Option "CustomEDID" "DFP-1:/etc/X11/xorg.conf.d/edid.bin"
#EndSection

# TODO: Need a corresponding edid read functionality.  (Or can we
#  just use the get-edid script from the read-edid package?)

firmware_path = "/lib/firmware/edid"

def usage():
    print("Usage: xedid <command> [edid-sources]")

def locate(pattern, root=os.curdir):
    '''Locate all files matching supplied filename pattern in and below
    supplied root directory.'''
    for path, dirs, files in os.walk(os.path.abspath(root)):
        for filename in fnmatch.filter(files, pattern):
            yield os.path.join(path, filename)

if __name__ == "__main__":
    from xdiagnose import info
    from xdiagnose.utils.option_handler import OptionHandler

    opt_hand = OptionHandler(info)
    opt_hand.add('-v', '--verbose', dest="verbose",
                 help="Show debug messages",
                 action="store_true", default=False,
                 desc="Turns on verbose debugging output.")
    options, args = opt_hand.parse_args()

    if len(args) > 0:
        command      = args[0]
    else:
        command = "show"

    source_files = args[1:]
    if len(source_files) == 0:
        # Xorg.0.log
        source_files = [ "/var/log/Xorg.0.log" ]

        # Kernel edid nodes
        re_lvds = re.compile(".*LVDS.*")
        # Identify what edid file sysfs nodes are present
        for node in locate('edid', root="/sys"):
            if re_lvds.match(node):
                continue
            source_files.append(node)

    sources      = []
    for filename in source_files:
        edid = Edid()
        if edid.load(filename):
            sources.append(edid)

    if command == "list":
        for edid in sources:
            print(edid.name)
    elif command == "show":
        for edid in sources:
            print(edid)
    elif command == "raw":
        for edid in sources:
            for i in range(0, int(len(edid.raw)/32)):
                j = i + 1
                print(edid.raw[i*32:j*32])
            print()
    else:
        print("Unknown command '%s'" %(command))
        usage()
        sys.exit(1)


    sys.exit(0)

    # Test that everything is sane
    if not os.path.isfile(filename):
        print("%s not found" %(filename))
        sys.exit(2)
    elif os.getenv("USER") != "root":
        print("Error:  Must run as root")
        sys.exit(3)
    elif len(edid_nodes) == 0:
        print("No external monitor edid node found.")
        sys.exit(4)
    elif len(edid_nodes) > 1:
        print("Multiple possible external monitor edid nodes found.\n")
        for node in edid_nodes:
            print(node)
        print("\nPlease rerun script specifying one ")
        print("of the above paths as the 'node-path' arg.")
        sys.exit(5)
    elif not os.access(edid_nodes[0], os.W_OK):
        print("%s is not writable" %(edid_nodes[0]))
        print("Writable edid is available only on Precise and newer,")
        print("and may not be available yet for all video drivers.")
        sys.exit(6)

    # Make sure the firmware path exists, creating it if needed
    try:
        os.makedirs(firmware_path)
    except OSError as exc:
        if exc.errno == errno.EEXIST:
            pass
        else:
            print("Could not mkdir %s" %(firmware_path))
            sys.exit(7)

    # Install the EDID
    edid_firmware = os.path.basename(filename)
    try:
        target = os.path.join(firmware_path, edid_firmware)
        shutil.copyfile(filename, target)
    except:
        print("Could not install firmware")
        sys.exit(8)

    # Activate new EDID
    try:
        f = open(edid_nodes[0], "w")
        f.write("edid/%s" %(edid_firmware))
        f.close()
    except:
        print("Could not activate new firmware in kernel")
        sys.exit(9)

# TODO: Need to find edid_firmware
#  Check that this echos the right string in

#  /sys/devices/pci0000:00/0000:00:02.0/drm/card0/card0-VGA-1/edid
# reboot

#The name is passed to the kernel as module parameter of the
#drm_kms_helper module either when loaded
#  options drm_kms_helper edid_firmware=edid/1280x1024.bin
#or as kernel commandline parameter
#  drm_kms_helper.edid_firmware=edid/1280x1024.bin
