
import {Component} from "../widgets/component";
import {PostConstruct, Autowired, Context} from "../context/context";
import {GridOptionsWrapper} from "../gridOptionsWrapper";
import {ColumnGroupChild} from "../entities/columnGroupChild";
import {ColumnGroup} from "../entities/columnGroup";
import {ColumnController} from "../columnController/columnController";
import {IRenderedHeaderElement} from "./iRenderedHeaderElement";
import {Column} from "../entities/column";
import {DropTarget} from "../dragAndDrop/dragAndDropService";
import {RenderedHeaderGroupCell} from "./renderedHeaderGroupCell";
import {RenderedHeaderCell} from "./renderedHeaderCell";
import {EventService} from "../eventService";
import {Events} from "../events";
import {Utils as _} from "../utils";

export class HeaderRowComp extends Component {

    @Autowired('gridOptionsWrapper') private gridOptionsWrapper: GridOptionsWrapper;
    @Autowired('columnController') private columnController: ColumnController;
    @Autowired('context') private context: Context;
    @Autowired('eventService') private eventService: EventService;

    private dept: number;
    private pinned: string;

    private headerElements: {[key: string]: IRenderedHeaderElement} = {};

    private eRoot: HTMLElement;
    private dropTarget: DropTarget;
    
    // if bottom row, this is false, otherwise true
    private showingGroups: boolean;

    constructor(dept: number, showingGroups: boolean, pinned: string, eRoot: HTMLElement, dropTarget: DropTarget) {
        super(`<div class="ag-header-row"/>`);
        this.dept = dept;
        this.showingGroups = showingGroups;
        this.pinned = pinned;
        this.eRoot = eRoot;
        this.dropTarget = dropTarget;
    }

    public destroy(): void {
        var idsOfAllChildren = Object.keys(this.headerElements);
        this.removeAndDestroyChildComponents(idsOfAllChildren);
        super.destroy();
    }

    private removeAndDestroyChildComponents(idsToDestroy: string[]): void {
        idsToDestroy.forEach( id => {
            var child = this.headerElements[id];
            this.getGui().removeChild(child.getGui());
            child.destroy();
            delete this.headerElements[id];
        });
    }

    @PostConstruct
    private init(): void {

        var rowHeight = this.gridOptionsWrapper.getHeaderHeight();
        this.getGui().style.top = (this.dept * rowHeight) + 'px';
        this.getGui().style.height = rowHeight + 'px';

        var virtualColumnsChangedListener = this.onVirtualColumnsChanged.bind(this);
        var displayedColumnsChangedListener = this.onDisplayedColumnsChanged.bind(this);

        this.eventService.addEventListener(Events.EVENT_VIRTUAL_COLUMNS_CHANGED, virtualColumnsChangedListener);
        this.eventService.addEventListener(Events.EVENT_DISPLAYED_COLUMNS_CHANGED, displayedColumnsChangedListener);

        this.addDestroyFunc( ()=> {
            this.eventService.removeEventListener(Events.EVENT_VIRTUAL_COLUMNS_CHANGED, virtualColumnsChangedListener);
            this.eventService.removeEventListener(Events.EVENT_DISPLAYED_COLUMNS_CHANGED, displayedColumnsChangedListener);
        });

        this.onVirtualColumnsChanged();
    }

    private onDisplayedColumnsChanged(): void {
        // because column groups are created and destroyed on the fly as groups are opened / closed and columns are moved,
        // we have to throw away all of the components when columns are changed, as the references to the old groups
        // are no longer value. this is not true for columns where columns do not get destroyed between open / close
        // or moving actions.
        if (this.showingGroups) {
            var idsOfAllChildren = Object.keys(this.headerElements);
            this.removeAndDestroyChildComponents(idsOfAllChildren);
        }
        this.onVirtualColumnsChanged();
    }
    
    private onVirtualColumnsChanged(): void {

        var currentChildIds = Object.keys(this.headerElements);

        var nodesAtDept = this.columnController.getVirtualHeaderGroupRow(this.pinned, this.dept);

        nodesAtDept.forEach( (child: ColumnGroupChild) => {
            var idOfChild = child.getUniqueId();

            // if we already have this cell rendered, do nothing
            if (currentChildIds.indexOf(idOfChild) >= 0) {
                _.removeFromArray(currentChildIds, idOfChild);
                return;
            }

            // skip groups that have no displayed children. this can happen when the group is broken,
            // and this section happens to have nothing to display for the open / closed state
            if (child instanceof ColumnGroup && (<ColumnGroup>child).getDisplayedChildren().length === 0) {
                return;
            }

            var renderedHeaderElement = this.createHeaderElement(child);
            this.headerElements[idOfChild] = renderedHeaderElement;
            this.getGui().appendChild(renderedHeaderElement.getGui());
        });

        // at this point, anything left in currentChildIds is an element that is no longer in the viewport
        this.removeAndDestroyChildComponents(currentChildIds);
    }

    private createHeaderElement(columnGroupChild:ColumnGroupChild):IRenderedHeaderElement {
        var result:IRenderedHeaderElement;
        if (columnGroupChild instanceof ColumnGroup) {
            result = new RenderedHeaderGroupCell(<ColumnGroup> columnGroupChild, this.eRoot, this.dropTarget);
        } else {
            result = new RenderedHeaderCell(<Column> columnGroupChild, this.eRoot, this.dropTarget);
        }
        this.context.wireBean(result);
        return result;
    }

}