"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 __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FactoryDatabase = void 0;
const path_1 = __importDefault(require("path"));
const typeorm_1 = require("typeorm");
const Factory_1 = require("./Factory");
const Trial_1 = require("./Trial");
const Source_1 = require("./Source");
const ModList_1 = require("./ModList");
const Dataset_1 = require("./Dataset");
const Logging_1 = require("./Logging");
class FactoryDatabase {
    //static FactoryDB: DataSource;
    static get FactoryDB() {
        if (FactoryDatabase.DefaultSource)
            return FactoryDatabase.SourcesMap.get(FactoryDatabase.DefaultSource);
        else
            return undefined;
    }
    static initialize(datasources = []) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', 'Initializing Factory Database');
            if (datasources.length > 0) {
                // use the specified datasources instead of the default sqlite one
                // first source added is made default
                for (let ds of datasources) {
                    Logging_1.Logging.log('info', `Configuring datasource ${ds.name} (${ds === null || ds === void 0 ? void 0 : ds.host}:${ds === null || ds === void 0 ? void 0 : ds.port})`);
                    yield FactoryDatabase.configureDataSource(ds.name, ds);
                }
            }
            else {
                if (!Factory_1.Factory.factoryDataPath)
                    throw new Error('Initialize Factory first - need paths set up');
                yield FactoryDatabase.configureDataSource('default', {
                    type: 'better-sqlite3',
                    database: path_1.default.join(Factory_1.Factory.factoryDataPath, 'factory-storage', 'factory.db'),
                    statementCacheSize: 500,
                    synchronize: true,
                    entities: [Trial_1.Trial, Source_1.Source, ModList_1.ModList,
                        Dataset_1.GameFlowItemRecord,
                        Dataset_1.GameFlowElectricRecord,
                        Dataset_1.GameFlowCircuitRecord,
                        Dataset_1.GameFlowPollutionRecord,
                        Dataset_1.GameFlowSystemRecord
                    ]
                });
            }
        });
    }
    static setSourceAsDefault(name) {
        if (!FactoryDatabase.SourcesMap.has(name))
            throw new Error(`Datasource ${name} does not exist`);
        FactoryDatabase.DefaultSource = name;
    }
    static configureDataSource(name, datasource) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Configuring datasource ${name}`);
            if (FactoryDatabase.SourcesMap.has(name))
                throw new Error(`Datasource ${name} already exists`);
            let ds = new typeorm_1.DataSource(datasource);
            yield ds.initialize();
            yield ds.synchronize(false);
            FactoryDatabase.SourcesMap.set(name, ds);
            if (!FactoryDatabase.DefaultSource)
                FactoryDatabase.DefaultSource = name;
            Logging_1.Logging.log('info', `Datasource ${name} synchronized`);
        });
    }
    static removeDataSource(name) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Removing datasource ${name}`);
            if (!FactoryDatabase.SourcesMap.has(name))
                throw new Error(`Datasource ${name} does not exist`);
            let ds = FactoryDatabase.SourcesMap.get(name);
            if (ds) {
                yield ds.destroy();
                FactoryDatabase.SourcesMap.delete(name);
                if (FactoryDatabase.DefaultSource == name) {
                    FactoryDatabase.DefaultSource = undefined;
                    // set a new default
                    if (FactoryDatabase.SourcesMap.size > 0)
                        FactoryDatabase.DefaultSource = FactoryDatabase.SourcesMap.keys().next().value;
                }
            }
            Logging_1.Logging.log('info', `Datasource ${name} removed`);
        });
    }
    static checkIfTrialExists(sourceId, length, interval, recordItems = true, recordElectric = false, recordCircuits = true, recordPollution = true, recordSystem = true) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Checking if trial exists for source ${sourceId}`);
            let ret = yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).findOne({
                where: {
                    source: {
                        id: sourceId
                    },
                    length,
                    tickInterval: interval,
                    recordItems,
                    recordElectric,
                    recordCircuits,
                    recordPollution,
                    recordSystem
                }
            });
            if (ret)
                return ret.id;
            else
                return null;
        });
    }
    static checkIfDefaultTrialExists(sourceId) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Checking if default trial exists for source ${sourceId}`);
            let ret = yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).findOne({
                where: {
                    source: {
                        id: sourceId
                    },
                    length: 54000,
                    tickInterval: 300,
                    recordItems: true,
                    recordElectric: true,
                    recordCircuits: true,
                    recordPollution: true,
                    recordSystem: true
                }
            });
            if (ret)
                return ret.id;
            else
                return null;
        });
    }
    static deleteTrialData(trialId) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Deleting trial data for trial ${trialId}`);
            yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowItemRecord).delete({
                trial_id: trialId,
            });
            yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowPollutionRecord).delete({
                trial_id: trialId,
            });
            yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowCircuitRecord).delete({
                trial_id: trialId,
            });
            yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowSystemRecord).delete({
                trial_id: trialId,
            });
            let t = yield FactoryDatabase.loadTrial(trialId, false);
            if (t) {
                t.startedRunAt = null;
                t.startedAt = null;
                t.endedAt = null;
                t.textLogs = [];
                t.metadata = null;
                t.rawSystemText = null;
                t.itemMetadata = null;
                t.pollutionMetadata = null;
                t.stage = 'new';
                return yield FactoryDatabase.saveTrial(t);
            }
        });
    }
    static loadDatasetMetadata(trialId) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Loading dataset metadata for trial ${trialId}`);
            // need to load the trial data, then generate 'generic' summary to return
            if (!trialId)
                throw new Error('Invalid trialId');
            // STOP!!!! Dont actually analyze data here - this is just meant to return info like number of records, etc
            let itemRecords = yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowItemRecord).count({ where: { trial_id: trialId, } });
            let electricRecords = yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowElectricRecord).count({ where: { trial_id: trialId, } });
            let circuitRecords = yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowCircuitRecord).count({ where: { trial_id: trialId, } });
            let pollutionRecords = yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowPollutionRecord).count({ where: { trial_id: trialId, } });
            let systemRecords = yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowSystemRecord).count({ where: { trial_id: trialId, } });
            return {
                item: itemRecords,
                electric: electricRecords,
                circuit: circuitRecords,
                pollution: pollutionRecords,
                system: systemRecords,
            };
        });
    }
    static loadDatasetRecords(trialId, category) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Loading dataset records for trial ${trialId} in category ${category}`);
            switch (category) {
                case 'item':
                    return {
                        data: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowItemRecord).find({
                            where: {
                                trial_id: trialId,
                            }
                        }),
                    };
                case 'electric':
                    return {
                        data: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowElectricRecord).find({
                            where: {
                                trial_id: trialId,
                            }
                        }),
                    };
                case 'circuit':
                    return {
                        data: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowCircuitRecord).find({
                            where: {
                                trial_id: trialId,
                            }
                        }),
                    };
                case 'pollution':
                    return {
                        data: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowPollutionRecord).find({
                            where: {
                                trial_id: trialId,
                            }
                        }),
                    };
                case 'system':
                    return {
                        data: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowSystemRecord).find({
                            where: {
                                trial_id: trialId,
                            }
                        }),
                    };
                case 'all':
                    return {
                        data: {
                            item: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowItemRecord).find({
                                where: {
                                    trial_id: trialId,
                                }
                            }),
                            electric: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowElectricRecord).find({
                                where: {
                                    trial_id: trialId,
                                }
                            }),
                            circuit: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowCircuitRecord).find({
                                where: {
                                    trial_id: trialId,
                                }
                            }),
                            pollution: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowPollutionRecord).find({
                                where: {
                                    trial_id: trialId,
                                }
                            }),
                            system: yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowSystemRecord).find({
                                where: {
                                    trial_id: trialId,
                                }
                            }),
                        }
                    };
            }
            throw new Error('Invalid category - ' + category);
        });
    }
    static listTrials(count = 1000) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).find({
                take: count
            });
        });
    }
    static listSources(count = 1000) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield FactoryDatabase.FactoryDB.getRepository(Source_1.Source).find({
                take: count,
            });
        });
    }
    static loadTrial(id, includeSource = true) {
        return __awaiter(this, void 0, void 0, function* () {
            let t = yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).findOne({
                where: {
                    id
                },
                relations: includeSource ? [
                    'source'
                ] : []
            });
            if (includeSource)
                t.source = yield FactoryDatabase.loadSource(t.source.id, includeSource);
            return t;
        });
    }
    static loadSource(id, includeModList = true) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield FactoryDatabase.FactoryDB.getRepository(Source_1.Source).findOne({
                where: {
                    id
                },
                relations: includeModList ? [
                    'modList',
                ] : []
            });
        });
    }
    static loadModList(id) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield FactoryDatabase.FactoryDB.getRepository(ModList_1.ModList).findOne({
                where: {
                    id
                }
            });
        });
    }
    static deleteTrial(id) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Deleting trial ${id}`);
            yield FactoryDatabase.deleteTrialData(id);
            yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).delete({
                id
            });
        });
    }
    static deleteSource(id) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Deleting source ${id}`);
            yield FactoryDatabase.FactoryDB.getRepository(Source_1.Source).delete({
                id
            });
        });
    }
    static deleteModList(id) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Deleting mod list ${id}`);
            yield FactoryDatabase.FactoryDB.getRepository(ModList_1.ModList).delete({
                id
            });
        });
    }
    static saveTrial(trial, saveExtra = true) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Saving trial ${trial.id}`);
            if (saveExtra && trial.source) {
                yield FactoryDatabase.saveSource(trial.source, true);
            }
            return yield FactoryDatabase.FactoryDB.getRepository(Trial_1.Trial).save(trial);
        });
    }
    static saveSource(source, saveExtra = true) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Saving source ${source.id}`);
            if (saveExtra && source.modList) {
                yield FactoryDatabase.saveModList(source.modList);
            }
            return yield FactoryDatabase.FactoryDB.getRepository(Source_1.Source).save(source);
        });
    }
    static saveModList(modList) {
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Saving mod list ${modList.id}`);
            return yield FactoryDatabase.FactoryDB.getRepository(ModList_1.ModList).save(modList);
        });
    }
    static insertItemRecords(records) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            // chunk size of 1000 to start
            Logging_1.Logging.log('info', `Inserting ${records.length} item records for trial ${(_a = records[0]) === null || _a === void 0 ? void 0 : _a.trial_id}`);
            for (let i = 0; i < records.length; i += 1000) {
                yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowItemRecord).insert(records.slice(i, i + 1000));
            }
        });
    }
    static insertElectricRecords(records) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Inserting ${records.length} electric records for trial ${(_a = records[0]) === null || _a === void 0 ? void 0 : _a.trial_id}`);
            for (let i = 0; i < records.length; i += 1000) {
                yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowElectricRecord).insert(records.slice(i, i + 1000));
            }
        });
    }
    static insertCircuitRecords(records) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Inserting ${records.length} circuit records for trial ${(_a = records[0]) === null || _a === void 0 ? void 0 : _a.trial_id}`);
            for (let i = 0; i < records.length; i += 1000) {
                yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowCircuitRecord).insert(records.slice(i, i + 1000));
            }
        });
    }
    static insertPollutionRecords(records) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Inserting ${records.length} pollution records for trial ${(_a = records[0]) === null || _a === void 0 ? void 0 : _a.trial_id}`);
            for (let i = 0; i < records.length; i += 1000) {
                yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowPollutionRecord).insert(records.slice(i, i + 1000));
            }
        });
    }
    static insertSystemRecords(records) {
        var _a;
        return __awaiter(this, void 0, void 0, function* () {
            Logging_1.Logging.log('info', `Inserting ${records.length} system records for trial ${(_a = records[0]) === null || _a === void 0 ? void 0 : _a.trial_id}`);
            for (let i = 0; i < records.length; i += 1000) {
                yield FactoryDatabase.FactoryDB.getRepository(Dataset_1.GameFlowSystemRecord).insert(records.slice(i, i + 1000));
            }
        });
    }
    static findLargestTrialOfSource(sourceId) {
        return __awaiter(this, void 0, void 0, function* () {
            const q = 'select id,\n' +
                '       "endedAt",\n' +
                '       "recordItems",\n' +
                '       "recordElectric",\n' +
                '       "recordCircuits",\n' +
                '       "recordSystem",\n' +
                '       (length / trial."tickInterval") as "tickDataPoints"\n' +
                'from trial\n' +
                'where "sourceId" = $1\n' +
                'and stage = \'analyzed\'\n' +
                'order by "tickDataPoints" desc, "endedAt" desc\n' +
                'LIMIT 1';
            const r = yield FactoryDatabase.FactoryDB.query(q, [sourceId]);
            if (r && r.length == 1)
                return r[0];
            else
                throw new Error('No trial has been run for this source');
        });
    }
    static findStatusAndTrialOfExecution(execId) {
        return __awaiter(this, void 0, void 0, function* () {
            const q = 'select fs.*, fr.trial_id, fr.allocated_at\n' +
                'from public.factory_status fs\n' +
                '         inner join public.factory_request fr ON (fr.execution_id = CAST(fs.execution_id as uuid))\n' +
                'where fs.execution_id = $1 ';
            const r = yield FactoryDatabase.FactoryDB.query(q, [execId]);
            if (r && r.length == 1)
                return r[0];
            else
                throw new Error('No trial has been run for this source');
        });
    }
}
exports.FactoryDatabase = FactoryDatabase;
FactoryDatabase.SourcesMap = new Map();
//# sourceMappingURL=FactoryDatabase.js.map