/*
* License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt
*/
import createSagaMiddleware from 'redux-saga';
import thunkMiddleware from 'redux-thunk';
import {createLogger} from 'redux-logger';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import {masterSaga, dispatchAddSaga} from './MasterSaga.js';
import AppDataCntlr from './AppDataCntlr.js';
import BackgroundCntlr from './background/BackgroundCntlr.js';
import * as LayoutCntlr from './LayoutCntlr.js';
import {recordHistory} from './History.js';
import FieldGroupCntlr, {MOUNT_COMPONENT} from '../fieldGroup/FieldGroupCntlr.js';
import MouseReadoutCntlr from '../visualize/MouseReadoutCntlr.js';
import ImagePlotCntlr from '../visualize/ImagePlotCntlr.js';
import ExternalAccessCntlr from './ExternalAccessCntlr.js';
import * as TableStatsCntlr from '../charts/TableStatsCntlr.js';
import ChartsCntlrDef from '../charts/ChartsCntlr.js';
import TablesCntlr from '../tables/TablesCntlr';
import WorkspaceCntlr from '../visualize/WorkspaceCntlr.js';
import DrawLayerFactory from '../visualize/draw/DrawLayerFactory.js';
import DrawLayerCntlr from '../visualize/DrawLayerCntlr.js';
import MultiViewCntlr from '../visualize/MultiViewCntlr.js';
import ComponentCntlr, {DIALOG_OR_COMPONENT_KEY} from '../core/ComponentCntlr.js';
//--- import Sagas
import {imagePlotter} from '../visualize/saga/ImagePlotter.js';
import {watchReadout} from '../visualize/saga/MouseReadoutWatch.js';
import {watchForRelatedActions} from '../fieldGroup/FieldGroupCntlr.js';
import {watchExtensionActions} from '../core/messaging/ExternalAccessWatcher.js';
//--- import drawing Layers
// import ActiveTarget from '../drawingLayers/ActiveTarget.js';
import FixedMarker from '../drawingLayers/FixedMarker.js';
import SelectArea from '../drawingLayers/SelectArea.js';
import DistanceTool from '../drawingLayers/DistanceTool.js';
import PointSelection from '../drawingLayers/PointSelection.js';
import StatsPoint from '../drawingLayers/StatsPoint.js';
import NorthUpCompass from '../drawingLayers/NorthUpCompass.js';
import Catalog from '../drawingLayers/Catalog.js';
import Artifact from '../drawingLayers/Artifact.js';
import WebGrid from '../drawingLayers/WebGrid.js';
import HiPSGrid from '../drawingLayers/HiPSGrid.js';
import HiPSMOC from '../drawingLayers/HiPSMOC.js';
import RegionPlot from '../drawingLayers/RegionPlot.js';
import MarkerTool from '../drawingLayers/MarkerTool.js';
import FootprintTool from '../drawingLayers/FootprintTool.js';
import ImageOutline from '../drawingLayers/ImageOutline.js';
import ImageRoot from '../drawingLayers/ImageRoot.js';
import {showExampleDialog} from '../ui/ExampleDialog.jsx';
import ImageLineBasedFootprint from '../drawingLayers/ImageLineBasedFootprint.js';
/**
* @global
* @public
* @typedef {Object} ApplicationState
*
* @prop {VisRoot} allPlots - image plotting store (Controller: ImagePlotCntlr.js)
* @prop {TableSpace} table_space - table data store (Controller: TablesCntlr.js)
* @prop {Object} charts - information about 2D plots (Controller: ChartsCntlr.js)
* @prop {FieldGroupStore} fieldGroup - field group data for form and dialog input (Controller: FieldGroupCntlr.js)
* @prop {Object} readout - mouse readout information (Controller: ReadoutCntlr.js)
* @prop {AppDataStore} app_data - general application information (Controller: AppDataCntlr.js)
* @prop {Object} drawLayers - information about the drawing layers e.g. select tool, catalogs overlays, regions, etc
* @prop {Viewer} imageMultiView - data about the various image viewers (Controller: MultiViewCntlr.js)
* @prop {Object} externalAccess - controls communication events with eternal applications (Controller: ExternalAccessCntlr.js)
* @prop {Object} layout - information about application layout (Controller: LayoutCntlr.js)
* @prop {Object} tblstats - stats for histogram, etc (Controller: TableStatsCntlr.js)
* @prop {Object} dialogOrComponent - hold information about dialog visibility and other components (Controller: ComponentCntlr.js)
*
*/
/**
* @typedef {Object} Action
* @prop {String} type - the action constant, a unique string identifying this action
* @prop {Object} payload - object with anything, the data
* @global
* @public
*/
/**
* A map to rawAction.type to an ActionCreator
* @type {Map<string, function>}
*/
const actionCreators = new Map();
const drawLayerFactory= DrawLayerFactory.makeFactory(FixedMarker, SelectArea,DistanceTool,
PointSelection, StatsPoint, NorthUpCompass, ImageRoot,
Catalog, Artifact, WebGrid, RegionPlot,
MarkerTool, FootprintTool, HiPSGrid, HiPSMOC,
ImageOutline, ImageLineBasedFootprint);
/**
* A collection of reducers keyed by the node's name under the root.
* @type {Object<string, function>}
*/
const reducers = {
[LayoutCntlr.LAYOUT_PATH]: LayoutCntlr.reducer,
[ExternalAccessCntlr.EXTERNAL_ACCESS_KEY]: ExternalAccessCntlr.reducer,
[TableStatsCntlr.TBLSTATS_DATA_KEY]: TableStatsCntlr.reducer,
[DIALOG_OR_COMPONENT_KEY]: ComponentCntlr.reducer
};
function registerCntlr(cntlr={}) {
cntlr.reducers && Object.entries(cntlr.reducers()).forEach(([k,v]) => reducers[k]= v);
cntlr.actionCreators && Object.entries(cntlr.actionCreators()).forEach(([k,v]) => actionCreators.set(k,v));
}
// registering controllers...
registerCntlr(AppDataCntlr);
registerCntlr(BackgroundCntlr);
registerCntlr(ImagePlotCntlr);
registerCntlr(FieldGroupCntlr);
registerCntlr(MouseReadoutCntlr);
registerCntlr(ExternalAccessCntlr);
registerCntlr(TablesCntlr);
registerCntlr(DrawLayerCntlr.getDrawLayerCntlrDef(drawLayerFactory));
registerCntlr(ChartsCntlrDef);
registerCntlr(MultiViewCntlr);
registerCntlr(WorkspaceCntlr);
let redux = null;
// pre-map a set of action => creator prior to bootstrapping.
actionCreators.set(TableStatsCntlr.LOAD_TBL_STATS, TableStatsCntlr.loadTblStats);
actionCreators.set('exampleDialog', (rawAction) => {
showExampleDialog();
return rawAction;
});
/**
* object with a key that can be filtered out, value should be a boolean or a function that returns a boolean
*/
// eslint-disable-next-line
var filterOutOfLogging= {
[ExternalAccessCntlr.EXTENSION_ACTIVATE]: (action) => !action.payload.extension || action.payload.extension.extType!=='PLOT_MOUSE_READ_OUT',
[MOUNT_COMPONENT]: false
};
/**
* array of action types that will be logged as collapsed
*/
var collapsedLogging= [
ExternalAccessCntlr.EXTENSION_ACTIVATE
];
window.enableFireflyReduxLogging= false;
/**
* Can be used for debugging. Adjust content of filter function to suit your needs
* @param getState
* @param action
* @return {boolean}
*/
// function logFilter(getState,action) {
// const {type}= action;
// if (!type) return false;
// if (type.startsWith('VisMouseCntlr')) return false;
// if (type.startsWith('EFFECT')) return false;
// if (type.startsWith('FieldGroupCntlr')) return false;
// if (type.startsWith('layout')) return false;
// if (type.startsWith('table_space')) return false;
// if (type.startsWith('tblstats')) return false;
// if (type.startsWith('table_ui')) return false;
// if (type.startsWith('app_data')) return false;
// if (type.startsWith('ReadoutCntlr')) return false;
// return window.enableFireflyReduxLogging;
// }
function logFilter(getState,action) {
return window.enableFireflyReduxLogging;
}
function collapsedFilter(getState,action) {
return collapsedLogging.includes(action.type);
}
// eslint-disable-next-line
const logger= createLogger({duration:true, predicate:logFilter, collapsed:collapsedFilter}); // developer can add for debugging
function createRedux() {
// create a rootReducer from all of the registered reducers
const rootReducer = combineReducers(reducers);
const sagaMiddleware = createSagaMiddleware();
const middleWare= applyMiddleware(thunkMiddleware, logger, sagaMiddleware); //todo: turn off action logging
const store = createStore(rootReducer, middleWare);
sagaMiddleware.run(masterSaga);
return store;
}
function startCoreSagas() {
dispatchAddSaga( imagePlotter);
dispatchAddSaga( watchReadout);
dispatchAddSaga( watchForRelatedActions);
dispatchAddSaga( watchExtensionActions);
}
function bootstrap() {
if (redux === null) {
redux = createRedux();
startCoreSagas();
}
return Promise.resolve('success');
}
/**
* Process the rawAction. This uses the actionCreators map to resolve
* the ActionCreator given the action.type. If one is not mapped, then it'll
* create a simple 'pass through' ActionCreator that returns the rawAction as an action.
*
* <i>Note: </i> Often it makes sense to have a utility function call <code>process</code>. In that case
* the utility function should meet the follow criteria. This is a good way to document and default the
* payload parameters. The utility function should implement the following standard:
* <ul>
* <li>The function name should start with "dispatch"</li>
* <li>The action type as the second part of the name</li>
* <li>The function should be exported from the controller</li>
* <li>The function parameters should the documented with jsdocs</li>
* <li>Optional parameters should be clear</li>
* </ul>
* Utility function Example - if action type is <code>PLOT_IMAGE</code> and the <code>PLOT_IMAGE</code> action
* is exported from the ImagePlotCntlr module. The the name should be <code>processPlotImage</code>.
*
*
* @param {Action} rawAction
* @returns {Promise}
*/
function process(rawAction) {
if (!redux) throw Error('firefly has not been bootstrapped');
var ac = actionCreators.get(rawAction.type);
if (!rawAction.payload) rawAction= Object.assign({},rawAction,{payload:{}});
if (ac) {
redux.dispatch(ac(rawAction));
} else {
redux.dispatch( rawAction );
}
recordHistory(rawAction);
}
function addListener(listener, ...types) {
if (!redux) return;
if (types.length) {
return () => {
var appState = redux.getState();
};
} else {
return redux.subscribe(listener);
}
}
function registerCreator(actionCreator, ...types) {
if (types) {
types.forEach( (v) => actionCreators.set(v, actionCreator) );
}
}
function registerReducer(dataRoot, reducer) {
reducers[dataRoot] = reducer;
}
function getState() {
return redux ? redux.getState() : null;
}
function getRedux() {
return redux;
}
function getDrawLayerFactory() {
return drawLayerFactory;
}
function registerDrawLayer(factoryDef) {
drawLayerFactory.register(factoryDef);
}
function setDrawLayerDefaults(typeId,defaults) {
drawLayerFactory.setDrawLayerDefaults(typeId,defaults);
}
function createDrawLayer(drawLayerTypeId, params) {
return drawLayerFactory.create(drawLayerTypeId,params);
}
export var reduxFlux = {
registerCreator,
registerReducer,
bootstrap,
getState,
process,
addListener,
registerDrawLayer,
createDrawLayer,
getDrawLayerFactory,
setDrawLayerDefaults,
getRedux
};