import { Component, OnInit, ChangeDetectionStrategy, ContentChildren, QueryList, ElementRef, ViewChild, Input, Output, EventEmitter } from '@angular/core';
import { ResizableBoxComponent, boxSpec, size } from '../resizable-box.component';

@Component({
  selector: 'rbj-resizable-box-container',
  templateUrl: './resizable-box-container.component.html',
  styleUrls: ['./resizable-box-container.component.scss']
})
export class ResizableBoxContainerComponent implements OnInit {

  private _specList: boxSpec[];
  private _enableDrag: boolean = false;

  @ContentChildren(ResizableBoxComponent) boxes: QueryList<ResizableBoxComponent>;

  @ViewChild('container', { static: true }) containerDiv: ElementRef;
  @ViewChild('boxHolder', { static: true }) boxHolder: ResizableBoxComponent;

  layoutChanged: EventEmitter<boolean> = new EventEmitter<boolean>();

  @Input()
  padding: number = 10;

  @Input()
  gridSizeY: number = 20;
  @Input()
  gridSizeX: number = 120;

  @Input()
  boxMinSize = { height: 150, width: 150 } as size;

  @Input()
  public set enableDragging(v: boolean) {
    if (this._enableDrag != v) {

      this._enableDrag = v;
      this.boxes.forEach(box => {
        box.draggable = v;
      });
      this.calculateContainerHeight();
    }
  }

  public get enableDragging(): boolean {
    return this._enableDrag;
  }
  public set specList(v: boxSpec[]) {
    this._specList = v;
  }
  public get specList(): boxSpec[] {

    return this._specList;
  }
  max_X: number;
  holderSpec = new boxSpec('', 0, 0, 0, 0);
  displayHolder: boolean = false;
  containerHeight: number = 500;

  constructor() { }

  ngOnInit() {
  }

  ngAfterContentInit() {

    this.initializeBoxes();
  };

  initializeBoxes() {
    if (this.specList) {
      this.specList.forEach(spec => {
        var box = this.boxes.find(b => b.id == spec.id);
        box.change.subscribe(boxSpec => this.boxChanged(boxSpec));
        box.draggingStart.subscribe(boxSpec => {
          this.displayHolder = true;
          this.boxHolder.moveAndResize(boxSpec)
        })
        box.draggingEnd.subscribe(boxSpec => {
          this.displayHolder = false;
          this.getBoxById(boxSpec.id).moveAndResize(this.holderSpec)

          this.boxes.toArray().filter(b => b.id != this.holderSpec.id).forEach(box => {
            var newSpec = box.spec
            this.fillVerticalEmpty(newSpec);
            if (box.spec.diff(newSpec)) {
              box.moveAndResize(newSpec);
            }
          });
        })
        box.animationMoveEnd.subscribe(() => { this.moving = false; })
      });
      this.renderBoxes(this.specList);
    }
  }

  renderBoxes(specList: boxSpec[] = null) {
    this.max_X = Number(this.containerDiv.nativeElement.clientWidth);
    this.max_X = this.max_X > 1582 ? 1582 : this.max_X;
    this.gridSizeX = (this.max_X - this.padding * 12) / 12;
    if (!specList) {
      specList = this.boxes.map(b => b.spec);
    }
    this.boxHolder.gridSizeX = this.gridSizeX;
    this.boxHolder.gridSizeY = this.gridSizeY;
    this.boxHolder.gridPadding = this.padding;
    specList.forEach(spec => {
      var box = this.boxes.find(b => b.id == spec.id);
      box.max_X = this.max_X;
      box.gridSizeX = this.gridSizeX;
      box.gridSizeY = this.gridSizeY;
      box.gridPadding = this.padding;
      box.moveAndResize(spec);
    });
    this.calculateContainerHeight(specList);
  }

  calculateContainerHeight(specList: boxSpec[] = null) {
    if (!specList) {
      specList = this.boxes.map(b => b.spec);
    }
    this.containerHeight = (Math.max(...specList.map(spec => (spec.position.y + spec.size.height + 1)))) * this.gridSizeY;

  }

  boxChanged(spec: boxSpec) {
    this.holderSpec.id = spec.id;

    this.holderSpec.position.x = spec.position.x;
    this.holderSpec.position.y = spec.position.y;
    this.holderSpec.size.width = spec.size.width;
    this.holderSpec.size.height = spec.size.height;
    // this.holderSpec.position.x = Math.round(spec.position.x / this.gridSizeX);
    // this.holderSpec.position.y = Math.round(spec.position.y / this.gridSizeY);
    // this.holderSpec.size.width = Math.round(spec.size.width / this.gridSizeX);
    // this.holderSpec.size.height = Math.round(spec.size.height / this.gridSizeY);
    if (this.boxHolder.spec.diff(this.holderSpec)) {

      //if (this.checkSameColBox(spec)) return;
      //this.checkSameRowBox(this.holderSpec);
      this.fillVerticalEmpty(this.holderSpec);
      this.getBoxById(spec.id).y = this.holderSpec.top;
      this.boxHolder.moveAndResize(this.holderSpec, 200);
      this.checkCrossBox(this.holderSpec, 1);

      this.layoutChanged.next(true);
    };
  }
  moving: boolean = false;
  maxStack: number = 0;

  checkCrossBox(spec: boxSpec, stack: number) {
    var otherBoxes = this.boxes.toArray().filter(b => b.id != spec.id);
    for (let i = 0; i < otherBoxes.length; i++) {
      this.maxStack = this.maxStack > stack ? this.maxStack : stack;

      const box = otherBoxes[i];
      if (box.spec.top >= spec.top && spec.bottom >= box.spec.top && (
        box.x <= spec.left && box.spec.right > spec.left ||
        box.x >= spec.left && box.x < spec.right)
      ) {
        var moveTo = new boxSpec(box.id, box.x, spec.bottom + 1, box.width, box.height);

        this.fillVerticalEmpty(moveTo);
        box.moveAndResize(moveTo);
        this.checkCrossBox(moveTo, stack + 1);
      };
    }
    if (this.maxStack == stack) {
      this.maxStack = 0;
      this.boxes.toArray().filter(b => b.id != this.holderSpec.id).forEach(box => {
        var newSpec = box.spec
        this.fillVerticalEmpty(newSpec);
        if (box.spec.diff(newSpec)) {
          box.moveAndResize(newSpec);
        }
      });

      this.calculateContainerHeight();
    }
  }

  fillVerticalEmpty(spec: boxSpec) {
    var otherBoxes = this.boxes.toArray().filter(b => b.id != spec.id &&
      b.spec.top < spec.top
      && (
        b.x <= spec.left && b.spec.right > spec.left ||
        b.x >= spec.left && b.x < spec.right));

    if (otherBoxes.length == 0) {
      spec.position.y = 0;
    }
    else {
      spec.position.y = Math.max(...otherBoxes.map(b => b.spec.bottom)) + 1;
    }
  }

  saveBoxSpecs(): boxSpec[] {
    this.layoutChanged.next(false);

    this._specList = this.boxes.map(b => b.spec);
    return this._specList;
  }

  getBoxById(id: string): ResizableBoxComponent {
    return this.boxes.find(b => b.id == id);
  }

  onResize(event) {
    //event.target.innerWidth;
    this.renderBoxes();
  }

  reset() {
    this.layoutChanged.next(false);
    this.renderBoxes(this.specList);
  }
}
