# AUTOGENERATED! DO NOT EDIT! File to edit: notebooks/00_actions.ipynb (unless otherwise specified).

__all__ = ['update_templates', 'add_badges', 'install_git_filters', 'update_gitignore', 'update_project', 'sch_to_bom',
           'bom_to_sch', 'export_manufacturing', 'export_sch', 'export_pcb', 'run_erc', 'run_drc', 'set_date',
           'set_revision']

# Cell
import os
import sys
import pkg_resources
import subprocess
from pprint import pprint
import datetime as dt

import jinja2
from fastcore.script import *
import pandas as pd
from kifield.kifield import explode

from kicad_helpers import *
from .utilities import _set_root, _run_cmd, _print_cmd_output

# Cell
def update_templates(v:Param("verbose", bool),
                     overwrite:Param("overwrite existing templates", bool),
                     root:Param("project root directory", str)="."):
    """Install templates from the `kicad_helpers/templates` directory (ignoring
    anything in the project's `.gitignore` list).
    """
    templates_path = os.path.abspath(pkg_resources.resource_filename('kicad_helpers', 'templates'))
    root = _set_root(root)
    metadata = get_project_metadata(root)
    file_list = []
    exists_flag = False
    for root_, dirs, files in os.walk(templates_path):
        if len(files):
            for file in files:
                path = os.path.join(root_[len(templates_path) + 1:], file)
                if not in_gitignore(path, root):
                    src_path = os.path.abspath(os.path.join(templates_path, path))
                    dst_path = os.path.abspath(os.path.join(root, path))

                    # Create the `dst_path` directory if it doesn't exist
                    os.makedirs(os.path.split(dst_path)[0], exist_ok=True)

                    if os.path.exists(dst_path):
                        if not overwrite:
                            print(f"{ path } already exists")
                            exists_flag = True
                            continue

                    with open(src_path) as f:
                        template = jinja2.Template(f.read())

                    with open(dst_path, "w") as f:
                        if v:
                            print(f"Render { path } template.")
                        f.write(template.render(**metadata))

    if not overwrite and exists_flag:
        print("To overwrite existing files, use the --overwrite flag.")

# Cell
def _add_lines_to_file(root, file_name, lines, v=False, prepend=False):
    file_path = os.path.join(root, file_name)
    if os.path.exists(file_path):
        with open(file_path, "r") as f:
            file_lines = f.readlines()
    else:
        file_lines = []

    for line in lines:
        if line not in file_lines:
            if v:
                print(f"Append \"{ line.strip() }\" to { file_path }")
            if prepend:
                file_lines.insert(0, line)
            else:
                file_lines.append(line)
        elif v:
            print(f"\"{ line.strip() }\" already exists in { file_path }")

    with open(file_path, "w") as f:
        f.writelines(file_lines)

# Cell
@call_parse
def add_badges(root:Param("project root directory", str)=".",
               v:Param("verbose", bool)=False,
               github:Param("github", bool)=False,
               kitspace:Param("kitspace", bool)=False):
    """Add badges to the README.md file."""
    root = _set_root(root)
    badges = []
    if kitspace:
        badges.append(kitspace_badge(root))
    if github:
        badges.append(github_badge(root))
    _add_lines_to_file(root,
                       "README.md",
                       [line + "\n" for line in badges],
                       v,
                       prepend=True
    )

# Cell
_gitattributes_list = ["*.pro filter=kicad_project",
    "*.sch filter=kicad_sch"
]

def install_git_filters(root=".", v=False):
    """Install git filters to prevent insignificant changes to the kicad
    `*.pro` and `*.sch` files from being tracked by git.

    See: https://jnavila.github.io/plotkicadsch/
    """
    root = _set_root(root)

    # Add filters for kicad project and schematic files
    _add_lines_to_file(root,
                       ".gitattributes",
                       [line + "\n" for line in _gitattributes_list],
                       v
    )
    if v:
        print("Add filters to git config.")

    # Add filters to the project's git config
    _run_cmd(f"cd { root } && git config filter.kicad_project.clean \"sed -E 's/^update=.*$/update=Date/'\"")
    _run_cmd(f"cd { root } && git config filter.kicad_project.smudge cat")

    # This seems to break ERC checking. Disable for now...
    #_run_cmd(f"cd { root } && git config filter.kicad_sch.clean \"sed -E 's/#(PWR|FLG)[0-9]+/#\1?/'\"")
    #_run_cmd(f"cd { root } && git config filter.kicad_sch.smudge cat")

# Cell

_gitignore_list = ["_autosave*",
    "*bak",
    "*.xml",
    ".ipynb_checkpoints",
    "*-erc.txt",
    "*-drc.txt",
    "kibot_errors.filter"
]

def update_gitignore(root=".", v=False):
    """Add the following entries to the `.gitignore` file:
    ```
        _autosave*
        *bak
        *.xml
        .ipynb_checkpoints
        *-erc.txt
        *-drc.txt
        kibot_errors.filter
    ```
    """
    # Add filters for kicad project and schematic files
    _add_lines_to_file(root,
                       ".gitignore",
                       [line + "\n" for line in _gitignore_list],
                       v
    )

# Cell
@call_parse
def update_project(v:Param("verbose", bool),
                   overwrite:Param("overwrite existing templates", bool),
                   root:Param("project root directory", str)="."):
    """Setup a new project (or update an existing project) with templates from
    the `kicad_helpers/templates` directory.
    Also installs git filters to prevent insignificant changes to the kicad
    `*.pro` and `*.sch` files from being tracked by git (see
    https://jnavila.github.io/plotkicadsch/ for more details).
    """
    update_templates(v=v, overwrite=overwrite, root=root)
    install_git_filters(root=root, v=v)
    update_gitignore(root=root, v=v)

# Cell
@call_parse
def sch_to_bom(root:Param("project root directory", str)=".",
               v:Param("verbose", bool)=False,
               overwrite:Param("update existing schematic", bool)=False):
    """Update/create BOM from KiCad schematic.
    """
    root = _set_root(root)
    cmd = f"{ sys.executable } -m kifield -r --nobackup --overwrite --group -x { get_schematic_path(root) } -i { get_bom_path(root) }"
    if v:
        print(cmd)
    _print_cmd_output(cmd)

    # Open the BOM
    df = pd.read_csv(get_bom_path(root))

    # Calculate the quantity of each part
    df["Quantity"] = [len(explode(refs)) for refs in df["Refs"]]

    # Make quantity the second column
    if df.columns[-1] == "Quantity":
        df = df[[df.columns[0]] + ["Quantity"] + list(df.columns[1:-1])]

    # Sort alphabetically by Ref
    df.sort_values(by="Refs", inplace=True)
    df.to_csv(get_bom_path(root), index=False)

# Cell
@call_parse
def bom_to_sch(root:Param("project root directory", str)=".",
               v:Param("verbose", bool)=False,
               overwrite:Param("update existing schematic", bool)=False):
    """Update KiCad schematic from BOM file.
    """
    root = _set_root(root)
    cmd = f"{ sys.executable } -m kifield -r --nobackup --overwrite --fields ~Quantity -x { get_bom_path(root) } -i { get_schematic_path(root) }"
    if v:
        print(cmd)
    _print_cmd_output(cmd)

# Cell
@call_parse
def export_manufacturing(root:Param("project root directory", str)=".",
                         manufacturer:Param(f"\"default\" or manufacturer name", str)="default",
                         v:Param("verbose", bool)=False,
                         output:Param("output path relative to ROOT")="."):
    """Export manufacturing files (gerber, drill, and position) by running
    KiBot in a local docker container.
    """
    root = _set_root(root)
    if manufacturer not in get_manufacturers(root):
        raise RuntimeError(f"MANUFACTURER must be one of the following: { ', '.join(get_manufacturers(root)) }.")

    config = f".kicad_helpers_config/manufacturers/{ manufacturer }.yaml"
    run_kibot_docker(config=config, root=root, v=v, output=output)

# Cell
@call_parse
def export_sch(root:Param("project root directory", str)=".",
               ext:Param(f"svg or pdf", str)="pdf",
               v:Param("verbose", bool)=False,
               output:Param("output path relative to ROOT")="."):
    """Export the schematic by running KiBot in a local docker container.
    """
    root = _set_root(root)
    supported_types = ["svg", "pdf"]

    if ext not in supported_types:
        raise RuntimeError(f"EXT must be one of: { ','.join(supported_types) }.")

    config = f".kicad_helpers_config/sch_{ ext }.yaml"
    run_kibot_docker(config=config, root=root, v=v, output=output)

# Cell
@call_parse
def export_pcb(root:Param("project root directory", str)=".",
               ext:Param(f"svg or pdf", str)="pdf",
               v:Param("verbose", bool)=False,
               output:Param("output path relative to ROOT")="."):
    """Export the pcb layout by running KiBot in a local docker container.
    """
    root = _set_root(root)
    supported_types = ["svg", "pdf"]

    if ext not in supported_types:
        raise RuntimeError(f"EXT must be one of: { ','.join(supported_types) }.")

    config = f".kicad_helpers_config/pcb_{ ext }.yaml"
    run_kibot_docker(config=config, root=root, v=v, output=output)

# Cell
@call_parse
def run_erc(root:Param("project root directory", str)=".",
            v:Param("verbose", bool)=False):
    """Run electrical rules check (ERC) to verify schematic connections. It
    checks for output pin conflicts, missing drivers and unconnected pins.
    Print the report to `stdout`.
    """
    root = _set_root(root)
    cmd = f"eeschema_do run_erc { get_schematic_path(root)[len(root) + 1:] } ."
    try:
        output = run_docker_cmd(cmd,
           workdir=os.path.abspath(root),
           container="setsoft/kicad_auto_test:latest",
           v=v
        )
    except subprocess.CalledProcessError as e:
        output = e.output.decode("utf-8")
    if v:
        print(output)
    erc_path = os.path.join(root, get_project_name(root) + ".erc")
    with open(erc_path, "r") as f:
        erc = f.read()
    os.remove(erc_path)
    print(erc)

# Cell
@call_parse
def run_drc(root:Param("project root directory", str)=".",
            v:Param("verbose", bool)=False):
    """Run design rules check (DRC) and print the report to `stdout`.
    """
    root = _set_root(root)
    cmd = f"pcbnew_do run_drc { get_board_path(root)[len(root) + 1:] } ."
    try:
        output = run_docker_cmd(cmd,
            workdir=os.path.abspath(root),
            container="setsoft/kicad_auto_test:latest",
            v=v
        )
    except subprocess.CalledProcessError as e:
        output = e.output.decode("utf-8")
    if v:
        print(output)
    drc_path = os.path.join(root, "drc_result.rpt")
    with open(drc_path, "r") as f:
        drc = f.read()
    os.remove(drc_path)
    print(drc)

# Cell
@call_parse
def set_date(date:Param("date (defaults to today's date)", str)=None,
             root:Param("project root directory", str)=".",
             v:Param("verbose", bool)=False):
    """Set the date in all schematic and board files.
    """
    root = _set_root(root)
    if date is None:
        date = dt.datetime.now().date().isoformat()
    update_schematic_metadata({"Date": date},
                              root=root, all_sheets=True)
    update_board_metadata({"date": date}, root=root)

# Cell
@call_parse
def set_revision(revision:Param("revision", str),
                 root:Param("project root directory", str)=".",
                 v:Param("verbose", bool)=False):
    """Set the revision in all schematic and board files.
    """
    root = _set_root(root)
    update_schematic_metadata({"Rev": revision},
                              root=root, all_sheets=True)
    update_board_metadata({"rev": revision}, root=root)