#!/usr/bin/env python
"""
Arduino-mk Makefile and project initialiser

This script can be used in its basic form create a project specific Makefile
for use with Arduino-mk. Addionally, it can be used to create a template
Arduino source file and a traditional boilerplate project file structure.

Example:
    * Run prompted within current working directory (requires Clint): `ardmk-init --cli`
    * Create Arduino Uno Makefile (useful within a library example): `ardmk-init -b uno`
    * Create boilerplate Arduino Uno project in current working directory of same
      name: `ardmk-init -b uno --project`
    * Create Arduino-mk nano Makefile in current working directory with template .ino:
    `ardmk-init -b nano -u atmega328 -tn my-project`

See `armk-init --help` for CLI arguments
"""

from __future__ import print_function
import os
import argparse

## Global Vars
VERSION = "1.2"
ARD_TEMPLATE = "\n\
#include <Arduino.h>\n\
#include <Wire.h>\n\
\n\
\n\
void setup() {\n\
}\n\
\n\
void loop() {\n\
}\n\
"

## Command Parser
PARSER = argparse.ArgumentParser(prog='ardmk-init',
                                 description='Arduino Makefile and boilerplate project generator.\
        For use with Ard-Makefile: https://github.com/sudar/Arduino-Makefile.\
        Script created by John Whittington https://github.com/tuna-f1sh 2017\
        \n\nVersion: ' + VERSION)
PARSER.add_argument('-v', '--verbose', action='store_true',
                    help="print file contents during creation")
PARSER.add_argument('-d', '--directory', default=os.getcwd(), help='directory to run generator, default cwd')
PARSER.add_argument('-b', '--board', default='uno', help='board tag')
PARSER.add_argument('-u', '--micro', default='AUTO', help='microcontroller on board')
PARSER.add_argument('-f', '--freq', default='AUTO', help='clock frequency')
PARSER.add_argument('-p', '--port', default='AUTO', help='monitor port')
PARSER.add_argument('-n', '--name', default=os.path.basename(os.getcwd()), help='project name')
PARSER.add_argument('-s', '--sam', action='store_true', help='sam device, will include Sam.mk rather than Arduino.mk')
PARSER.add_argument('--cli', action='store_true', help='run with user prompts (requires "Clint" module), rather than args')
PARSER.add_argument('-P', '--project', action='store_true',
                    help='create boilerplate project with src, lib and bin folder structure')
PARSER.add_argument('-t', '--template', action='store_true',
                    help='create bare minimum Arduino source file') 
PARSER.add_argument('-V', '--version', action='version', version='%(prog)s '+ VERSION)
ARGS = PARSER.parse_args()

try:
    from clint.textui import prompt, validators
except ImportError:
    if ARGS.cli:
        print("Python module 'clint' is required for running prompted. Install the module or run with arguments only")
        quit()


def generate_makefile():
    """
    Generate the Makefile content using prompts or parsed arguments
    """
    # Header
    file_content = "# Generated by ard-make version " + VERSION + "\n\n"

    # Basic
    if ARGS.cli:
        print("Generating Arduino Ard-Makefile project in "
                          + os.path.abspath(ARGS.directory))
        btag = prompt.query('Board tag?', default='uno')
        if btag != 'uno':
            bsub = prompt.query('Board sub micro?', default='atmega328')
            f_cpu = prompt.query('Board frequency', default='16000000L')
        else:
            bsub = 'AUTO'
            f_cpu = 'AUTO'
        monitor_port = prompt.query('Arduino port?', default='AUTO')
    else:
        btag = ARGS.board
        bsub = ARGS.micro
        f_cpu = ARGS.freq
        monitor_port = ARGS.port

    file_content += check_define('BOARD_TAG', btag)
    file_content += check_define('BOARD_SUB', bsub)
    file_content += check_define('F_CPU', f_cpu)
    file_content += check_define('MONITOR_PORT', monitor_port)

    # Extended
    if ARGS.cli:
        if not prompt.yn('Extended options?', default='n'):
            if not prompt.yn('Define local folders?', default='n'):
                src_dir = prompt.query('Sources folder (Makefile will be created here)?',
                                       default='', validators=[])
                userlibs = prompt.query('Library folder (will create if does not exist) - AUTO is Sketchbook directory?',
                                        default='AUTO', validators=[])
                obj_dir = prompt.query('Output directory?', default='AUTO', validators=[])
            else:
                src_dir = ''
                userlibs = 'AUTO'
                obj_dir = 'AUTO'
            boards_txt = prompt.query('Boards file?', default='AUTO')
            isp_prog = prompt.query('ISP programmer?', default='atmelice_isp')
            isp_port = prompt.query('ISP port?', default='AUTO')
            if not prompt.yn('Quiet make?', default='n'):
                file_content += "ARDUINO_QUIET = 1\n"

            file_content += check_define('ISP_PROG', isp_prog)
            file_content += check_define('ISP_PORT', isp_port)
            file_content += check_define('BOARDS_TXT', boards_txt)

            # Check andd create folders
            check_create_folder(src_dir)
            check_create_folder(userlibs)
            check_create_folder(obj_dir)

            # Makefile will be in src_dir so lib and bin must be relative
            if src_dir:
                userlibs = "../" + userlibs
                obj_dir = "../" + obj_dir

            file_content += check_define('USER_LIB_PATH', userlibs)
            file_content += check_define('OBJDIR', obj_dir)
        else:
            src_dir = ''

        if ARGS.template or not prompt.yn('Create template Arduino source?', default='n'):
            source_filename = prompt.query('Name of project?',
                                           default=os.path.basename(os.getcwd()))
            if src_dir:
                write_template(src_dir + "/" + source_filename)
            else:
                write_template(source_filename)
            file_content += check_define('TARGET', source_filename)

    else:
        if ARGS.project:
            src_dir = 'src'
            userlibs = 'lib'
            obj_dir = 'bin'
        else:
            src_dir = ''
            userlibs = 'AUTO'
            obj_dir = 'AUTO'

        # Check andd create folders
        check_create_folder(src_dir)
        check_create_folder(userlibs)
        check_create_folder(obj_dir)

        # Makefile will be in src_dir so lib and bin must be relative
        if src_dir:
            userlibs = "../" + userlibs
            obj_dir = "../" + obj_dir

        file_content += check_define('USER_LIB_PATH', userlibs)
        file_content += check_define('OBJDIR', obj_dir)

        if ARGS.project or ARGS.template:
            if src_dir:
                write_template(src_dir + "/" + ARGS.name)
            else:
                write_template(ARGS.name)
            file_content += check_define('TARGET', ARGS.name)

    if not "ARDMK_DIR" in os.environ:
        if not ARGS.cli:
            print("Warning: ARDMK_DIR environment variable not defined. \
                        Must be defined for Makefile to work")
        else:
            ardmk = prompt.query('Arduino Makefile path?',
                                 default='/usr/share/arduino',
                                 validators=[validators.PathValidator()])
            ardmk = "ARDMK_DIR := " + ardmk + "\n"

    if ARGS.sam:
        file_content += "\ninclude $(ARDMK_DIR)/Sam.mk"
    else:
        file_content += "\ninclude $(ARDMK_DIR)/Arduino.mk"

    # Add forward slash if source directory exists
    if src_dir:
        write_to_makefile(file_content, (src_dir + "/"))
    else:
        write_to_makefile(file_content, "")

    return file_content

def write_to_makefile(file_content, path):
    """
    Write the Makefile file
    """
    makefile = open(path + "Makefile", 'w')
    print("Writing Makefile...")
    if ARGS.verbose:
        print(file_content)
    makefile.write(file_content)
    makefile.close()

def write_template(filename):
    """
    Write template Arduino .ino source
    """
    print("Writing " + os.path.abspath(filename) + ".ino...")
    if os.path.isfile(filename + '.ino'):
        if not ARGS.cli:
            print(filename + '.ino' + ' already exists! Stopping.')
            return
        print(filename + '.ino' + ' already exists! Overwrite?')
        if prompt.yn('Continue?', default='n'):
            return
    src = open((filename + ".ino"), 'w')
    if ARGS.verbose:
        print(ARD_TEMPLATE)
    src.write("/* Project: " + filename + " */\n" + ARD_TEMPLATE)
    src.close()

def check_create_folder(folder):
    """
    Check if folder exists and make it if it doesn't and hasn't been set to AUTO
    """
    if folder and not folder == 'AUTO':
        if not os.path.exists(folder):
            print("Creating " + os.path.abspath(folder) + " folder")
            os.makedirs(folder)

def check_define(define, user):
    """
    Check whether user has set define and return Makefile formatted string if they have
    """
    # Return is empty unless user has passed value
    string = ""

    # Set define only if not empty or set to AUTO
    if user and not user == 'AUTO':
        string = define + " = " + user + "\n"

    return string

def check_args():
    """
    Check input args will work with Makefile
    """
    # Micro should be defined for non uno boards
    if ARGS.board != 'uno' and ARGS.micro == 'AUTO':
        print('\n!!! Warning: --micro should be defined and not left AUTO for non-Uno boards\n')


if __name__ == '__main__':
    # Create directory if not exist
    check_create_folder(ARGS.directory)
    # Check input args
    check_args()
    # Change to dir so all commands are run relative
    os.chdir(ARGS.directory)
    if os.path.isfile('Makefile'):
        if not ARGS.cli:
            print('Makefile in ' + os.path.abspath(ARGS.directory)
                             + ' already exists! Please remove before generating. Stopping.')
            quit()

        # Confirm with user if not quiet mode
        print('Makefile in ' + os.path.abspath(ARGS.directory)
                         + ' already exists! Overwrite?')
        if prompt.yn('Continue?', default='n'):
            quit()
    # Run it
    generate_makefile()