# coding: utf-8
#
# Copyright 2010 Alexandre Fiori
# based on the original Tornado by Facebook
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

"""
Command-line tool for creating cyclone applications out of the box. ::

    usage: cyclone app [options]
    Options:
     -h --help              Show this help.
     -n --new               Dumps a sample server code to stdout.
     -p --project=NAME      Create new cyclone project.
     -g --git               Use in conjunction with -p to make it a git \
repository.
     -m --modname=NAME      Use another name for the module \
[default: project_name]
     -v --version=VERSION   Set project version [default: 0.1]
     -s --set-pkg-version   Set version on package name [default: False]
     -t --target=PATH       Set path where project is created \
[default: current directory]
     -l --license=FILE      Append the following license file \
[default: Apache 2]
     -a --appskel=SKEL      Set the application skeleton [default: default]

    SKEL:
      default              Basic cyclone project
      signup               Basic sign up/in/out, password reset, etc
      foreman              Create a foreman based project \
(suited to run on heroku and other PaaS)

    Examples:
     For a simple hello world:
     $ cyclone app -n > hello.py

     For a project that requires sign up:
     $ cyclone app --project=foobar --appskel=signup
"""

from __future__ import with_statement
import base64
import getopt
import os
import re
import string
import sys
import uuid
import zipfile
from datetime import datetime

DEFAULT_LICENSE = """\
# Copyright %(year)s Foo Bar
# Powered by cyclone
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""

SAMPLE_SERVER = """\
#
# Start the server:
#   cyclone run server.py

import cyclone.web


class MainHandler(cyclone.web.RequestHandler):
    def get(self):
        self.write("Hello, world")


class Application(cyclone.web.Application):
    def __init__(self):
        handlers = [
            (r"/", MainHandler),
        ]

        settings = dict(
            xheaders=False,
            static_path="./static",
            templates_path="./templates",
        )

        cyclone.web.Application.__init__(self, handlers, **settings)\
"""


def new_project(**kwargs):
    zf = kwargs["skel"]
    dst = kwargs["project_path"]

    os.mkdir(dst, 0755)
    for n in zf.namelist():
        mod = n.replace("modname", kwargs["modname"])
        if n[-1] in (os.path.sep, "\\", "/"):
            os.mkdir(os.path.join(dst, mod), 0755)
        else:
            ext = n.rsplit(".", 1)[-1]
            fd = open(os.path.join(dst, mod), "w", 0644)
            if ext in ("conf", "html", "txt", "py", "md", "sh", "d") or \
                    n in ("Procfile"):
                #print "patching: %s" % n
                fd.write(string.Template(zf.read(n)).substitute(kwargs))
            else:
                fd.write(zf.read(n))
            fd.close()

    # make sure we can actually run start.sh
    if os.path.exists(os.path.join(dst, "start.sh")):
        os.chmod(os.path.join(dst, "start.sh"), 0755)

    if kwargs["use_git"] is True:
        os.chdir(kwargs["project_path"])
        os.system("git init")
        os.system("git add .gitignore")


def usage(version):
    print("""\
usage: cyclone app [options]
Options:
 -h --help              Show this help.
 -n --new               Dumps a sample server code to stdout.
 -p --project=NAME      Create new cyclone project.
 -g --git               Use in conjunction with -p to make it a git repository.
 -m --modname=NAME      Use another name for the module [default: project_name]
 -v --version=VERSION   Set project version [default: %s]
 -s --set-pkg-version   Set version on package name [default: False]
 -t --target=PATH       Set path where project is created \
[default: current directory]
 -l --license=FILE      Append the following license file [default: Apache 2]
 -a --appskel=SKEL      Set the application skeleton [default: default]

SKEL:
  default              Basic cyclone project
  signup               Basic sign up/in/out, password reset, etc
  foreman              Create a foreman based project \
(suited to run on heroku and other PaaS)

Examples:
 For a simple hello world:
 $ cyclone app -n > hello.py

 For a project that requires sign up:
 $ cyclone app --project=foobar --appskel=signup""" % (version))
    sys.exit(0)


def main():
    project = None
    modname = None
    use_git = False
    set_pkg_version = False
    default_version, version = "0.1", None
    default_target, target = os.getcwd(), None
    license_file = None
    skel = "default"

    shortopts = "hgsnp:m:v:t:l:a:"
    longopts = ["help", "new", "git", "set-pkg-version",
                 "project=", "modname=", "version=", "target=", "license=",
                 "appskel="]
    try:
        opts, args = getopt.getopt(sys.argv[1:], shortopts, longopts)
    except getopt.GetoptError:
        usage(default_version)

    for o, a in opts:
        if o in ("-h", "--help"):
            usage(default_version)

        if o in ("-n", "--new"):
            print "%s%s" % (DEFAULT_LICENSE % {"year": datetime.now().year},
                            SAMPLE_SERVER)
            sys.exit(1)

        if o in ("-g", "--git"):
            use_git = True

        if o in ("-s", "--set-pkg-version"):
            set_pkg_version = True

        elif o in ("-p", "--project"):
            project = a

        elif o in ("-m", "--modname"):
            modname = a

        elif o in ("-v", "--version"):
            version = a

        elif o in ("-t", "--target"):
            target = a

        elif o in ("-l", "--license"):
            license_file = a

        elif o in ("-a", "--appskel"):
            if a in ("default", "foreman", "signup"):
                skel = a
            else:
                print("Invalid appskel name: %s" % a)
                sys.exit(1)

    if license_file is None:
        license = DEFAULT_LICENSE % {"year": datetime.now().year}
    else:
        with open(license_file) as f:
            license = f.read()

    if project is None:
        usage(default_version)
    elif not re.match(r"^[0-9a-z][0-9a-z_-]+$", project, re.I):
        print("Invalid project name.")
        sys.exit(1)

    mod_is_proj = False
    if modname is None:
        mod_is_proj = True
        modname = project

    if modname in ("frontend", "tools", "twisted"):
        if mod_is_proj is True:
            print("Please specify a different modname, using "
                  "--modname=name. '%s' is not allowed." % modname)
        else:
            print("Please specify a different modname. "
                  "'%s' is not allowed." % modname)
        sys.exit(1)

    if not re.match(r"^[0-9a-z_]+$", modname, re.I):
        print("Invalid module name.")
        sys.exit(1)

    if version is None:
        version = default_version

    if target is None:
        target = default_target

    if not (os.access(target, os.W_OK) and os.access(target, os.X_OK)):
        print("Cannot create project on target directory "
              "'%s': permission denied" % target)
        sys.exit(1)

    name = "Foo Bar"
    email = "root@localhost"
    if use_git is True:
        with os.popen("git config --list") as fd:
            for line in fd:
                line = line.replace("\r", "").replace("\n", "")
                try:
                    k, v = line.split("=", 1)
                except:
                    continue

                if k == "user.name":
                    name = v
                elif k == "user.email":
                    email = v

    skel = zipfile.ZipFile(open(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "appskel_%s.zip" % skel), "rb"))

    if set_pkg_version is True:
        project_name = "%s-%s" % (project, version)
    else:
        project_name = project

    project_path = os.path.join(target, project_name)
    if os.path.exists(project_path):
        print("Directory '%s' already exists. Either remove it, or set a "
              "different project name. "
              "e.g.: python -m cyclone.app -p %sz" % (project_path,
                                                      project_name))
        sys.exit(1)

    new_project(skel=skel,
                name=name,
                email=email,
                project=project,
                project_name=project_name,
                project_path=project_path,
                modname=modname,
                version=version,
                target=target,
                use_git=use_git,
                license=license,
                cookie_secret=base64.b64encode(uuid.uuid4().bytes +
                                               uuid.uuid4().bytes))


if __name__ == "__main__":
    main()
