import { reducerWithInitialState } from 'typescript-fsa-reducers';
import { Reducer } from 'redux';
import {
    init,
    updateNode,
    addNode,
    nodeSelected,
    nodeDeselected,
    removeNode,
    removeLink
} from '../actions/diagram';
import { BaseNodeModel, DiagramModel, LinkModel } from 'react-gojs';

export interface DiagramState {
    model: DiagramModel<NodeModel, LinkModel>;
    selectedNodeKeys: string[];
}

export interface NodeModel extends BaseNodeModel {
    color: string;
    text:string;
    category:string;
    tag:any|null;
}

const initHandler = (state: DiagramState, payload: DiagramModel<NodeModel, LinkModel>): DiagramState => {
    return {
        ...state,
        model: payload
    };
};

const updateNodeHandler = (state: DiagramState,payload:NodeModel): DiagramState => {
    const nodeToRemoveIndex = state.model.nodeDataArray.findIndex(node => node.key === payload.key);
    if (nodeToRemoveIndex === -1) {
        return state;
    }
    const updatedNode = state.model.nodeDataArray[nodeToRemoveIndex];
    updatedNode.category =  payload.category;
    updatedNode.key =  payload.key;
    updatedNode.color =  payload.color;
    updatedNode.tag =  payload.tag;
    updatedNode.text =  payload.text;

    return {
        ...state,
        model: {
            ...state.model,
            nodeDataArray: state.model.nodeDataArray
        }
    };
};

const addNodeHandler = (state: DiagramState, payload: DiagramModel<NodeModel, LinkModel>): DiagramState => {
    const linksToAdd: LinkModel[] = state.selectedNodeKeys.map(parent => {
        return { from: parent, to: payload.nodeDataArray[0].key};
    });
    return {
        ...state,
        model: {
            ...state.model,
            nodeDataArray: [...state.model.nodeDataArray, payload.nodeDataArray[0]],
            linkDataArray:
                linksToAdd.length > 0
                    ? [...state.model.linkDataArray].concat(linksToAdd)
                    : [...state.model.linkDataArray]
        }
    };
};

const removeNodeHandler = (state: DiagramState, payload: string): DiagramState => {
    const nodeToRemoveIndex = state.model.nodeDataArray.findIndex(node => node.key === payload);
    if (nodeToRemoveIndex === -1) {
        return state;
    }
    return {
        ...state,
        model: {
            ...state.model,
            nodeDataArray: [
                ...state.model.nodeDataArray.slice(0, nodeToRemoveIndex),
                ...state.model.nodeDataArray.slice(nodeToRemoveIndex + 1)
            ]
        }
    };
};

const removeLinkHandler = (state: DiagramState, payload: LinkModel): DiagramState => {
    const linkToRemoveIndex = state.model.linkDataArray.findIndex(
        link => link.from === payload.from && link.to === payload.to
    );
    if (linkToRemoveIndex === -1) {
        return state;
    }
    return {
        ...state,
        model: {
            ...state.model,
            linkDataArray: [
                ...state.model.linkDataArray.slice(0, linkToRemoveIndex),
                ...state.model.linkDataArray.slice(linkToRemoveIndex + 1)
            ]
        }
    };
};

const nodeSelectedHandler = (state: DiagramState, payload: string): DiagramState => {
    return {
        ...state,
        selectedNodeKeys: [...state.selectedNodeKeys, payload]
    };
};

const nodeDeselectedHandler = (state: DiagramState, payload: string): DiagramState => {
    const nodeIndexToRemove = state.selectedNodeKeys.findIndex(key => key === payload);
    if (nodeIndexToRemove === -1) {
        return state;
    }
    return {
        ...state,
        selectedNodeKeys: [
            ...state.selectedNodeKeys.slice(0, nodeIndexToRemove),
            ...state.selectedNodeKeys.slice(nodeIndexToRemove + 1)
        ]
    };
};

export const diagramReducer: Reducer<DiagramState> = reducerWithInitialState<DiagramState>({
    model: {
        nodeDataArray: [],
        linkDataArray: []
    },
    selectedNodeKeys: []
})
    .case(init, initHandler)
    .case(updateNode, updateNodeHandler)
    .case(addNode, addNodeHandler)
    .case(removeNode, removeNodeHandler)
    .case(removeLink, removeLinkHandler)
    .case(nodeSelected, nodeSelectedHandler)
    .case(nodeDeselected, nodeDeselectedHandler)
    .build();

export const modelSelector = (state: DiagramState) => state.model;
export const nodeSelectionSelector = (state: DiagramState) => state.selectedNodeKeys;
