/**
 * @file Apeman app configuration file.
 * @namespace apemanfile
 * @param {object} apeman - Apeman module.
 * @returns {object} - An apeman file object.
 * @see {@link http://www.npmjs.org/package/apeman | apeman}
 * @author Taka Okunishi
 *
 */
var path = require('path'),
    fs = require('fs'),
    util = require('util'),
    format = util.format;

module.exports = function (apeman) {
    var cwd = process.cwd();
    process.chdir(__dirname);

    var Apemanfile = apeman.ApemanfileObject,
        prototypePath = apeman.findupApp('apc-abstract-heir'),
        prototype = require(prototypePath)(apeman),
        basedir = __dirname;

    // Helper modules
    var prototypeHelper = prototype.helper();


    var h = prototypeHelper.fallbackCopy(prototypeHelper, {
        lookupPaths: [basedir].concat(prototypeHelper.lookupPaths),
        coffeeSrcDir: 'clientside/src/coffee',
        cssSrcDir: 'clientside/src/css',
        cssMinSrcDir: 'clientside/src/css_min',
        fontSrcDir: 'clientside/src/font',
        fontFlattenSrcDir: 'clientside/src/font_flatten',
        htmlSrcDir: 'clientside/src/html',
        htmlSrcPartialsDir: 'clientside/src/html/_partials',
        htmlMinSrcDir: 'clientside/src/html_min',
        imgSrcDir: 'clientside/src/img',
        imgResizedSrcDir: 'clientside/src/img_resized',
        imgThemesSrcDir: 'clientside/src/img_themes',
        jadeSrcDir: 'clientside/src/jade',
        jadePrecompiledSrcDir: 'clientside/src/jade_precompiled',
        jsLibrariesSrcDir: 'clientside/src/js_libraries',
        jsMinSrcDir: 'clientside/src/js_min',
        jsSrcDir: 'clientside/src/js',
        jsTmplDir: 'tmpl/js',
        lessFontsSrcDir: 'clientside/src/less/style/fonts',
        lessIconsSrcDir: 'clientside/src/less/style/icons',
        lessSrcDir: 'clientside/src/less',
        lessThemesSrcDir: 'clientside/src/less_themes',
        styleguideDocDir: 'doc/styleguide',
        taskDataDir: 'task/data',
        settingsDir: 'settings',
        fontsExtPattern: '.+(ttf|otf|eot|svg|woff)',
        constantsDir: 'serverside/constants',
        dataVarDir: 'var/data',
        secretsDir: 'settings/secrets',
        distDevDir: 'clientside/dist/dev',
        distProductionDir: 'clientside/dist/production',
        newSymlinkIfExists: function (src) {
            return h.newSymlink().src(h.lookup(src));
        },
        newJsSrcSymlink: function (filename) {
            var src = h.jsSrcDir + '/' + filename;
            return h.newSymlinkIfExists(src);
        },
        newJsMinSrcSymlink: function (filename) {
            var src = h.jsMinSrcDir + '/' + filename;
            return h.newSymlinkIfExists(src);
        },
        /**
         * Resolve task data file name. Search in apemanfile.helper.taskDataDir.
         * @function apemanfile.helper.resolveTaskDataFile
         * @param {string} filename - File name to resolve.
         * @returns {string} - Resolved file name.
         */
        resolveTaskDataFile: function (filename) {
            var h = this;
            var fullname = path.join(h.taskDataDir, filename);
            return h.resolve(fullname) ||
                h.resolve(fullname + '.js') ||
                h.resolve(fullname + '.json');
        },
        /**
         * Resolve settings file name. Search in apemanfile.helper.settingsDir
         * @function apemanfile.helper.settingFile
         * @param {string} filename - File name to resolve.
         * @returns {string} - Resolved file name.
         */
        resolveSettingFile: function (filename) {
            var h = this,
                fullname = path.join(h.settingsDir, filename);
            return h.resolve(fullname) ||
                h.resolve(fullname + '.js') ||
                h.resolve(fullname + '.json');
        },
        /**
         * Define a size object.
         * @function apemanfile.helper.square
         * @param {number} size - With and height size.
         * @returns {{width: *, height: *}}
         */
        square: function (size) {
            return h.rect(size, size);
        },
        /**
         * Define a rect object.
         * @function apemanfile.helper.:
         * @param {number} width - Rect with.
         * @param {number} height - Rect height.
         * @returns {{width: *, height: *}}
         */
        rect: function (width, height) {
            return {width: width, height: height};
        },
        /**
         * Define a new static file structure.
         * @function apemanfile.helper.newStaticStructure
         * @param {object} data - Structure data.
         * @returns {Object} - Structure data.
         */
        newStaticStructure: function (data) {
            return h.deepCopy({
                javascripts: {},
                stylesheets: {},
                images: {},
                fonts: {},
                'favicon.ico': h.newSymlink(h.imgSrcDir + '/favicon.ico')
            }, data || {});
        }
    });

    // Add helper functions if _apemanfile_helper.js found.
    h.loadLocalHelperFile();

    var meta = {
        name: 'apc-static',
        version: h.readLocalVERSION() || '0.0.0',
        repository: 'https://github.com/apeman-apps/apc-static.git',
        description: 'An apeman core app. Web app to serve static files. A heir of apc-abstract-heir.',
        author: {
            name: 'Taka Okunishi',
            email: 'okunishinishi@apeman.info'
        }
    };

    var baseConstants = {
            APEMAN_ID_COOKIE_KEY: 'apeman-id',
            DEFAULT_LANG: 'en',
            SUPPORTED_LANGS: 'en,ja',
            CONSOLE_ENABLED: false,
            PAGE_NOT_FOUND_STATUS_CODE: 404,
            SOMETHING_WRONG_STATUS_CODE: 503,
            REDIRECT_STATUS_CODE: 302,
            ACCESS_LOG_FORMAT: 'a',
            BASE_DIR_PATH: '#{basedir}',
            CONTEXT_NAME: '',
            LOG_LEVEL: 'info', // off, fatal, error, warn, info, debug, trace
            LOG_DIR_PATH: '#{BASE_DIR_PATH}/var/#{CONTEXT_NAME}/log',
            LOG_NAME_PREFIX: '',
            SERVER_LOG_NAME: '#{LOG_NAME_PREFIX}server.log',
            ACCESS_LOG_NAME: '#{LOG_NAME_PREFIX}access.log',
            ERROR_LOG_NAME: '#{LOG_NAME_PREFIX}error.log',
            BATCH_LOG_NAME: '#{LOG_NAME_PREFIX}batch.log',
            SERVER_LOG_PATH: '#{LOG_DIR_PATH}/#{SERVER_LOG_NAME}',
            ACCESS_LOG_PATH: '#{LOG_DIR_PATH}/#{ACCESS_LOG_NAME}',
            ERROR_LOG_PATH: '#{LOG_DIR_PATH}/#{ERROR_LOG_NAME}',
            BATCH_LOG_PATH: '#{LOG_DIR_PATH}/#{BATCH_LOG_NAME}',
            CLIENTSIDE_DIR_PATH: '#{BASE_DIR_PATH}/clientside',
            NODE_ENV: 'production',
            HTML_DIR_NAME: 'html',
            HTML_DIR_PATH: '#{CLIENTSIDE_DIR_PATH}/src/#{HTML_DIR_NAME}',
            EN_ERROR_HTML_DIR_PATH: '#{HTML_DIR_PATH}/en/error',
            JA_ERROR_HTML_DIR_PATH: '#{HTML_DIR_PATH}/ja/error',
            HTML_EXT_NAME: '.html',
            PAGE_NOT_FOUND_HTML_NAME: 'page-not-found#{HTML_EXT_NAME}',
            SOMETHING_WRONG_HTML_NAME: 'something-wrong#{HTML_EXT_NAME}',
            EN_PAGE_NOT_FOUND_HTML_PATH: '#{EN_ERROR_HTML_DIR_PATH}/#{PAGE_NOT_FOUND_HTML_NAME}',
            JA_PAGE_NOT_FOUND_HTML_PATH: '#{JA_ERROR_HTML_DIR_PATH}/#{PAGE_NOT_FOUND_HTML_NAME}',
            EN_SOMETHING_WRONG_HTML_PATH: '#{EN_ERROR_HTML_DIR_PATH}/#{SOMETHING_WRONG_HTML_NAME}',
            JA_SOMETHING_WRONG_HTML_PATH: '#{JA_ERROR_HTML_DIR_PATH}/#{SOMETHING_WRONG_HTML_NAME}',
            SERVER_CRASH_DELAY: 30 * 1000,
            SHOULD_MINIFY_HTML: false,
            SECRETS_DIR: '#{BASE_DIR_PATH}/settings/secrets',
            PUBLIC_DIR_PATH: '#{CLIENTSIDE_DIR_PATH}/dist/#{CONTEXT_NAME}',
            VAR_HTML_DIR_PATH: '#{BASE_DIR_PATH}/var/#{CONTEXT_NAME}/html',
            VAR_DATA_DIR_PATH: '#{BASE_DIR_PATH}/var/#{CONTEXT_NAME}/data',
            JADE_DATA_DIR_PATH: 'task/data/jade-contexts',
            JADE_EN_DATA_FILE_PATH: '#{JADE_DATA_DIR_PATH}/jade-en-context-data.js',
            JADE_EN_MIN_DATA_FILE_PATH: '#{JADE_DATA_DIR_PATH}/jade-en-min-context-data.js',
            JADE_JA_DATA_FILE_PATH: '#{JADE_DATA_DIR_PATH}/jade-ja-context-data.js',
            JADE_JA_MIN_DATA_FILE_PATH: '#{JADE_DATA_DIR_PATH}/jade-ja-min-context-data.js',
            PIXABAY_SECRET_FILE_NAME: 'pixabay-secret.js',
            PIXABAY_SECRET_FILE_PATH: '#{SECRETS_DIR}/#{PIXABAY_SECRET_FILE_NAME}',
            PIXABAY_DATA_DIR_PATH: '#{VAR_DATA_DIR_PATH}/pixabay',
            PIXABAY_TAGS_FILE_PATH: '#{VAR_DATA_DIR_PATH}/tags.json',
            PIXABAY_TAGGED_DATA_DIR_PATH: '#{PIXABAY_DATA_DIR_PATH}/tagged',
            PIXABAY_GALLERY_DATA_DIR_PATH: '#{PIXABAY_TAGGED_DATA_DIR_PATH}',
            PIXABAY_GALLERY_TMPL_PATH: path.resolve('clientside/src/jade_precompiled/batch/photo-gallery.js'),
            PIXABAY_GALLERY_HTML_DIR_PATH: '#{VAR_HTML_DIR_PATH}/pixabay'
        },
        productionConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'production',
            SHOULD_MINIFY_HTML: true,
            NODE_ENV: 'production',
            HTML_EXT_NAME: '.min.html',
            HTML_DIR_NAME: 'html_min',
            CONTEXT_LOADER_NAME: 'newProductionContext'
        }),
        devConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'dev',
            LOG_NAME_PREFIX: 'dev-',
            NODE_ENV: 'development',
            CONTEXT_LOADER_NAME: 'newDevContext',
            CONSOLE_ENABLED: true,
            LOG_LEVEL: 'trace'
        }),
        scenarioTestConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'scenario_test',
            LOG_NAME_PREFIX: 'scenario-',
            CONSOLE_ENABLED: true,
            NODE_ENV: 'test',
            CONTEXT_LOADER_NAME: 'newScenarioTestContext',
            PUBLIC_DIR_PATH: '#{CLIENTSIDE_DIR_PATH}/dist/dev',
            LOG_LEVEL: 'debug',
            PIXABAY_SECRET_FILE_NAME: 'pixabay-scenario-secret.js'
        }),
        browserScenarioTestConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'browser_scenario_test',
            LOG_NAME_PREFIX: 'browser-scenario-test-',
            NODE_ENV: 'test',
            CONTEXT_LOADER_NAME: 'newBrowserScenarioTestContext',
            PUBLIC_DIR_PATH: '#{CLIENTSIDE_DIR_PATH}/dist/production',
            CONSOLE_ENABLED: false,
            SHOULD_MINIFY_HTML: true,
            LOG_LEVEL: 'debug'
        }),
        docConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'doc',
            LOG_NAME_PREFIX: 'doc-',
            CONTEXT_LOADER_NAME: 'newDocContext',
            LOG_LEVEL: 'error'
        }),
        ciConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'ci',
            LOG_NAME_PREFIX: 'ci-',
            CONTEXT_LOADER_NAME: 'newCIContext',
            LOG_LEVEL: 'info'
        }),
        operationConstants = h.fallbackCopy(baseConstants, {
            CONTEXT_NAME: 'operation',
            LOG_NAME_PREFIX: 'operation-',
            CONSOLE_ENABLED: false,
            CONTEXT_LOADER_NAME: 'newDocContext',
            LOG_LEVEL: 'info'
        }),
        operationDevConstants = h.fallbackCopy(operationConstants, {
            CONTEXT_NAME: 'operation_dev',
            LOG_NAME_PREFIX: 'operation-dev-',
            CONTEXT_LOADER_NAME: 'newDocContext',
            NODE_ENV: 'development',
            CONSOLE_ENABLED: true,
            LOG_LEVEL: 'trace'
        });

    h.pixabayBatchLoaders = ['newPixabayDataFetchBatch', 'newPixabayGalleryRenderBatch'];

    /**
     * File system structure data.
     * An empty object represent a directory.
     * @type object
     * @private
     */
    var structureData = {
        bin: {
            'start-dev-server': h.newStartServerBinFile(devConstants).mode('755'),
            'start-production-server': h.newStartServerBinFile(productionConstants).mode('755'),
            'start-doc-server': h.newStartServerBinFile(docConstants).mode('755'),
            'start-ci-server': h.newStartServerBinFile(ciConstants).mode('755'),
            'start-operation-server': h.newStartServerBinFile(operationConstants).mode('755'),
            'start-operation-dev-server': h.newStartServerBinFile(operationDevConstants).mode('755'),
            'watch': h.newTaskBinFile({taskName: 'watch'}).mode('755'),
            'run-pixabay-dev-batches': h.newRunBatchesBinFile(h.fallbackCopy(devConstants, {
                batchLoaders: h.pixabayBatchLoaders
            })).mode('755'),
            'run-pixabay-production-batches': h.newRunBatchesBinFile(h.fallbackCopy(productionConstants, {
                batchLoaders: h.pixabayBatchLoaders
            })).mode('755')
        },
        clientside: {
            src: {
                coffee: {
                    apeman: {},
                    contexts: {}
                },
                css: {},
                css_min: {},

                font: {},
                font_flatten: {},
                html: {
                    en: {
                    },
                    ja: {
                    },
                    _partials: {}
                },
                html_min: {
                    en: {},
                    ja: {}
                },
                img: {},
                img_resized: {},
                img_themes: {
                    favicon: {},
                    startup_image: {}
                },
                jade: {
                    batch: {},
                    base: {},
                    error: {},
                    mixin: {}
                },
                jade_precompiled: {},
                js: {
                    contexts: {},
                    locales: {}
                },
                js_min: {},
                js_libraries: {},
                less: {
                    variable: {
                        'urls.less': h.newUrlsLessFile(h.resolveTaskDataFile('urls-less-data.js')),
                    },
                    style: {
                        icons: {
                            'ionicons-icon-style.less': h.newIoniconsIconStyleLessFile(h.resolveTaskDataFile('ionicons-data.js')),
                            'font-awesome-icon-style.less': h.newFontAwesomeIconStyleLessFile(h.resolveTaskDataFile('font-awesome-data.js'))
                        },
                        fonts: {

                        }
                    },
                    mixin: {},
                    'base.less': h.newBaseLessFile({}).force(false).mode('644'),
                    'index.less': h.newIndexLessFile({}).force(false).mode('644')
                },
                less_themes: {
                    variable: {
                        color_scheme: {}
                    },
                    mixin: {}
                }
            },
            dist: {
                doc: h.newSymlink().src('doc'),
                ci: {
                    'robots.txt': h.newRobotsTxtFile({userAgent: '*', disallow: ['/']})
                },
                dev: h.newStaticStructure({
                    en: h.newStaticStructure({
                        javascripts: {
                            'context.js': h.newJsSrcSymlink('contexts/dev-context.js'),
                            'l.js': h.newJsSrcSymlink('locales/en.js')
                        }
                    }),
                    ja: h.newStaticStructure({
                        javascripts: {
                            'context.js': h.newJsSrcSymlink('contexts/dev-context.js'),
                            'l.js': h.newJsSrcSymlink('locales/ja.js')
                        }
                    }),

                    javascripts: {
                        'context.js': h.newJsSrcSymlink('contexts/dev-context.js'),
                        'l.js': h.newJsSrcSymlink('locales/en.js')
                    }
                }),
                production: h.newStaticStructure({
                    en: h.newStaticStructure({
                        javascripts: {
                            'context.js': h.newJsSrcSymlink('contexts/production-context.js'),
                            'l.min.js': h.newJsMinSrcSymlink('locales/en.min.js')
                        }
                    }),
                    ja: h.newStaticStructure({
                        javascripts: {
                            'context.js': h.newJsSrcSymlink('contexts/production-context.js'),
                            'l.min.js': h.newJsMinSrcSymlink('locales/en.min.js')
                        }
                    }),
                    javascripts: {
                        'context.js': h.newJsSrcSymlink('contexts/production-context.js'),
                        'l.min.js': h.newJsMinSrcSymlink('locales/en.min.js')
                    }
                }),
                operation: {
                    'robots.txt': h.newRobotsTxtFile({userAgent: '*', disallow: ['/']})
                },
                operation_dev: {
                    'robots.txt': h.newRobotsTxtFile({userAgent: '*', disallow: ['/']})
                }
            }
        },
        doc: {
            'robots.txt': h.newRobotsTxtFile({userAgent: '*', disallow: ['/']}),
            'index.html': h.newDocIndexHtmlFile({})
        },
        lib: {
            compile: {},
            fetch: {},
            html: {},
            image: {},
            math: {},
            minify: {},
            lint: {},
            url: {},
            zip: {},
            uuid: {}
        },
        locales: {
        },
        serverside: {
            batch: {},
            constants: {},
            context: {},
            logger: {},
            middleware: {},
            request: {},
            response: {},
            server: {}
        },
        settings: {
            secrets: {
                'pixabay-scenario-secret.js.example': h.newPixabaySecretJsFile({}).force(false).mode('644'),
                'pixabay-scenario-secret.js': h.newPixabaySecretJsFile({}).force(false).mode('644'),
                'pixabay-secret.js.example': h.newPixabaySecretJsFile({}).force(false).mode('644'),
                'pixabay-secret.js': h.newPixabaySecretJsFile({}).force(false).mode('644'),
            }
        },
        task: {
            data: {
                'jade-contexts': {
                    'jade-en-context-data.js': h.newJadeContextDataJsFile({lang: 'en'}),
                    'jade-ja-context-data.js': h.newJadeContextDataJsFile({lang: 'ja'}),
                    'jade-en-min-context-data.js': h.newJadeContextDataJsFile({lang: 'en', min: true}),
                    'jade-ja-min-context-data.js': h.newJadeContextDataJsFile({lang: 'ja', min: true})
                }
            }
        },
        tmpl: {
            html: {},
            less: {
                themes: {}
            },
            text: {}
        },
        test: {
            browser_unit_tests: {
                'karma.config.js': h.newKarmaConfigJsFile({
                    files: [
                        'clientside/src/js_libraries/libraries.js',
                        'clientside/src/coffee/**/*.coffee',
                        'test/browser_unit_tests/**/*-test.js'
                    ]
                })
            },
            scenario_tests: {
                batch_scenario: {
                    '00.before.js': h.newScenarioTestBeforeJsFile({}).force(false).mode('644'),
                    '99.after.js': h.newScenarioTestAfterJsFile({}).force(false).mode('644')
                },
                crawl_scenario: {
                    '00.before.js': h.newScenarioTestBeforeJsFile({}).force(false).mode('644'),
                    '99.after.js': h.newScenarioTestAfterJsFile({}).force(false).mode('644')
                }
            },
            unit_tests: {
                lib: {
                    compile: {},
                    fetch: {},
                    html: {},
                    image: {},
                    math: {},
                    minify: {},
                    lint: {},
                    url: {},
                    uuid: {},
                    zip: {}
                },
                serverside: {
                    batch: {},
                    constants: {},
                    context: {},
                    logger: {},
                    middleware: {},
                    request: {},
                    response: {},
                    server: {}
                },
                task: {
                    worker: {
                    }
                }
            }
        },
        var: {
            production: {
                data: {},
                log: {}
            },
            dev: {
                data: {},
                log: {}
            },
            ci: {
                log: {}
            },
            doc: {
                log: {}
            },
            scenario_test: {
                data: {},
                log: {}
            },
            browser_scenario_test: {
                data: {},
                log: {}
            },
            operation: {},
            operation_dev: {}
        },
        '_apemanfile_helper.js': h.newApemanfileHelperJsFile(h.apemanHelperData),
        '.travis.yml': h.newTravisYmlFile({}).force(false).mode('644'),
        '.gitignore': h.newGitignoreFile({
            patterns: [
                '.coveralls.yml',
                '.apemanhelperdata',
                h.secretsDir + '/*-secret.js',
            ]})
    };

    /**
     * Packages installed by npm.
     * @type object
     * @private
     */
    var nodePackages = {
        "connect": "~2.12.0",
        "cookies": "~0.4.0",
        "dateformat": "~1.0.7-1.2.3",
        "imagemagick": "~0.1.3",
        "locale": "~0.0.14",
        "pluralize": "~0.0.7",
        "request": "~2.33.0",
    };

    /**
     * Packages installed by npm for development user.
     * @type {object}
     * @private
     */
    var nodePackages$dev = {
        "bower": "~1.2.8",
        "cheerio": "~0.13.1",
        "clean-css": "~2.0.8",
        "csslint": "~0.10.0",
        "phantomjs": "1.9.1-0",
        "coffee-script": "~1.6.3",
        "color-scheme": "~0.0.5",
        "karma-script-launcher": "~0.1.0",
        "karma-chrome-launcher": "~0.1.2",
        "karma-firefox-launcher": "~0.1.3",
        "karma-phantomjs-launcher": "~0.1.2",
        "karma-jasmine": "~0.1.5",
        "karma-coffee-preprocessor": "~0.1.3",
        "karma": "~0.10.9",
        "html": "~0.0.7",
        "html-minifier": "~0.5.5",
        "html5-lint": "~0.2.1",
        "js-beautify": "~1.4.0",
        "jade": "~1.1.5",
        "kss": "~ 0.3.7",
        "less": "~1.6.1",
        "node-markdown": "~0.1.1",
        "uglify-js": "~2.4.12"
    };

    var bowerPackages = {
        jquery: '~2.1.0'
    };

    var prototypeTasks = prototype.tasks();


    var tasks = {
        structure: {
            config: {
                data: structureData
            }
        },
        generateColorThemes: h.generateColorThemesLessFilesTask({
            destDir: h.lessThemesSrcDir + '/variable/color_scheme'
        }),
        generateClientsideThemesLess: h.generateThemeLessFilesTask({
            destDir: h.lessThemesSrcDir + '/mixin',
            dataFile: h.resolveTaskDataFile('style-themes-data.js')
        }),
        themes: [
            'generateColorThemes',
            'generateClientsideThemesLess'
        ],
        generateClientsideLocales: h.generateClientsideLocaleFilesTask({
            srcDir: 'locales',
            destDir: h.jsSrcDir + '/locales'
        }),
        generateClientsideFontsLess: h.generateFontLessFilesTask({
            destDir: h.lessFontsSrcDir,
            imports: h.resolve(h.lessSrcDir + '/variable/urls.less'),
            nameFormat: '%s-font-style.less',
            fontFiles: h.fontFlattenSrcDir + '/**/*' + h.fontsExtPattern
        }),
        generateColorLess: h.generateColorLessTask({
            dest: h.lessSrcDir + '/variable/colors.less',
            settingFile: h.resolveSettingFile('style-color-setting'),
            srcPattern: h.lessThemesSrcDir + '/variable/color_scheme/{{variation}}-{{scheme}}/{{variation}}-{{scheme}}-color-scheme-theme-{{theme}}.less'
        }),
        clientside: [
            'generateClientsideLocales',
            'generateClientsideFontsLess',
            'themes',
            'generateColorLess'
        ],
        generateLocalesDataJson: h.generateDataJsonTask({
            src: 'locales/*.js',
            ignore: 'locales/+(index|.*|_*).js'
        }),
        dataJson: [
            'generateLocalesDataJson'
        ],
        generateCIConstantsJson: h.generateConstantsJsonTask({
            data: ciConstants,
            dest: h.constantsDir + '/ci-constants.json'
        }),
        generateDevConstantsJson: h.generateConstantsJsonTask({
            data: devConstants,
            dest: h.constantsDir + '/dev-constants.json'
        }),
        generateDocConstantsJson: h.generateConstantsJsonTask({
            data: docConstants,
            dest: h.constantsDir + '/doc-constants.json'
        }),
        generateScenarioTestConstantsJson: h.generateConstantsJsonTask({
            data: scenarioTestConstants,
            dest: h.constantsDir + '/scenario-test-constants.json'
        }),
        generateBrowserScenarioTestConstantsJson: h.generateConstantsJsonTask({
            data: browserScenarioTestConstants,
            dest: h.constantsDir + '/browser-scenario-test-constants.json'
        }),
        generateProductionConstantsJson: h.generateConstantsJsonTask({
            data: productionConstants,
            dest: h.constantsDir + '/production-constants.json'
        }),
        generateOperationConstantsJson: h.generateConstantsJsonTask({
            data: operationConstants,
            dest: h.constantsDir + '/operation-constants.json'
        }),
        generateOperationDevConstantsJson: h.generateConstantsJsonTask({
            data: operationDevConstants,
            dest: h.constantsDir + '/operation-dev-constants.json'
        }),
        generateServersideConstantsJson: [
            'generateCIConstantsJson',
            'generateDevConstantsJson',
            'generateDocConstantsJson',
            'generateScenarioTestConstantsJson',
            'generateBrowserScenarioTestConstantsJson',
            'generateProductionConstantsJson',
            'generateOperationConstantsJson',
            'generateOperationDevConstantsJson',
        ],
        serverside: [
            'generateServersideConstantsJson'
        ],
        compileApemanCoffee: h.concatAndCompileCoffeeScriptFilesTask({
            src: h.coffeeSrcDir + '/apeman/*.coffee',
            dest: h.jsLibrariesSrcDir + '/apeman.js',
            tmpl: h.jsTmplDir + '/apeman.js.hbs'
        }),
        compileContextsCoffee: h.compileCoffeeDirectoryTask({
            srcDir: h.coffeeSrcDir + '/contexts',
            destDir: h.jsSrcDir + '/contexts',
            pattern: '*.coffee'
        }),
        compileCoffee: [
            'compileApemanCoffee',
            'compileContextsCoffee'
        ],
        compileSrcLess: h.compileLessDirectoryTask({
            srcDir: h.lessSrcDir,
            destDir: h.cssSrcDir,
            pattern: '**/*.less',
            ignore: [
                '+(variable|mixin|style)/**/*.less',
                '**/_*.less'
            ]
        }),
        compileLess: [
            'compileSrcLess'
        ],
        precompileJade: h.precompileJadeDirectoryTask({
            srcDir: h.jadeSrcDir,
            pattern: '**/*.jade',
            ignore: ['+(base|mixin)/**/*', 'layout.jade'],
            destDir: h.jadePrecompiledSrcDir
        }),
        compileJadeToEn: h.compileJadeDirectoryTask({
            srcDir: h.jadePrecompiledSrcDir,
            destDir: h.htmlSrcDir + '/en',
            ignore: ['+(batch)/**/*', '**/layout.*'],
            dataFile: h.resolveTaskDataFile('jade-contexts/jade-en-context-data.js')
        }),
        compileJadeToEnMin: h.compileJadeDirectoryTask({
            srcDir: h.jadePrecompiledSrcDir,
            destDir: h.htmlMinSrcDir + '/en',
            ignore: ['+(batch)/**/*', '**/layout.*'],
            dataFile: h.resolveTaskDataFile('jade-contexts/jade-en-min-context-data.js'),
            minify: true
        }),
        compileJadeToJa: h.compileJadeDirectoryTask({
            srcDir: h.jadePrecompiledSrcDir,
            destDir: h.htmlSrcDir + '/ja',
            ignore: ['+(batch)/**/*', '**/layout.*'],
            dataFile: h.resolveTaskDataFile('jade-contexts/jade-ja-context-data.js')
        }),
        compileJadeToJaMin: h.compileJadeDirectoryTask({
            srcDir: h.jadePrecompiledSrcDir,
            destDir: h.htmlMinSrcDir + '/ja',
            ignore: ['+(batch)/**/*', '**/layout.*'],
            dataFile: h.resolveTaskDataFile('jade-contexts/jade-ja-min-context-data.js'),
            minify: true
        }),
        compileJade: [
            'compileJadeToEn',
            'compileJadeToEnMin',
            'compileJadeToJa',
            'compileJadeToJaMin'
        ],
        compile: [
            'compileCoffee',
            'compileLess',
            'precompileJade',
            'compileJade'
        ],
        concatLibrariesJs: h.concatFilesTask({
            src: [
                h.jsLibrariesSrcDir + '/apeman.js',
                h.jsLibrariesSrcDir + '/bower_components/jquery/dist/jquery.js'
            ],
            dest: h.jsLibrariesSrcDir + '/libraries.js'
        }),
        concat: [
            'concatLibrariesJs'
        ],
        publishDevCss: h.publishStaticFilesTask({
            srcDir: h.cssSrcDir,
            destDir: [
                h.distDevDir + '/stylesheets',
                h.distDevDir + '/en/stylesheets',
                h.distDevDir + '/ja/stylesheets'
            ]
        }),
        publishProductionCss: h.publishStaticFilesTask({
            srcDir: h.cssMinSrcDir,
            destDir: [
                h.distProductionDir + '/stylesheets',
                h.distProductionDir + '/en/stylesheets',
                h.distProductionDir + '/ja/stylesheets'
            ]
        }),
        publishCss: [
            'publishDevCss',
            'publishProductionCss'
        ],
        publishImages: h.publishStaticFilesTask({
            srcDir: h.imgSrcDir,
            destDir: [
                h.distDevDir + '/images',
                h.distDevDir + '/en/images',
                h.distDevDir + '/ja/images',
                h.distProductionDir + '/images',
                h.distProductionDir + '/en/images',
                h.distProductionDir + '/ja/images'
            ]
        }),
        publishResizedImages: h.publishStaticFilesTask({
            srcDir: h.imgResizedSrcDir,
            destDir: [
                h.distDevDir + '/images',
                h.distDevDir + '/en/images',
                h.distDevDir + '/ja/images',
                h.distProductionDir + '/images',
                h.distProductionDir + '/en/images',
                h.distProductionDir + '/ja/images'
            ]
        }),
        publishFonts: h.publishStaticFilesTask({
            srcDir: h.fontFlattenSrcDir,
            destDir: [
                h.distDevDir + '/fonts',
                h.distDevDir + '/en/fonts',
                h.distDevDir + '/ja/fonts',
                h.distProductionDir + '/fonts',
                h.distProductionDir + '/en/fonts',
                h.distProductionDir + '/ja/fonts'
            ]
        }),
        publishDevLibrariesJs: h.publishStaticFilesTask({
            srcDir: h.jsLibrariesSrcDir,
            pattern: 'libraries.js',
            destDir: [
                h.distDevDir + '/javascripts',
                h.distDevDir + '/en/javascripts',
                h.distDevDir + '/ja/javascripts'
            ]
        }),
        publishProductionLibrariesJs: h.publishStaticFilesTask({
            srcDir: h.jsMinSrcDir,
            pattern: 'libraries.min.js',
            destDir: [
                h.distProductionDir + '/javascripts',
                h.distProductionDir + '/en/javascripts',
                h.distProductionDir + '/ja/javascripts'
            ]
        }),
        publishJs: [
            'publishDevLibrariesJs',
            'publishProductionLibrariesJs'
        ],
        publishDevHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlSrcDir + '/en',
            destDir: h.distDevDir,
            pattern: '**/*.html'
        }),
        publishDevEnHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlSrcDir + '/en',
            destDir: h.distDevDir + '/en',
            pattern: '**/*.html'
        }),
        publishDevJaHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlSrcDir + '/ja',
            destDir: h.distDevDir + '/ja',
            pattern: '**/*.html'
        }),
        publishProductionHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlMinSrcDir + '/en',
            destDir: h.distProductionDir,
            pattern: '**/*.min.html',
            ext: '.min.html'
        }),
        publishProductionEnHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlMinSrcDir + '/en',
            destDir: h.distProductionDir + '/en',
            pattern: '**/*.min.html',
            ext: '.min.html'
        }),
        publishProductionJaHtml: h.publishHtmlFilesTask({
            srcDir: h.htmlMinSrcDir + '/ja',
            destDir: h.distProductionDir + '/ja',
            pattern: '**/*.min.html',
            ext: '.min.html'
        }),
        publishHtml: [
            'publishDevHtml',
            'publishDevEnHtml',
            'publishDevJaHtml',
            'publishProductionHtml',
            'publishProductionEnHtml',
            'publishProductionJaHtml'
        ],
        publish: [
            'publishImages',
            'publishResizedImages',
            'publishFonts',
            'publishCss',
            'publishJs',
            'publishHtml'
        ],
        minifySrcCss: h.minifyCssFilesTask({
            srcDir: h.cssSrcDir,
            destDir: h.cssMinSrcDir,
            pattern: '**/*.css'
        }),
        minifyCss: [
            'minifySrcCss'
        ],
        minifySrcJs: h.minifyJsFilesTask({
            srcDir: h.jsSrcDir,
            destDir: h.jsMinSrcDir,
            pattern: '**/*.js'
        }),
        minifySrcLibrariesJs: h.minifyJsFilesTask({
            srcDir: h.jsLibrariesSrcDir,
            destDir: h.jsMinSrcDir,
            pattern: 'libraries.js'
        }),
        minifyJs: [
            'minifySrcJs',
            'minifySrcLibrariesJs'
        ],
        minify: [
            'minifyJs',
            'minifyCss'
        ],
        flattenFontDirectory: h.generateBasenameLinkTask({
            src: h.fontSrcDir + '/**/*' + h.fontsExtPattern,
            linkDir: h.fontFlattenSrcDir
        }),
        flatten: [
            'flattenFontDirectory'
        ],
        generateSizedFavicons: h.generateSizedImageFilesTask({
            src: h.imgSrcDir + '/favicon@apeman.png',
            sizes: [
                h.square(32),
                h.square(48),
                h.square(64),
                h.square(128)
            ],
            dest: h.imgResizedSrcDir + '/favicon-{{width}}x{{height}}.png'
        }),
        generateSizedTouchIcons: h.generateSizedImageFilesTask({
            src: h.imgSrcDir + '/touch-icon@apeman.png',
            //@see https://gist.github.com/tfausak/2222823
            sizes: [
                h.square(57), //iOS 6 iPhone
                h.square(72), //iOS 6 iPad
                h.square(76), //iOS 7 iPad
                h.square(114), //iOS 6 iPhone (retina)
                h.square(120), //iOS 7 iPhone (retina)
                h.square(144), //iOS 6 iPad (retina)
                h.square(152)  //iOS 7 iPad (retina)
            ],
            dest: h.imgResizedSrcDir + '/apple-touch-icon-{{width}}x{{height}}.png'
        }),
        resizeImage: [
            'generateSizedFavicons',
            'generateSizedTouchIcons'
        ],
        resize: [
            'resizeImage'
        ],
        generateFaviconIco: h.generateIcoImageTask({
            src: h.imgResizedSrcDir + '/favicon-32x32.png',
            dest: h.imgSrcDir + '/favicon.ico'
        }),
        convertImage: [
            'generateFaviconIco'
        ],
        convert: [
            'convertImage'
        ],
        generateFaviconThemeImages: h.generateThemeFaviconFilesTask({
            destDir: h.imgThemesSrcDir + '/favicon',
            count: 26,
            rounds: [0, 20, 40]
        }),
        generateStartupThemeImages: h.generateThemeStartupImageFilesTask({
            destDir: h.imgThemesSrcDir + '/startup_image',
            count: 12
        }),
        generateApemanDocReadmeHtml: h.generateHtmlFromMarkdownTask({
            src: apeman.docPath.readme,
            dest: h.htmlSrcPartialsDir + '/apeman-doc-%s.partial.html'
        }),
        generateApemanWikiHtml: h.generateHtmlFromMarkdownTask({
            src: fs.existsSync(apeman.docPath.wikiDir) &&
                fs.readdirSync(apeman.docPath.wikiDir)

                    .filter(function (filename) {
                        return !filename.match(/^\./);
                    })
                    .map(function (filename) {
                        return path.resolve(apeman.docPath.wikiDir, filename);
                    }) ||
                [],
            dest: h.htmlSrcPartialsDir + '/apeman-wiki-%s.partial.html'
        }),
        generateApemanDocHtml: [
            'generateApemanDocReadmeHtml',
            'generateApemanWikiHtml'
        ],
        html: [
            'generateApemanDocHtml'
        ],
        imgThemes: [
            'generateFaviconThemeImages',
            'generateStartupThemeImages'
        ],
        linkThemeImages: h.linkThemeFilesTask({
            srcDir: h.imgThemesSrcDir,
            destDir: h.imgSrcDir,
            settingFile: h.resolveSettingFile('style-theme-setting')
        }),
        linkThemeMixinLess: h.linkThemeFilesTask({
            srcDir: h.lessThemesSrcDir + '/mixin',
            destDir: h.lessSrcDir + '/mixin',
            settingFile: h.resolveSettingFile('style-theme-setting')
        }),
        link: [
            'linkThemeImages',
            'linkThemeMixinLess'
        ],
        generateLessMixinIndex: h.generateIndexLessFileTask({
            dir: h.lessSrcDir + '/mixin'
        }),
        generateLessVariableIndex: h.generateIndexLessFileTask({
            dir: h.lessSrcDir + '/variable'
        }),
        generateLessIndices: [
            'generateLessMixinIndex',
            'generateLessVariableIndex'
        ],
        cleanJadePrecompileDir: h.cleanDirectoryTask({
            dir: h.jadePrecompiledSrcDir
        }),
        clean: prototypeTasks.clean.concat([
            'cleanJadePrecompileDir'
        ]),
        build: prototypeTasks.build.concat([
            'inherit',
            'flatten',
            'clientside',
            'serverside',
            'dataJson',
            'html',
            'link',
            'compile',
            'concat',
            'resize',
            'convert',
            'minify',
            'publish',
        ]),
        generateLibLogIndex: {
            config: {
                capitalize: prototypeTasks.generateLibLogIndex.config.capitalize.concat([
                    '*_logger'
                ])
            }
        },
        generateLocalesIndex: h.generateIndexTask({
            dir: 'locales',
            doc: {
                overview: 'Locale modules',
                namespace: 'locales'
            }
        }),
        generateSettingsIndex: h.generateIndexTask({
            dir: 'settings',
            doc: {
                overview: 'Settings modules',
                namespace: 'settings'
            }
        }),
        index: prototypeTasks.index.concat([
            'generateLocalesIndex',
            'generateSettingsIndex',
            'generateLessIndices'
        ]),
        installNodePackages: {
            config: {
                packages: nodePackages
            }
        },
        installNodeDevPackages: {
            config: {
                packages: nodePackages$dev
            }
        },
        installBowerComponents: h.installBowerComponentsTask({
            destDir: h.jsLibrariesSrcDir + '/bower_components',
            packages: bowerPackages
        }),
        install: prototypeTasks.install.concat([
            'installBowerComponents'
        ]),
        runServersideMiddlewareUnitTest: h.runNodeunitTask({
            files: [ 'test/unit_tests/middleware/**/server/*.js']
        }),
        runServersideRequestUnitTest: h.runNodeunitTask({
            files: [ 'test/unit_tests/serverside/**/request/*.js']
        }),
        runServersideResponseUnitTest: h.runNodeunitTask({
            files: [ 'test/unit_tests/serverside/**/response/*.js']
        }),
        runServersideServerUnitTest: h.runNodeunitTask({
            files: [ 'test/unit_tests/serverside/**/server/*.js']
        }),
        runServersideUnitTest: [
            'runServersideMiddlewareUnitTest',
            'runServersideRequestUnitTest',
            'runServersideResponseUnitTest',
            'runServersideServerUnitTest'
        ],
        unitTest: prototypeTasks.unitTest.concat([
            'runServersideUnitTest'
        ]),
        runBrowserUnitTest: h.runKarmaTestTask({
            karmaConfigFile: 'test/browser_unit_tests/karma.config.js'
        }),
        browserUnitTest: [
            'runBrowserUnitTest'
        ],
        runBatchScenarioTest: h.runMochaTask({
            files: ['test/scenario_tests/batch_scenario/*.js'],
            timeout: 10000
        }),
        runCrawlScenarioTest: h.runMochaTask({
            files: ['test/scenario_tests/crawl_scenario/*.js'],
            timeout: 10000
        }),
        scenarioTest: [
            'runCrawlScenarioTest'
        ],
        test: prototypeTasks.test.concat([
            'browserUnitTest'
        ]),
        generateServersideUnitTestFiles: h.generateUnitTestFilesTask({
            srcDir: 'serverside/',
            destDir: 'test/unit_tests/serverside',
            pattern: '+(batch|context|middleware|request|response|server|logger)/*.js',
            data: {
                testResourcePath: '../../../test_resource',
                mockInjectorPath: '../../../mock_injector'
            }
        }),
        generateUnitTestFiles: prototypeTasks.generateUnitTestFiles.concat([
            'generateServersideUnitTestFiles'
        ]),
        generateScenarioTestFiles: [

        ],
        generateTestFiles: prototypeTasks.generateTestFiles.concat([
            'generateScenarioTestFiles'
        ]),
        generateApiguide: {
            config: {
                src: prototypeTasks.generateApiguide.config.src
                    .map(function (filename) {
                        return path.resolve(path.dirname(prototypePath), filename);
                    })
                    .concat([
                        path.resolve('lib/**/*.js'),
                        path.resolve('task/**/*.js'),
                        path.resolve('README.md'),
                        path.resolve('Apemanfile.js'),
                        path.resolve('_apemanfile_helper.js')
                    ]),
                // Available themes:
                // [ "amelia","cerulean","cosmo","cyborg","flatly","journal","readable",
                //   "simplex","slate","spacelab","spruce","superhero","united"]
                theme: 'cosmo'
            }
        },
        generateStyleguide: h.generateStyleguideTask({
            srcDir: [
                h.lessThemesSrcDir + '/mixin',
                h.lessThemesSrcDir + '/variable/color_scheme',
                h.lessIconsSrcDir,
                h.lessFontsSrcDir
            ],
            themesDataFile: h.resolveTaskDataFile('style-themes-data.js'),
            destDir: h.styleguideDocDir,
            aliases: {
                font: h.fontFlattenSrcDir,
                favicon: h.imgThemesSrcDir + '/favicon',
                startup_image: h.imgThemesSrcDir + '/startup_image'
            }
        }),
        travisEncrypt: h.executeTravisEncryptTask({
            secretFile: h.secretsDir + '/travis-secret.js'
        }),
        travis: [
            'travisEncrypt'
        ],
        styleguide: [
            'generateStyleguide'
        ],
        themesAndStyleguide: [
            'themes',
            'styleguide'
        ],
        doc: prototypeTasks.doc.concat([
            'styleguide'
        ]),
        watchCoffeeToCompile: h.watchForTaskTask({
            dir: [h.coffeeSrcDir, h.coffeeSrcDir + '/*'],
            task: 'compileCoffee'
        }),
        fullCompileJade: [
            'precompileJade',
            'compileJade'
        ],
        watchJadeToCompile: h.watchForTaskTask({
            timeout: 12 * 1000,
            dir: [h.jadeSrcDir, h.jadeSrcDir + '/*'],
            task: ['fullCompileJade']
        }),
        watchLessToCompile: h.watchForTaskTask({
            dir: [h.lessSrcDir, h.lessSrcDir + '/*', h.lessSrcDir + '/*/*'],
            task: 'compileLess'
        }),
        watchToCompile: [
            'watchJadeToCompile',
            'watchLessToCompile',
            'watchCoffeeToCompile'
        ],
        watchToTest: [
        ],
        watch: [
            'watchToCompile',
            'watchToTest'
        ]
    };

    h.libIndexTasks = function (libModuleNames) {
        var tasks = {};
        libModuleNames.forEach(function (name) {
            var taskName = h.camelizeString(['generate', 'lib', name, 'index'].join('_'));
            tasks[taskName] = h.generateIndexTask({
                dir: format('lib/%s', name),
                doc: {
                    overview: format('%s module.', h.capitalizeString(name)),
                    namespace: format('lib.%s', name)
                },
                capitalize: false
            });
        });
        return tasks;
    };

    var libIndexTasks = h.libIndexTasks('compile,html,fetch,image,math,minify,lint,url,zip,uuid'.split(','));
    Object.keys(libIndexTasks).forEach(function (taskName) {
        tasks[taskName] = libIndexTasks[taskName];
        tasks.index.push(taskName);
    });

    var serversideModuleNames = 'batch,context,middleware,request,response,server,logger'.split(',');
    serversideModuleNames.forEach(function (name) {
        var taskName = h.camelizeString(['generate', 'serverside', name, 'index'].join('_'));
        tasks[taskName] = h.generateIndexTask({
            dir: format('serverside/%s', name),
            doc: {
                overview: format('%s module.', h.capitalizeString(name)),
                namespace: format('serverside.%s', name)
            },
            capitalize: (function () {
                switch (name) {
//                    case 'log':
//                        return ['logger'];
                    default:
                        return false;
                }
            })()
        });
        tasks.index.push(taskName);
    });


    //Create a new apeman file object.
    var apemanfile = new Apemanfile(
        prototype
    ).set({
            basedir: basedir,
            prototypePath: prototypePath,
            meta: meta,
            tasks: tasks,
            helper: h
        });
    process.chdir(cwd);

    return  apemanfile;
};
