/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2024 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#import "GridLayout.h"



@implementation GridLayout

@synthesize label=_label;
@synthesize uilayout=_uilayout;
@synthesize newline=_newline;

- (GridLayout*)init {
    self = [super init];
    _columnspacing = 0;
    _rowspacing = 0;
    _children = cxArrayListCreateSimple(sizeof(GridElm), 32);
    
    return self;
}

/*
- (void) layout {
    [super layout];
    
    NSRect r1 = _test.frame;
    NSSize s1 = _test.intrinsicContentSize;
    NSEdgeInsets e1 = _test.alignmentRectInsets;
    
    printf("fuck\n");
}
 */


- (void) layout {
    int ncols = _cols+1;
    int nrows = _rows+1;
    
    GridDef *cols = calloc(ncols, sizeof(GridDef));
    GridDef *rows = calloc(nrows, sizeof(GridDef));
    
    NSRect viewFrame = self.frame;
    
    int colspacing = _columnspacing;
    int rowspacing = _rowspacing;
    
    CxIterator i = cxListIterator(_children);
    cx_foreach(GridElm *, elm, i) {
        NSSize size = elm->view.intrinsicContentSize;
        NSEdgeInsets alignment = elm->view.alignmentRectInsets;
        if(size.width != NSViewNoIntrinsicMetric) {
            CGFloat width = size.width + alignment.left + alignment.right;
            if(width > cols[elm->x].preferred_size) {
                cols[elm->x].preferred_size = width;
            }
            elm->preferred_width = width;
        }
        if(size.height != NSViewNoIntrinsicMetric) {
            CGFloat height = size.height + alignment.top + alignment.right;
            //CGFloat height = size.height;
            if(height > rows[elm->y].preferred_size) {
                rows[elm->y].preferred_size = height;
            }
            elm->preferred_height = height;
        }
        
        if(elm->hexpand) {
            cols[elm->x].expand = TRUE;
        }
        if(elm->vexpand) {
            rows[elm->y].expand = TRUE;
        }
    }
    
    int col_ext = 0;
    int row_ext = 0;
    
    int preferred_width = 0;
    int preferred_height = 0;
    for(int j=0;j<ncols;j++) {
        preferred_width += cols[j].preferred_size + colspacing;
        if(cols[j].expand) {
            col_ext++;
        }
    }
    for(int j=0;j<nrows;j++) {
        preferred_height += rows[j].preferred_size + rowspacing;
        if(rows[j].expand) {
            row_ext++;
        }
    }
    
    _preferredSize.width = preferred_width;
    _preferredSize.height = preferred_height;
    
    
    int hremaining = viewFrame.size.width - preferred_width;
    int vremaining = viewFrame.size.height - preferred_height;
    int hext = hremaining/col_ext;
    int vext = vremaining/row_ext;
    
    for(int j=0;j<ncols;j++) {
        GridDef *col = &cols[j];
        if(col->expand) {
            col->size = col->preferred_size + hext;
        } else {
            col->size = col->preferred_size;
        }
    }
    for(int j=0;j<nrows;j++) {
        GridDef *row = &rows[j];
        if(row->expand) {
            row->size = row->preferred_size + vext;
        } else {
            row->size = row->preferred_size;
        }
    }
    
    int pos = 0;
    for(int j=0;j<ncols;j++) {
        cols[j].pos = pos;
        pos += cols[j].size + colspacing;
    }
    pos = 0;
    for(int j=0;j<nrows;j++) {
        rows[j].pos = pos;
        pos += rows[j].size + rowspacing;
    }
    
    i = cxListIterator(_children);
    cx_foreach(GridElm *, elm, i) {
        //NSSize size = elm->view.intrinsicContentSize;
        GridDef *col = &cols[elm->x];
        GridDef *row = &rows[elm->y];
        
        NSEdgeInsets alignment = elm->view.alignmentRectInsets;
        NSRect frame;
        if(elm->hfill) {
            frame.size.width = col->size;
        } else {
            frame.size.width = elm->preferred_width;
        }
        if(elm->vfill) {
            frame.size.height = row->size;
        } else {
            frame.size.height = elm->preferred_height;
        }
        frame.origin.x = col->pos - (alignment.left+alignment.right)/2;
        frame.origin.y = viewFrame.size.height - row->pos - frame.size.height + ((alignment.top+alignment.right)/2);
        elm->view.frame = frame;
    }
    
    free(cols);
    free(rows);
}
 

- (NSSize)intrinsicContentSize {
    return self.preferredSize;
}

- (void) addView:(NSView*)view fill:(BOOL)fill {
    if(_newline) {
        _y++;
        _x = 0;
        _newline = FALSE;
    }
    
    GridElm elm;
    elm.x = _x;
    elm.y = _y;
    elm.margin = 0;
    elm.colspan = _uilayout.colspan;
    elm.rowspan = _uilayout.rowspan;
    elm.hfill = _uilayout.hfill;
    elm.vfill = _uilayout.vfill;
    elm.hexpand = _uilayout.hexpand;
    elm.vexpand = _uilayout.vexpand;
    elm.view = view;
    cxListAdd(_children, &elm);
    
    [self addSubview:view];
    self.needsLayout = YES;
    
    if(_x > _cols) {
        _cols = _x;
    }
    if(_y > _rows) {
        _rows = _y;
    }
    _x++;
}

- (void) dealloc {
    cxListFree(_children);
}

@end
