"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
var __importStar = (this && this.__importStar) || function (mod) {
    if (mod && mod.__esModule) return mod;
    var result = {};
    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
    result["default"] = mod;
    return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const Utils = __importStar(require("../../util/src"));
const Path = __importStar(require("path"));
const FS = __importStar(require("fs"));
const Store = __importStar(require("./store"));
const urlencode_1 = __importStar(require("urlencode")), Urlencode = urlencode_1;
const EsAST = __importStar(require("@babel/parser"));
const Http = __importStar(require("http"));
const UrlQuery = __importStar(require("query-string"));
class FTPBridge {
    constructor() {
        this.server = Http.createServer();
        this.port = 9090;
    }
    run() {
        return __awaiter(this, void 0, void 0, function* () {
            yield Utils.Net.cleanPort(this.port);
            this.server.on('request', (request, response) => {
                Utils.Console.info(`receive request : ${request.url}`);
                const queryinfo = UrlQuery.parseUrl(`${request.url}`);
                if (queryinfo.url === '/api') {
                    switch (queryinfo.query.action) {
                        case 'file':
                            const path = `${queryinfo.query.path}`;
                            if (FS.existsSync(path)) {
                                response.statusCode = 200;
                                response.end(FS.readFileSync(path));
                            }
                            else {
                                response.statusCode = 201;
                                response.end();
                            }
                            break;
                        default:
                            response.statusCode = 404;
                            response.end();
                            break;
                    }
                }
                else {
                    response.statusCode = 404;
                    response.end();
                }
                Utils.Console.info(`post response : ${response.statusCode}`);
            });
            return new Promise((resolve, reject) => {
                this.server.listen(this.port, () => {
                    Utils.Console.info(`${Utils.ACUtils.localIP()}:${this.port}`);
                    resolve();
                });
            });
        });
    }
    fillFileRemoteUrl(filePath) {
        let result = `http://${Utils.ACUtils.localIP()}:${this.port}/api?action=file`;
        result = `${result}&path=${Urlencode.encode(filePath)}`;
        result = `${result}&stamp=${(new Date()).getTime()}`;
        return result;
    }
}
var Build;
(function (Build) {
    Build.ftp = new FTPBridge();
    function allCases(path) {
        return __awaiter(this, void 0, void 0, function* () {
            const cost = new Utils.Cost();
            const cases = new Array();
            const projects = Store.pickProjects(path);
            Utils.Console.info(`扫描可识别的工程（${projects.length}个）[${path}]`);
            let index = 1;
            let failured = 0;
            for (const root of projects) {
                Utils.Console.info(`开始构造用例[${index}/${projects.length}]`);
                index++;
                try {
                    const ptype = Store.projectType(root);
                    if (ptype === Store.ProjectType.Vue) {
                        const acase = yield vueCase(root);
                        cases.push(...acase);
                    }
                    else if (ptype === Store.ProjectType.Tiny) {
                        const acase = yield tinyCase(root);
                        cases.push(...acase);
                    }
                    else if (ptype === Store.ProjectType.Falcon) {
                        const acase = yield falconCase(root);
                        cases.push(...acase);
                    }
                    else {
                        failured++;
                    }
                }
                catch (error) {
                    Utils.Console.error(`构造用例异常: ${error} [${root}]`);
                    failured++;
                    continue;
                }
            }
            Utils.Console.info(`共成功构造用例${cases.length}个，失败${failured}个 [${path}]`);
            cost.mark();
            return Promise.resolve(cases);
        });
    }
    Build.allCases = allCases;
    function vueCase(path) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!FS.existsSync(Path.join(path, 'dist/compress/mQAppId.zip'))) {
                return Promise.reject(Error(`未找到可提取的cube编译产物`));
            }
            const cases = new Array();
            Utils.Console.info(`提取应用配置 [${Path.join(path, 'dist/app.manifest')}]`);
            const manifest = FS.existsSync(Path.join(path, 'dist/app.manifest')) ? JSON.parse(FS.readFileSync(Path.join(path, 'dist/app.manifest')).toString()) : {};
            Utils.Console.info(`提取用例配置 [${Path.join(path, 'rubik.json')}]`);
            const rbk = FS.existsSync(Path.join(path, 'rubik.json')) ? JSON.parse(FS.readFileSync(Path.join(path, 'rubik.json')).toString()) : {};
            let index = 1;
            for (const page of manifest.pages) {
                Utils.Console.info(`构建用例配置 [${page}] [${index}/${manifest.pages.length}]`);
                let acase = new Object(undefined);
                //  优先使用rubik.json内配置的字段
                if (rbk.case) {
                    acase = Object.assign({}, rbk.case);
                }
                //  追加默认处理
                if (!acase.poles) {
                    acase.poles = 'snapshot'; // 默认桩点
                }
                if (!acase.ID) {
                    acase.ID = `vue_${acase.poles}_${manifest.id}_${page}`;
                }
                else {
                    acase.ID = `vue_${acase.poles}_${acase.ID}_${page}`;
                }
                acase.ID = Urlencode.encode(acase.ID);
                if (!acase.type) {
                    acase.type = "vue";
                }
                if (!acase.subtype) {
                    acase.subtype = 'only_paint_check';
                }
                acase.iosScheme = `rubik:/launch?type=vue&dist=${urlencode_1.default(Build.ftp.fillFileRemoteUrl(Path.join(path, 'dist/compress/mQAppId.zip')))}&path=${Urlencode.encode(page)}`;
                Utils.Console.info(`创建用例配置:\n${JSON.stringify(acase, undefined, 2)}`);
                cases.push(acase);
                index++;
            }
            return Promise.resolve(cases);
        });
    }
    function falconCase(path) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!FS.existsSync(Path.join(path, 'dist'))) {
                return Promise.reject(Error(`未找到可提取的cube编译产物`));
            }
            const cases = new Array();
            Utils.Console.info(`提取工程配置 [${Path.join(path, 'package.json')}]`);
            const pkg = JSON.parse(FS.readFileSync(Path.join(path, 'package.json')).toString());
            Utils.Console.info(`提取用例配置 [${Path.join(path, 'rubik.json')}]`);
            const rbk = FS.existsSync(Path.join(path, 'rubik.json')) ? JSON.parse(FS.readFileSync(Path.join(path, 'rubik.json')).toString()) : {};
            Utils.Console.info(`清理用例构建产物 [${Path.join(path, 'dist/_rubik')}]`);
            const casePath = Path.join(path, 'dist/_rubik');
            Utils.FS.rmdirs(casePath);
            Utils.FS.mkdirs(casePath);
            let index = 1;
            Utils.FS.walk(Path.join(path, 'dist'), (root, name, stats) => {
                if (Path.extname(name) === '.bin') {
                    Utils.Console.info(`构建页面用例 [${root}] [${index}]`);
                    let manifest = JSON.parse(FS.readFileSync(Path.join(root, 'main.json')).toString());
                    manifest = JSON.parse(manifest.meta);
                    let acase = new Object(undefined);
                    //  优先使用rubik.json内配置的字段
                    if (rbk.case) {
                        acase = Object.assign({}, rbk.case);
                    }
                    //  追加默认处理
                    if (!acase.poles) {
                        acase.poles = 'snapshot'; // 默认桩点
                    }
                    if (!acase.ID) {
                        acase.ID = `falcon_${acase.poles}_${pkg.name}_${manifest.name}`;
                    }
                    else {
                        acase.ID = `falcon_${acase.poles}_${acase.ID}_${manifest.name}`;
                    }
                    acase.ID = Urlencode.encode(acase.ID);
                    if (!acase.type) {
                        acase.type = "falcon";
                    }
                    if (!acase.subtype) {
                        acase.subtype = 'only_paint_check';
                    }
                    Utils.FS.copy(root, Path.join(casePath, `${acase.ID}/dist`));
                    Utils.FS.copy(Path.join(path, 'package.json'), Path.join(casePath, `${acase.ID}/app.manifest`));
                    cases.push(acase);
                    index++;
                }
            });
            for (const acase of cases) {
                const distPath = Path.join(casePath, `${acase.ID}.zip`);
                yield Utils.FS.zip(Path.join(casePath, acase.ID), distPath);
                Utils.FS.rmdirs(Path.join(casePath, acase.ID));
                acase.iosScheme = `rubik:/launch?type=falcon&dist=${Urlencode.encode(Build.ftp.fillFileRemoteUrl(distPath))}`;
                Utils.Console.info(`创建用例配置:\n${JSON.stringify(acase, undefined, 2)}`);
            }
            return Promise.resolve(cases);
        });
    }
    function tinyCase(path) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!FS.existsSync(Path.join(path, 'dist/native'))) {
                return Promise.reject(Error(`未找到可提取的cube编译产物`));
            }
            const cases = new Array();
            Utils.Console.info(`提取工程配置 [${Path.join(path, 'dist/package/package.json')}]`);
            const pkg = JSON.parse(FS.readFileSync(Path.join(path, 'dist/package/package.json')).toString());
            Utils.Console.info(`提取应用配置 [${Path.join(path, 'dist/native/appConfig.json')}]`);
            const app = JSON.parse(FS.readFileSync(Path.join(path, 'dist/native/appConfig.json')).toString());
            Utils.Console.info(`提取用例配置 [${Path.join(path, 'rubik.json')}]`);
            const rbk = FS.existsSync(Path.join(path, 'rubik.json')) ? JSON.parse(FS.readFileSync(Path.join(path, 'rubik.json')).toString()) : {};
            let index = 1;
            const srcPath = Path.join(path, `dist/_rubik.zip`);
            Utils.FS.rmdirs(srcPath);
            yield Utils.FS.zip(Path.join(path, `dist/native`), srcPath);
            for (const page of app.pages) {
                Utils.Console.info(`构建页面用例 [${page}] [${index}/${app.pages.length}]`);
                let acase = new Object(undefined);
                //  优先使用rubik.json内配置的字段
                if (rbk.case) {
                    acase = Object.assign({}, rbk.case);
                }
                //  追加默认处理
                if (!acase.poles) {
                    acase.poles = 'snapshot'; // 默认桩点
                }
                if (!acase.ID) {
                    acase.ID = `tiny_${acase.poles}_${pkg.name}_${page}`;
                }
                else {
                    acase.ID = `tiny_${acase.poles}_${acase.ID}_${page}`;
                }
                acase.ID = Urlencode.encode(acase.ID);
                if (!acase.type) {
                    acase.type = "appx";
                }
                if (!acase.subtype) {
                    acase.subtype = 'only_paint_check';
                }
                acase.iosScheme = `rubik:/launch?type=appx&dist=${Urlencode.encode(Build.ftp.fillFileRemoteUrl(srcPath))}&path=${Urlencode.encode(page)}`;
                Utils.Console.info(`创建用例配置:\n${JSON.stringify(acase, undefined, 2)}`);
                cases.push(acase);
                index++;
            }
            return Promise.resolve(cases);
        });
    }
})(Build = exports.Build || (exports.Build = {}));
/*
    规范DEMO用例集 http://gitlab.alipay-inc.com/yousai.lys/test_cube/tree/test/cssSupports
    
    DEMO中每个可操作对功能页均支持参数切换，参数的配置规则有两类:
    - [page].js 内 { data: { values: [], currentStyle: "" } }
    - [page].js 内 { data: { styleList: [], currentStyle: "" } }

    拆分方案 :
    - 将配置参数逐条构成新的功能页面
    - currentStyle 初始值设置为当前配置参数
    - 更新功能 app.json 内pages字段（过滤掉已拆分的原页面）
*/
var TinySuit;
(function (TinySuit) {
    const pickers = Object({
        File: pick4File,
        Program: pick4program,
        ExpressionStatement: pick4ExpressionStatement,
        CallExpression: pick4CallExpression,
        ObjectExpression: pick4ObjectExpression,
        ObjectProperty: pick4ObjectProperty,
    });
    const encoders = Object({
        ObjectProperty: encode4ObjectProperty,
        ObjectExpression: encode4ObjectExpression,
        Identifier: encode4Identifier,
        StringLiteral: encode4StringLiteral,
        ArrayExpression: encode4ArrayExpression,
    });
    function split(path, dist = Path.join(process.env.HOME ? process.env.HOME : '', 'Desktop')) {
        const cost = new Utils.Cost();
        let count = 0;
        if (FS.statSync(path).isDirectory()) {
            dist = Path.join(dist, Path.basename(path));
            Utils.FS.rmdirs(dist);
            Utils.FS.mkdirs(dist);
            Utils.FS.copy(path, dist);
            Utils.FS.rmdirs(Path.join(dist, 'src/pages'));
            const app = JSON.parse(FS.readFileSync(Path.join(dist, 'src/app.json')).toString());
            app.pages = [];
            FS.writeFileSync(Path.join(dist, 'src/app.json'), JSON.stringify(app, undefined, 2));
            const subcaseRoot = Path.join(dist, 'src/pages/__rubik');
            Utils.FS.mkdirs(subcaseRoot);
            const saveSubpage = (originRoot, originName, newName, js) => {
                Utils.FS.copy(Path.join(originRoot, `${originName}.axml`), Path.join(subcaseRoot, `${newName}/${newName}.axml`));
                Utils.FS.copy(Path.join(originRoot, `${originName}.acss`), Path.join(subcaseRoot, `${newName}/${newName}.acss`));
                Utils.FS.copy(Path.join(originRoot, `${originName}.json`), Path.join(subcaseRoot, `${newName}/${newName}.json`));
                FS.writeFileSync(Path.join(subcaseRoot, `${newName}/${newName}.js`), js);
                const app = JSON.parse(FS.readFileSync(Path.join(dist, 'src/app.json')).toString());
                if (!app.pages) {
                    app.pages = [];
                }
                app.pages = [...app.pages, `pages/__rubik/${newName}/${newName}`];
                FS.writeFileSync(Path.join(dist, 'src/app.json'), JSON.stringify(app, undefined, 2));
                Utils.Console.info(`拆分出子用例 ${newName}`);
                count++;
            };
            Utils.FS.walk(Path.join(path, 'src'), (root, name, stats) => {
                if (stats.isFile() && Path.extname(name) === '.axml') {
                    const pageName = name.split('.axml').join('');
                    const js = FS.readFileSync(Path.join(root, `${pageName}.js`)).toString();
                    const ast = EsAST.parse(js);
                    const dataNode = pick(ast, {
                        keyType: 'Identifier',
                        keyName: 'data'
                    });
                    const dataInfo = encode(dataNode);
                    const data = dataInfo.data;
                    //  values + currentStyle
                    if (data.values && data.currentStyle !== undefined) {
                        const currentStyleNode = pick(dataNode, {
                            keyType: 'Identifier',
                            keyName: 'currentStyle'
                        });
                        let index = 0;
                        for (const avalue of data.values) {
                            const subjs = `${js.substring(0, currentStyleNode.value.start)}\`${data.property}: ${avalue};\`${js.substring(currentStyleNode.value.end)}`;
                            saveSubpage(root, pageName, `${pageName}_values_${index}`, subjs);
                            index++;
                        }
                    }
                    //  styleList + currentStyle
                    if (data.styleList && data.currentStyle !== undefined) {
                        const currentStyleNode = pick(dataNode, {
                            keyType: 'Identifier',
                            keyName: 'currentStyle'
                        });
                        let index = 0;
                        for (const astyle of data.styleList) {
                            const subdata = Object.assign({}, data);
                            subdata.currentStyle = `${astyle}`;
                            const subjs = `${js.substring(0, currentStyleNode.value.start)}\`${astyle};\`${js.substring(currentStyleNode.value.end)}`;
                            saveSubpage(root, pageName, `${pageName}_styleList_${index}`, subjs);
                            index++;
                        }
                    }
                    //  demoList
                    if (data.demoList && data.currentDemo !== undefined && data.currentNote !== undefined) {
                        for (const ademo of data.demoList) {
                            const subdata = Object.assign({}, data);
                            subdata.currentDemo = ademo.name;
                            subdata.currentNode = ademo.note;
                            //  TODO
                        }
                    }
                }
            });
        }
        Utils.Console.info(`总计拆分出总用例 [${count}] 个`);
        cost.mark();
        return Promise.resolve();
    }
    TinySuit.split = split;
    //  pick ...
    function pick(node, options) {
        const call = pickers[`${node.type}`];
        if (call) {
            return call(node, options);
        }
        return undefined;
    }
    function pick4File(node, options) {
        return pick(node.program, options);
    }
    function pick4program(node, options) {
        let result = undefined;
        for (const abody of node.body) {
            result = pick(abody, options);
            if (result) {
                break;
            }
        }
        return result;
    }
    function pick4ExpressionStatement(node, options) {
        return pick(node.expression, options);
    }
    function pick4CallExpression(node, options) {
        let result = undefined;
        for (const aargu of node.arguments) {
            result = pick(aargu, options);
            if (result) {
                break;
            }
        }
        return result;
    }
    function pick4ObjectExpression(node, options) {
        let result = undefined;
        for (const aprop of node.properties) {
            result = pick(aprop, options);
            if (result) {
                break;
            }
        }
        return result;
    }
    function pick4ObjectProperty(node, options) {
        if (node.key && node.key.type === options.keyType && node.key.name === options.keyName) {
            return node;
        }
        return pick(node.value, options);
    } // encode ...
    function encode(node) {
        const call = encoders[`${node.type}`];
        if (call) {
            return call(node);
        }
        return undefined;
    }
    function encode4ObjectProperty(node) {
        if (node.key && node.key.type === 'Identifier' && node.key.name === 'data') {
            return { data: encode(node.value) };
        }
        return undefined;
    }
    function encode4ObjectExpression(node) {
        const result = Object(undefined);
        for (const aprop of node.properties) {
            if (aprop.type === 'ObjectProperty') {
                const key = encode(aprop.key);
                const value = encode(aprop.value);
                if (key) {
                    result[`${key}`] = value;
                }
            }
        }
        return result;
    }
    function encode4Identifier(node) {
        return node.name;
    }
    function encode4StringLiteral(node) {
        return node.value;
    }
    function encode4ArrayExpression(node) {
        const result = new Array();
        for (const aelement of node.elements) {
            result.push(encode(aelement));
        }
        return result;
    }
})(TinySuit = exports.TinySuit || (exports.TinySuit = {}));
//# sourceMappingURL=build.js.map