#!/usr/bin/env python

"""
Copyright 2017 The Fuchsia Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.

This modules provides an interface to build minfs images from fuchsia
manifest files.

"""

import os
import stat
import subprocess
import sys

def _get_file_len(path):
    """Returns the length of an input file in bytes. """
    fd = os.open(path, os.O_RDONLY)
    try:
        return os.lseek(fd, 0, os.SEEK_END)
    finally:
        os.close(fd)


def build_minfs_image(manifest_path, minfs_image_path, minfs_bin):
    """Populate a minfs container with the contents specified by the fuchsia
       manifest file.
    """

    # Determine the length of the minfs image. Note that this works on
    # regular files as well as block devices so it should allow us to
    # use the minfs tool to manipulate block devices.
    minfs_image_len = _get_file_len(minfs_image_path)
    disk_path = "%s@%d" % (minfs_image_path, minfs_image_len)

    minfs_cmd = [minfs_bin, disk_path, "cp", None, None]

    # parse the manifest file and find the files to copy
    file_count = 0
    created_dirs = set()
    with open(manifest_path, "r") as manifest_file:
        for line in manifest_file:
            if "=" not in line:
                continue

            if line.count("=") != 1:
                raise Exception("Unexpected format, too many equal signs: %d" %
                                line.count("="))
            parts = line.split("=")
            parts[0] = parts[0].strip()
            parts[1] = parts[1].strip()

            # first figure out if we need to make a directory for this file
            # This should split file name off the past
            dir_path = os.path.split(parts[0])[0]

            # track the directories we need to make for this file
            dirs_to_make = []

            # see if the directory for this file has been created
            while dir_path != "" and dir_path not in created_dirs:
                dir_path, new_dir = os.path.split(dir_path)
                dirs_to_make.append(new_dir)

            # now create the directory path
            while len(dirs_to_make) > 0:
                dir_path = os.path.join(dir_path, dirs_to_make.pop())

                mkdir_cmd = [minfs_bin, disk_path, "mkdir", "::%s" % dir_path]
                try:
                    subprocess.check_call(mkdir_cmd)
                except (subprocess.CalledProcessError):
                    print "Error creating directory '%s'" % dir_path
                    sys.exit(-1)
                except (OSError):
                    print "Unable to execute minfs"
                    sys.exit(-1)

                # record that we've created this directory
                created_dirs.add(dir_path)

            targ_path = "::%s" % parts[0]
            minfs_cmd[3] = parts[1]
            minfs_cmd[4] = targ_path

            try:
                subprocess.check_call(minfs_cmd)
            except (subprocess.CalledProcessError):
                print "Error copying file %s" % parts[1]
                sys.exit(-1)
            except (OSError):
                print "Unable to execute minfs"
                sys.exit(-1)

            file_count += 1
            # some status output
            sys.stdout.write("%s\r" % (" " * 100))
            sys.stdout.write("Copying '%s' \r" % parts[0])
            sys.stdout.flush()

    return file_count
