local sgmatch = string.gmatch
local utils = require 'bin.scaffold.utils'

local gitignore = [[
# lor
client_body_temp
fastcgi_temp
logs
proxy_temp
tmp
uwsgi_temp

# Compiled Lua sources
luac.out

# luarocks build files
*.src.rock
*.zip
*.tar.gz

# Object files
*.o
*.os
*.ko
*.obj
*.elf

# Precompiled Headers
*.gch
*.pch

# Libraries
*.lib
*.a
*.la
*.lo
*.def
*.exp

# Shared objects (inc. Windows DLLs)
*.dll
*.so
*.so.*
*.dylib

# Executables
*.exe
*.out
*.app
*.i*86
*.x86_64
*.hex
]]

local mime_types = [[
types {
    text/html                             html htm shtml;
    text/css                              css;
    text/xml                              xml;
    image/gif                             gif;
    image/jpeg                            jpeg jpg;
    application/javascript                js;
    application/atom+xml                  atom;
    application/rss+xml                   rss;

    text/mathml                           mml;
    text/plain                            txt;
    text/vnd.sun.j2me.app-descriptor      jad;
    text/vnd.wap.wml                      wml;
    text/x-component                      htc;

    image/png                             png;
    image/tiff                            tif tiff;
    image/vnd.wap.wbmp                    wbmp;
    image/x-icon                          ico;
    image/x-jng                           jng;
    image/x-ms-bmp                        bmp;
    image/svg+xml                         svg svgz;
    image/webp                            webp;

    application/font-woff                 woff;
    application/java-archive              jar war ear;
    application/json                      json;
    application/mac-binhex40              hqx;
    application/msword                    doc;
    application/pdf                       pdf;
    application/postscript                ps eps ai;
    application/rtf                       rtf;
    application/vnd.apple.mpegurl         m3u8;
    application/vnd.ms-excel              xls;
    application/vnd.ms-fontobject         eot;
    application/vnd.ms-powerpoint         ppt;
    application/vnd.wap.wmlc              wmlc;
    application/vnd.google-earth.kml+xml  kml;
    application/vnd.google-earth.kmz      kmz;
    application/x-7z-compressed           7z;
    application/x-cocoa                   cco;
    application/x-java-archive-diff       jardiff;
    application/x-java-jnlp-file          jnlp;
    application/x-makeself                run;
    application/x-perl                    pl pm;
    application/x-pilot                   prc pdb;
    application/x-rar-compressed          rar;
    application/x-redhat-package-manager  rpm;
    application/x-sea                     sea;
    application/x-shockwave-flash         swf;
    application/x-stuffit                 sit;
    application/x-tcl                     tcl tk;
    application/x-x509-ca-cert            der pem crt;
    application/x-xpinstall               xpi;
    application/xhtml+xml                 xhtml;
    application/xspf+xml                  xspf;
    application/zip                       zip;

    application/octet-stream              bin exe dll;
    application/octet-stream              deb;
    application/octet-stream              dmg;
    application/octet-stream              iso img;
    application/octet-stream              msi msp msm;

    application/vnd.openxmlformats-officedocument.wordprocessingml.document    docx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet          xlsx;
    application/vnd.openxmlformats-officedocument.presentationml.presentation  pptx;

    audio/midi                            mid midi kar;
    audio/mpeg                            mp3;
    audio/ogg                             ogg;
    audio/x-m4a                           m4a;
    audio/x-realaudio                     ra;

    video/3gpp                            3gpp 3gp;
    video/mp2t                            ts;
    video/mp4                             mp4;
    video/mpeg                            mpeg mpg;
    video/quicktime                       mov;
    video/webm                            webm;
    video/x-flv                           flv;
    video/x-m4v                           m4v;
    video/x-mng                           mng;
    video/x-ms-asf                        asx asf;
    video/x-ms-wmv                        wmv;
    video/x-msvideo                       avi;
}
]]


local index_view_tpl = [[
<!DOCTYPE html>
<html>
<style>
body {
    font: 400 14px/1.6 "Open Sans",sans-serif;
    color: #555;
}

.lor {
    margin: 100px auto;
    width: 800px;
}

.name {
    display: block;
    font: 100 4.5em "Helvetica Neue","Open Sans",sans-serif;
    margin-bottom: 0.25em;
}

a {
    color: #259DFF;
    text-decoration: none;
}

.description {
  position: relative;
  top: -5px;
  font: 100 3em "Helvetica Neue","Open Sans",sans-serif;
  color: #AEAEAE;
}
</style>
<body>

<div class="lor">
<a href="#" class="name">
{{name}}
{% if locals.app_version then %}
    <span class="version">{{locals.app_version}}</span>
{% end %}
</a>
<span class="description">{{desc}}</span>

</div>
</body>
</html>
]]

local user_info_view_tpl = [[
<!DOCTYPE html>
<html>
<style>
body {
    font: 400 14px/1.6 "Open Sans",sans-serif;
    color: #555;
}

.lor {
    margin: 100px auto;
    width: 800px;
}

.desc {
  position: relative;
  bottom: 15px;
  font: 100 3em "Helvetica Neue","Open Sans",sans-serif;
  color: #AEAEAE;
}

.id {
    display: block;
    font: 100 3em "Helvetica Neue","Open Sans", sans-serif;
}

.name {
    display: block;
    font: 100 3em "Helvetica Neue","Open Sans", sans-serif;
    margin-bottom: 0.25em;
}

</style>
<body>

<div class="lor">
  <span class="desc">{{desc}}</span><br>
  <span class="id">{{id}}</span>
  <span class="name">{{name}}</span>
</div>
</body>
</html>
]]

local main_tpl = [[
local app = require("app.server")
app:run()
]]

local server_tpl = [[
local string_find = string.find
local lor = require("lor.index")
local router = require("app.router")
local app = lor()

-- 模板配置
app:conf("view enable", true)
app:conf("view engine", "tmpl")
app:conf("view ext", "html")
app:conf("view layout", "")
app:conf("views", "./app/views")

-- session和cookie支持，如果不需要可注释以下配置
local mw_cookie = require("lor.lib.middleware.cookie")
local mw_session = require("lor.lib.middleware.session")
app:use(mw_cookie())
app:use(mw_session({
    session_key = "__app__", -- the key injected in cookie
    session_aes_key = "aes_key_for_session", -- should set by yourself
    timeout = 3600 -- default session timeout is 3600 seconds
}))

-- 自定义中间件1: 注入一些全局变量供模板渲染使用
local mw_inject_version = require("app.middleware.inject_app_info")
app:use(mw_inject_version())

-- 自定义中间件2: 设置响应头
app:use(function(req, res, next)
    res:set_header("X-Powered-By", "Lor framework")
    next()
end)

router(app) -- 业务路由处理

-- 错误处理插件，可根据需要定义多个
app:erroruse(function(err, req, res, next)
    ngx.log(ngx.ERR, err)

    if req:is_found() ~= true then
        if string_find(req.headers["Accept"], "application/json") then
            res:status(404):json({
                success = false,
                msg = "404! sorry, not found."
            })
        else
            res:status(404):send("404! sorry, not found. " .. (req.path or ""))
        end
    else
        if string_find(req.headers["Accept"], "application/json") then
            res:status(500):json({
                success = false,
                msg = "500! internal error, please check the log."
            })
        else
            res:status(500):send("internal error, please check the log.")
        end
    end
end)

return app
]]


local router_tpl = [[
-- 业务路由管理
local userRouter = require("app.routes.user")

return function(app)

    -- simple router: hello world!
    app:get("/hello", function(req, res, next)
        res:send("hi! welcome to lor framework.")
    end)

    -- simple router: render html, visit "/" or "/?name=foo&desc=bar
    app:get("/", function(req, res, next)
        local data = {
            name =  req.query.name or "lor",
            desc =  req.query.desc or 'a framework of lua based on OpenResty'
        }
        res:render("index", data)
    end)

    -- group router: 对以`/user`开始的请求做过滤处理
    app:use("/user", userRouter())
end

]]


local user_router_tpl = [[
local lor = require("lor.index")
local userRouter = lor:Router() -- 生成一个group router对象

-- 按id查找用户
-- e.g. /query/123
userRouter:get("/query/:id", function(req, res, next)
    local query_id = tonumber(req.params.id) -- 从req.params取参数

    if not query_id then
        return res:render("user/info", {
            desc = "Error to find user, path variable `id` should be a number. e.g. /user/query/123"
        })
    end

    -- 渲染页面
    res:render("user/info", {
        id = query_id,
        name = "user" .. query_id,
        desc = "User Information"
    })
end)

-- 删除用户
-- e.g. /delete?id=123
userRouter:delete("/delete", function(req, res, next)
    local id = req.query.id -- 从req.query取参数
    if not id then
        return res:html("<h2 style='color:red'>Error: query param id is required.</h2>")
    end

    -- 返回html
    res:html("<span>succeed to delete user</span><br/>user id is:<b style='color:red'>" .. id .. "</b>")
end)

-- 修改用户
-- e.g. /put/123?name=sumory
userRouter:put("/put/:id", function(req, res, next)
    local id = req.params.id  -- 从req.params取参数
    local name = req.query.name -- 从req.query取参数

    if not id or not name then
        return res:send("error params: id and name are required.")
    end

    -- 返回文本格式的响应结果
    res:send("succeed to modify user[" .. id .. "] with new name:" .. name)
end)

-- 创建用户
userRouter:post("/post", function(req, res, next)
    local content_type = req.headers['Content-Type']

    -- 如果请求类型为form表单或json请求体
    if string.find(content_type, "application/x-www-form-urlencoded",1, true) or
        string.find(content_type, "application/json",1, true) then
        local id = req.body.id -- 从请求体取参数
        local name = req.body.name -- 从请求体取参数

        if not id or not name then
            return res:json({
                success = false,
                msg = "error params: id and name are required."
            })
        end

        res:json({-- 返回json格式的响应体
            success = true,
            data = {
                id = id,
                name = name,
                desc = "succeed to create new user" .. id
            }
        })
    else -- 不支持其他请求体
        res:status(500):send("not supported request Content-Type[" .. content_type .. "]")
    end
end)

return userRouter
]]



local middleware_tpl = [[

### 自定义插件目录(define your own middleware)


You are recommended to define your own middlewares and keep them in one place to manage.

建议用户将自定义插件存放在此目录下统一管理，然后在其他地方引用，插件的格式如下:

```
local middleware =  function(params)
    return function(req, res, next)
        -- do something with req/res
        next()
    end
end

return middleware
```

]]


local middleware_example_tpl = [[
--- 中间件示例： 为每个请求注入一些通用的变量
local lor = require("lor.index")
return function()
    return function(req, res, next)
        -- res.locals是一个table, 可以在这里注入一些“全局”变量
        -- 这个示例里注入app的名称和版本号， 在渲染页面时即可使用
        res.locals.app_name = "lor application"
        res.locals.app_version = lor.version or ""
        next()
    end
end
]]


local static_tpl = [[

### 静态文件目录(static files directory)

nginx对应配置为

```
location /static {
    alias app/static;
}
```

]]

local ngx_conf_directory = [[

### nginx configuration directory

]]


local ngx_config = require 'bin.scaffold.nginx.config'
local ngx_conf_template = require 'bin.scaffold.nginx.conf_template'
local function nginx_conf_content()
    -- read nginx.conf file
    local nginx_conf_template =  ngx_conf_template.get_ngx_conf_template()

    -- append notice
    nginx_conf_template = [[
#generated by `lor framework`
    ]] .. nginx_conf_template

    local match = {}
    local tmp = 1
    for v in sgmatch(nginx_conf_template , '{{(.-)}}') do
        match[tmp] = v
        tmp = tmp + 1
    end

    for _, directive in ipairs(match) do
        if ngx_config[directive] ~= nil then
            nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', ngx_config[directive])
        else
            nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', '#' .. directive)
        end
    end

    return nginx_conf_template
end
local ngx_conf_tpl = nginx_conf_content()


local start_sh = [[
#!/bin/sh

#####################################################################
# usage:
# sh start.sh -- start application @dev
# sh start.sh ${env} -- start application @${env}

# examples:
# sh start.sh prod -- use conf/nginx-prod.conf to start OpenResty
# sh start.sh -- use conf/nginx-dev.conf to start OpenResty
#####################################################################

if [ -n "$1" ];then
    PROFILE="$1"
else
    PROFILE=dev
fi

mkdir -p logs & mkdir -p tmp
echo "start lor application with profile: "${PROFILE}
nginx -p `pwd`/ -c conf/nginx-${PROFILE}.conf
]]


local stop_sh = [[
#!/bin/sh

#####################################################################
# usage:
# sh stop.sh -- stop application @dev
# sh stop.sh ${env} -- stop application @${env}

# examples:
# sh stop.sh prod -- use conf/nginx-prod.conf to stop OpenResty
# sh stop.sh -- use conf/nginx-dev.conf to stop OpenResty
#####################################################################

if [ -n "$1" ];then
    PROFILE="$1"
else
    PROFILE=dev
fi

mkdir -p logs & mkdir -p tmp
echo "stop lor application with profile: "${PROFILE}
nginx -s stop -p `pwd`/ -c conf/nginx-${PROFILE}.conf
]]

local reload_sh = [[
#!/bin/sh

#####################################################################
# usage:
# sh reload.sh -- reload application @dev
# sh reload.sh ${env} -- reload application @${env}

# examples:
# sh reload.sh prod -- use conf/nginx-prod.conf to reload OpenResty
# sh reload.sh -- use conf/nginx-dev.conf to reload OpenResty
#####################################################################

if [ -n "$1" ];then
    PROFILE="$1"
else
    PROFILE=dev
fi

mkdir -p logs & mkdir -p tmp
echo "reload lor application with profile: "${PROFILE}
nginx -s reload -p `pwd`/ -c conf/nginx-${PROFILE}.conf
]]



local Generator = {}

Generator.files = {
    ['.gitignore'] = gitignore,
    ['app/main.lua'] = main_tpl,
    ['app/server.lua'] = server_tpl,
    ['app/router.lua'] = router_tpl,
    ['app/routes/user.lua'] = user_router_tpl,
    ['app/views/index.html'] = index_view_tpl,
    ['app/views/user/info.html'] = user_info_view_tpl,
    ['app/middleware/README.md'] = middleware_tpl,
    ['app/middleware/inject_app_info.lua'] = middleware_example_tpl,
    ['app/static/README.md'] = static_tpl, -- static files directory,e.g. js/css/img
    ['conf/README.md'] = ngx_conf_directory, -- nginx config directory
    ['conf/nginx-dev.conf'] = ngx_conf_tpl, -- nginx config file
    ['conf/mime.types'] = mime_types, -- nginx mime
    ['start.sh'] = start_sh,
    ['stop.sh'] = stop_sh,
    ['reload.sh'] = reload_sh
}

function Generator.new(name)
    print('Creating app: ' .. name .. '...')
    Generator.create_files(name)
end

function Generator.create_files(parent)
    for file_path, file_content in pairs(Generator.files) do

        local full_file_path = parent .. '/' .. file_path
        local full_file_dirname = utils.dirname(full_file_path)
        os.execute('mkdir -p ' .. full_file_dirname .. ' > /dev/null')

        local fw = io.open(full_file_path, 'w')
        fw:write(file_content)
        fw:close()
        print('  created file ' .. full_file_path)
    end
end

return Generator




